Skip to content

Feature/de #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 131 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
66bce0e
Do thing
Apr 15, 2020
6dccd93
Add missing word employee
Apr 15, 2020
b409154
Add missing word employee
Apr 15, 2020
5612b2f
Mega ultra reuse code
Apr 15, 2020
872c553
Fix username desc
Apr 15, 2020
2ba8392
Merge branch 'master' into chore/refactor-detetctionlists
Apr 15, 2020
af781fd
Upgrade py42 and extractor
Apr 16, 2020
390bbd2
Merge branch 'chore/refactor-detetctionlists' of https://github.com/c…
Apr 16, 2020
c7ac5d3
Undo last commit
Apr 16, 2020
557330c
Upgrade py42 and extractor
Apr 16, 2020
7f2baf8
Impl add
Apr 16, 2020
0cdc409
Add tests
Apr 16, 2020
d13fef7
Format
Apr 16, 2020
406dd19
Fix search args
Apr 16, 2020
1b4c279
Fix underscore to hyphen issue
Apr 16, 2020
4be3302
Fix bulk
Apr 16, 2020
b6c4590
update py42
Apr 16, 2020
d690005
Fix tests
Apr 16, 2020
7ac0e40
Fix everything
Apr 16, 2020
ca2e168
More tests
Apr 16, 2020
966ab82
get handlers
Apr 16, 2020
0c02883
Save
Apr 16, 2020
63db05b
Remove bulk, had to massage it for that
Apr 16, 2020
713f68d
Revise
Apr 16, 2020
76cd42f
Fix tests
Apr 17, 2020
1f99191
Use generators
Apr 17, 2020
61c817f
Add more tests
Apr 17, 2020
742b56a
Update changelog
Apr 17, 2020
2adf39a
Remove print
Apr 17, 2020
59c125c
Remove command
Apr 17, 2020
6e78d22
REturn
Apr 17, 2020
516a6c0
Check for no user
Apr 17, 2020
f90c229
Simplify
Apr 17, 2020
41eca3b
Update tests
Apr 17, 2020
8c1d863
More tests
Apr 17, 2020
70e8d30
Update upstream
Apr 17, 2020
01a2922
Remove unused import
Apr 17, 2020
50e357e
Bumps
Apr 17, 2020
ea9e074
Merge branch 'chore/refactor-detetctionlists' into feature/rm-hr
Apr 17, 2020
1bc6d58
Bumps correctly
Apr 17, 2020
20d14a8
Tests
Apr 17, 2020
afe670b
Reader arg
Apr 17, 2020
9462bcb
More tests
Apr 17, 2020
ec2ed73
Rename methods
Apr 17, 2020
c9cec92
Docstr+ format
Apr 20, 2020
5db1983
Genericize the username desc
Apr 20, 2020
51ff8fb
Pull out command names
Apr 20, 2020
d449473
Add readme
Apr 20, 2020
312793f
Errorsgit sgit s
Apr 20, 2020
57c30bb
Error handling
Apr 20, 2020
5a9a125
Handle template for remove
Apr 20, 2020
97c778d
Print message for remove generation
Apr 20, 2020
b9cde61
Update CL
Apr 20, 2020
cf2aa52
Revise
Apr 20, 2020
769f599
Try fix tests
Apr 20, 2020
4b85169
Add missing periods
Apr 20, 2020
7e1f295
Revise README
Apr 20, 2020
13ce049
Revise Bulk docstr
Apr 20, 2020
5c226bf
Clarify method names
Apr 20, 2020
db13630
Doc
Apr 20, 2020
abdaad2
Save
Apr 20, 2020
61af99b
Docs revise
Apr 20, 2020
eb992f3
Merge branch 'feature/rm-hr' into feature/de
Apr 20, 2020
d46be49
Fix critical bug
Apr 20, 2020
a2c81fb
Fix critical bug
Apr 20, 2020
6737656
Merge branch 'feature/rm-hr' into feature/de
Apr 20, 2020
ccb43e0
Load subs
Apr 20, 2020
9c379a4
Fix more critical bugs
Apr 20, 2020
d5fa1d4
Merge branch 'feature/rm-hr' into feature/de
Apr 20, 2020
746ac24
Remove spaces
Apr 20, 2020
a418100
Formatting
Apr 20, 2020
746b21b
Merge branch 'feature/rm-hr' into feature/de
Apr 20, 2020
db3754a
Fix readme issues
Apr 20, 2020
25f68f1
Merge branch 'feature/rm-hr' into feature/de
Apr 20, 2020
79f6a5b
Merge
Apr 20, 2020
4a65322
Missing s
Apr 20, 2020
e1ae368
README command replace
Apr 20, 2020
a72f45f
Say delimited one line per user in readme
Apr 20, 2020
c7fd31b
Generic type for documentation purposes
Apr 20, 2020
2fcb5eb
Some pr fb
Apr 20, 2020
951c4aa
Merge branch 'master' into feature/rm-hr
Apr 20, 2020
9e97e70
Determine which handler to use based on num args
Apr 20, 2020
13aacdd
Print message during py42forbidden
Apr 20, 2020
9f12ba4
Handle flat files better
Apr 20, 2020
81eb8c1
Update readme
Apr 20, 2020
a241279
Remove pointless base class
Apr 21, 2020
3219f64
Update words
Apr 21, 2020
2746127
Make test for error log loc printing simpler
Apr 21, 2020
d833b3d
Remove redundancy and fix docstr
Apr 21, 2020
7f76cb4
<=
Apr 21, 2020
d3c48d9
Docs
Apr 21, 2020
95bbdb3
Profile and sdk names reuse
Apr 21, 2020
b4a5ca5
Make only accpet single cloud alias
Apr 21, 2020
27a206b
Format
Apr 21, 2020
37b0888
Better help
Apr 21, 2020
4a58374
elif
Apr 21, 2020
ffd8bd8
Str or unicode, makes pycharm happier
Apr 21, 2020
86ed52a
cloud alias
Apr 21, 2020
2f37256
cloud alias for real
Apr 21, 2020
e6be3b2
Bumps
Apr 21, 2020
7da5346
Remove pinning
Apr 21, 2020
28f5aee
Fix tests
Apr 21, 2020
52f2e69
Merge branch 'feature/rm-hr' into feature/de
Apr 21, 2020
d56d87e
Optimize imports
Apr 21, 2020
8029901
Fix tests
Apr 21, 2020
3c210ac
Ignore none
Apr 21, 2020
72788b9
Fix edge case with blank rows
Apr 21, 2020
0de86ad
Put back something that got deleted accidentally
Apr 21, 2020
c0a9910
Improve teset
Apr 21, 2020
ea0b472
Merge branch 'feature/rm-hr' into feature/de
Apr 21, 2020
8d8e10c
Actually use custom ex
Apr 21, 2020
3fa604f
Merge branch 'feature/rm-hr' into feature/de
Apr 21, 2020
fb3a8c6
Simplify
Apr 21, 2020
9acba6e
Simplify
Apr 21, 2020
a58bd85
Merge branch 'feature/rm-hr' into feature/de
Apr 21, 2020
1e99a11
Simplify
Apr 21, 2020
a326f1b
Safety first
Apr 21, 2020
7a05b56
Merge branch 'feature/rm-hr' into feature/de
Apr 21, 2020
ec6c2aa
Merge branch 'master' into feature/de
Apr 22, 2020
540f952
Merge branch 'feature/de' of https://github.com/code42/c42sec into fe…
Apr 22, 2020
e92e2d0
Create handlers
Apr 22, 2020
7fee80c
Singular cloud alias
Apr 22, 2020
dbf9a51
Fix tests
Apr 22, 2020
12f45bd
USe enum
Apr 22, 2020
200c3bf
undo some stuff
Apr 22, 2020
dadb9b5
Resolve flicts
Apr 22, 2020
68bf7de
Add format to help str
Apr 22, 2020
8f7fc29
Fix alias help
Apr 22, 2020
c6e4992
Fix quote
Apr 22, 2020
3d6ae5a
Update some docs
Apr 22, 2020
00d2b2a
Missing periods
Apr 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,21 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta
- `code42 high-risk-employee` commands:
- `bulk` with subcommands:
- `add`: that takes a csv file of users.
- `generate-template`: that creates the csv file template. And parameters:
- `generate-template`: that creates the file template. And parameters:
- `cmd`: with options `add` and `remove`.
- `path`
- `remove`: that takes a list of users in a file.
- `add` that takes parameters: `--username`, `--cloud-alias`, `--risk-factor`, and `--notes`.
- `remove` that takes a username.
- `code42 departing-employee` commands:
- `bulk` with subcommands:
- `add`: that takes a csv file of users.
- `generate-template`: that creates the file template. And parameters:
- `cmd`: with options `add` and `remove`.
- `path`
- `remove`: that takes a list of users in a file.
- `add` that takes parameters: `--username`, `--cloud-alias`, `--departure-date`, and `--notes`.
- `remove` that takes a username.

### Removed

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Use the `code42` command to interact with your Code42 environment.
* `code42 security-data` is a CLI tool for extracting AED events.
Additionally, you can choose to only get events that Code42 previously did not observe since you last recorded a
checkpoint (provided you do not change your query).
* `code42 high-risk-employee` is a collection of tools for managing the high risk employee detection list.
* `code42 high-risk-employee` is a collection of tools for managing the high risk employee detection list. Similarly,
there is `code42 departing-employee`.

## Requirements

Expand Down
15 changes: 14 additions & 1 deletion src/code42cli/cmds/detectionlists/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ def create_high_risk_employee_list(cls, handlers):
"""
return cls(DetectionLists.HIGH_RISK_EMPLOYEE, handlers)

@classmethod
def create_departing_employee_list(cls, handlers):
"""Creates a departing employee detection list.

Args:
handlers (DetectionListHandlers): A DTO containing implementations for adding /
removing users from specific lists.

Returns:
DetectionList: A departing employee detection list.
"""
return cls(DetectionLists.DEPARTING_EMPLOYEE, handlers)

def load_subcommands(self):
"""Loads high risk employee related subcommands"""
bulk = self.factory.create_bulk_command(lambda: self._load_bulk_subcommands())
Expand Down Expand Up @@ -151,7 +164,7 @@ def load_user_descriptions(argument_collection):
_load_username_description(argument_collection)
cloud_alias = argument_collection.arg_configs[DetectionListUserKeys.CLOUD_ALIAS]
notes = argument_collection.arg_configs[DetectionListUserKeys.NOTES]
cloud_alias.set_help(u"Alternative emails addresses for other cloud services.")
cloud_alias.set_help(u"An alternative email address for another cloud service.")
notes.set_help(u"Notes about the employee.")


Expand Down
48 changes: 48 additions & 0 deletions src/code42cli/cmds/detectionlists/departing_employee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from code42cli.cmds.detectionlists import (
DetectionList,
DetectionListHandlers,
load_user_descriptions,
get_user_id,
update_user,
)


def load_subcommands():
handlers = _create_handlers()
detection_list = DetectionList.create_departing_employee_list(handlers)
return detection_list.load_subcommands()


def _create_handlers():
return DetectionListHandlers(
add=add_departing_employee, remove=remove_departing_employee, load_add=_load_add_description
)


def add_departing_employee(
sdk, profile, username, cloud_alias=None, departure_date=None, notes=None
):
"""Adds an employee to the departing employee detection list.

Args:
sdk (py42.sdk.SDKClient): py42.
profile (C42Profile): Your code42 profile.
username (str): The username of the employee to add.
cloud_alias (str): An alternative email address for another cloud service.
departure_date (str): The date the employee is departing in format `YYYY-MM-DD`.
notes: (str): Notes about the employee.
"""
user_id = get_user_id(sdk, username)
update_user(sdk, user_id, cloud_alias, notes=notes)
sdk.detectionlists.departing_employee.add(user_id, departure_date)


def remove_departing_employee(sdk, profile, username):
user_id = get_user_id(sdk, username)
sdk.detectionlists.departing_employee.remove(user_id)


def _load_add_description(argument_collection):
load_user_descriptions(argument_collection)
departure_date = argument_collection.arg_configs[u"departure_date"]
departure_date.set_help(u"The date the employee is departing in format YYYY-MM-DD.")
106 changes: 9 additions & 97 deletions src/code42cli/cmds/securitydata/date_helper.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import re
from datetime import datetime, timedelta

from c42eventextractor.common import convert_datetime_to_timestamp
from py42.sdk.queries.fileevents.filters.event_filter import EventTimestamp

_MAX_LOOK_BACK_DAYS = 90
_FORMAT_VALUE_ERROR_MESSAGE = u"input must be a date/time string (e.g. 'YYYY-MM-DD', 'YY-MM-DD HH:MM', 'YY-MM-DD HH:MM:SS'), or a short value in days, hours, or minutes (e.g. 30d, 24h, 15m)"


class DateArgumentException(Exception):
def __init__(self, message=_FORMAT_VALUE_ERROR_MESSAGE):
super(DateArgumentException, self).__init__(message)


TIMESTAMP_REGEX = re.compile(u"(\d{4}-\d{2}-\d{2})\s*(.*)?")
MAGIC_TIME_REGEX = re.compile(u"(\d+)([dhm])$")
from code42cli.date_helper import DateArgumentException, parse_min_timestamp, parse_max_timestamp


def create_event_timestamp_filter(begin_date=None, end_date=None):
Expand All @@ -25,16 +11,16 @@ def create_event_timestamp_filter(begin_date=None, end_date=None):
end_date: The end date for the range.
"""
if begin_date and end_date:
min_timestamp = _parse_min_timestamp(begin_date)
max_timestamp = _parse_max_timestamp(end_date)
min_timestamp = parse_min_timestamp(begin_date)
max_timestamp = parse_max_timestamp(end_date)
return _create_in_range_filter(min_timestamp, max_timestamp)

elif begin_date and not end_date:
min_timestamp = _parse_min_timestamp(begin_date)
min_timestamp = parse_min_timestamp(begin_date)
return _create_on_or_after_filter(min_timestamp)

elif end_date and not begin_date:
max_timestamp = _parse_max_timestamp(end_date)
max_timestamp = parse_max_timestamp(end_date)
return _create_on_or_before_filter(max_timestamp)


Expand All @@ -43,90 +29,16 @@ def _create_in_range_filter(min_timestamp, max_timestamp):
return EventTimestamp.in_range(min_timestamp, max_timestamp)


def _create_on_or_after_filter(min_timestamp):
return EventTimestamp.on_or_after(min_timestamp)


def _create_on_or_before_filter(max_timestamp):
return EventTimestamp.on_or_before(max_timestamp)


def _parse_timestamp(date_str, rounding_func):
timestamp_match = TIMESTAMP_REGEX.match(date_str)
magic_match = MAGIC_TIME_REGEX.match(date_str)

if timestamp_match:
date, time = timestamp_match.groups()
dt = _get_dt_from_date_time_pair(date, time)
if not time:
dt = rounding_func(dt)

elif magic_match:
num, period = magic_match.groups()
dt = _get_dt_from_magic_time_pair(num, period)
if period == u"d":
dt = rounding_func(dt)

else:
raise DateArgumentException()
return dt


def _parse_min_timestamp(begin_date_str):
dt = _parse_timestamp(begin_date_str, _round_datetime_to_day_start)

boundary_date = _round_datetime_to_day_start(
datetime.utcnow() - timedelta(days=_MAX_LOOK_BACK_DAYS)
)
if dt < boundary_date:
raise DateArgumentException(u"'Begin date' must be within 90 days.")

return convert_datetime_to_timestamp(dt)


def _parse_max_timestamp(end_date_str):
dt = _parse_timestamp(end_date_str, _round_datetime_to_day_end)
return convert_datetime_to_timestamp(dt)


def _get_dt_from_date_time_pair(date, time):
date_format = u"%Y-%m-%d %H:%M:%S"
if time:
time = u"{}:{}:{}".format(*time.split(":") + [u"00", u"00"])
else:
time = u"00:00:00"
date_string = u"{} {}".format(date, time)
try:
dt = datetime.strptime(date_string, date_format)
except ValueError:
raise DateArgumentException()
else:
return dt


def _get_dt_from_magic_time_pair(num, period):
num = int(num)
if period == u"d":
dt = datetime.utcnow() - timedelta(days=num)
elif period == u"h":
dt = datetime.utcnow() - timedelta(hours=num)
elif period == u"m":
dt = datetime.utcnow() - timedelta(minutes=num)
else:
raise DateArgumentException(u"Couldn't parse magic time string: {}{}".format(num, period))
return dt


def _verify_timestamp_order(min_timestamp, max_timestamp):
if min_timestamp is None or max_timestamp is None:
return
if min_timestamp >= max_timestamp:
raise DateArgumentException(u"Begin date cannot be after end date")


def _round_datetime_to_day_start(dt):
return dt.replace(hour=0, minute=0, second=0, microsecond=0)
def _create_on_or_after_filter(min_timestamp):
return EventTimestamp.on_or_after(min_timestamp)


def _round_datetime_to_day_end(dt):
return dt.replace(hour=23, minute=59, second=59, microsecond=999000)
def _create_on_or_before_filter(max_timestamp):
return EventTimestamp.on_or_before(max_timestamp)
91 changes: 91 additions & 0 deletions src/code42cli/date_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from datetime import datetime, timedelta
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having this pulled out of cmds

import re

from c42eventextractor.common import convert_datetime_to_timestamp


_FORMAT_VALUE_ERROR_MESSAGE = (
u"input must be a date/time string (e.g. 'YYYY-MM-DD', "
u"'YY-MM-DD HH:MM', 'YY-MM-DD HH:MM:SS'), or a short value in days, "
u"hours, or minutes (e.g. 30d, 24h, 15m)"
)

TIMESTAMP_REGEX = re.compile(u"(\d{4}-\d{2}-\d{2})\s*(.*)?")
MAGIC_TIME_REGEX = re.compile(u"(\d+)([dhm])$")


class DateArgumentException(Exception):
def __init__(self, message=_FORMAT_VALUE_ERROR_MESSAGE):
super(DateArgumentException, self).__init__(message)


def parse_min_timestamp(begin_date_str, max_days_back=90):
dt = _parse_timestamp(begin_date_str, _round_datetime_to_day_start)

boundary_date = _round_datetime_to_day_start(datetime.utcnow() - timedelta(days=max_days_back))
if dt < boundary_date:
raise DateArgumentException(u"'Begin date' must be within 90 days.")

return convert_datetime_to_timestamp(dt)


def parse_max_timestamp(end_date_str):
dt = _parse_timestamp(end_date_str, _round_datetime_to_day_end)
return convert_datetime_to_timestamp(dt)


def _parse_timestamp(date_str, rounding_func):
timestamp_match = TIMESTAMP_REGEX.match(date_str)
magic_match = MAGIC_TIME_REGEX.match(date_str)

if timestamp_match:
date, time = timestamp_match.groups()
dt = _get_dt_from_date_time_pair(date, time)
if not time:
dt = rounding_func(dt)

elif magic_match:
num, period = magic_match.groups()
dt = _get_dt_from_magic_time_pair(num, period)
if period == u"d":
dt = rounding_func(dt)

else:
raise DateArgumentException()
return dt


def _get_dt_from_date_time_pair(date, time):
date_format = u"%Y-%m-%d %H:%M:%S"
if time:
time = u"{}:{}:{}".format(*time.split(":") + [u"00", u"00"])
else:
time = u"00:00:00"
date_string = u"{} {}".format(date, time)
try:
dt = datetime.strptime(date_string, date_format)
except ValueError:
raise DateArgumentException()
else:
return dt


def _get_dt_from_magic_time_pair(num, period):
num = int(num)
if period == u"d":
dt = datetime.utcnow() - timedelta(days=num)
elif period == u"h":
dt = datetime.utcnow() - timedelta(hours=num)
elif period == u"m":
dt = datetime.utcnow() - timedelta(minutes=num)
else:
raise DateArgumentException(u"Couldn't parse magic time string: {}{}".format(num, period))
return dt


def _round_datetime_to_day_start(dt):
return dt.replace(hour=0, minute=0, second=0, microsecond=0)


def _round_datetime_to_day_end(dt):
return dt.replace(hour=23, minute=59, second=59, microsecond=999000)
9 changes: 8 additions & 1 deletion src/code42cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

from code42cli import PRODUCT_NAME
from code42cli.cmds import profile
from code42cli.cmds.detectionlists import departing_employee as de
from code42cli.cmds.detectionlists import high_risk_employee as hre
from code42cli.cmds.detectionlists.enums import DetectionLists
from code42cli.cmds.securitydata import main as secmain
from code42cli.commands import Command
from code42cli.invoker import CommandInvoker
Expand Down Expand Up @@ -46,7 +48,12 @@ def _load_top_commands():
subcommand_loader=secmain.load_subcommands,
),
Command(
u"high-risk-employee",
DetectionLists.DEPARTING_EMPLOYEE,
detection_lists_description.format(u"departing employee"),
subcommand_loader=de.load_subcommands,
),
Command(
DetectionLists.HIGH_RISK_EMPLOYEE,
detection_lists_description.format(u"high risk employee"),
subcommand_loader=hre.load_subcommands,
),
Expand Down
Loading