Skip to content

Commit 70e3bf9

Browse files
author
Juliya Smith
authored
Remove detection lists root command + additional shared code (#35)
1 parent c6340a4 commit 70e3bf9

26 files changed

Lines changed: 499 additions & 184 deletions

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta
2626
- `code42 profile create` command.
2727
- `code42 profile delete` command.
2828
- `code42 profile delete-all` command.
29-
- `code42 detection-lists high-risk` commands:
29+
- `code42 high-risk-employee` commands:
3030
- `bulk` with subcommands:
3131
- `add`: that takes a csv file of users.
3232
- `generate-template`: that creates the csv file template. And parameters:

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
package_dir={"": "src"},
2222
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4",
2323
install_requires=[
24-
"c42eventextractor==0.2.2",
24+
"c42eventextractor==0.2.5",
2525
"keyring==18.0.1",
2626
"keyrings.alt==3.2.0",
27-
"py42==0.6.0",
27+
"py42==0.9.0",
2828
],
2929
license="MIT",
3030
include_package_data=True,

src/code42cli/args.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ class ArgConfig(object):
88
"""Stores a set of argparse commands for later use by a command."""
99

1010
def __init__(self, *args, **kwargs):
11-
self._settings = {}
12-
self._settings[u"action"] = kwargs.get(u"action")
13-
self._settings[u"choices"] = kwargs.get(u"choices")
14-
self._settings[u"default"] = kwargs.get(u"default")
15-
self._settings[u"help"] = kwargs.get(u"help")
16-
self._settings[u"options_list"] = list(args)
17-
self._settings[u"nargs"] = kwargs.get(u"nargs")
11+
self._settings = {
12+
u"action": kwargs.get(u"action"),
13+
u"choices": kwargs.get(u"choices"),
14+
u"default": kwargs.get(u"default"),
15+
u"help": kwargs.get(u"help"),
16+
u"options_list": list(args),
17+
u"nargs": kwargs.get(u"nargs"),
18+
}
1819

1920
@property
2021
def settings(self):
@@ -29,6 +30,9 @@ def set_help(self, help):
2930
def add_short_option_name(self, short_name):
3031
self._settings[u"options_list"].append(short_name)
3132

33+
def as_multi_val_param(self, nargs=u"+"):
34+
self._settings[u"nargs"] = nargs
35+
3236

3337
class ArgConfigCollection(object):
3438
def __init__(self):
@@ -57,7 +61,7 @@ def get_auto_arg_configs(handler):
5761

5862
for arg_position, key in enumerate(argspec.args):
5963
# do not create cli parameters for arguments named "sdk", "args", or "kwargs"
60-
if not key in [u"sdk", u"args", u"kwargs"]:
64+
if not key in [u"sdk", u"args", u"kwargs", u"self"]:
6165
arg_config = _create_auto_args_config(
6266
arg_position, key, argspec, num_args, num_kw_args
6367
)

src/code42cli/bulk.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ def _write_template_file(path, columns):
2121
new_csv.write(u",".join(columns))
2222

2323

24-
def create_bulk_processor(csv_file_path, row_handler):
24+
def run_bulk_process(csv_file_path, row_handler):
25+
processor = _create_bulk_processor(csv_file_path, row_handler)
26+
processor.run()
27+
28+
29+
def _create_bulk_processor(csv_file_path, row_handler):
2530
"""A factory method to create the bulk processor, useful for testing purposes."""
2631
return BulkProcessor(csv_file_path, row_handler)
2732

@@ -44,13 +49,10 @@ def __init__(self, csv_file_path, row_handler):
4449

4550
def run(self):
4651
"""Processes the csv file specified in the ctor, calling `self.row_handler` on each row."""
47-
rows = self._get_rows()
48-
self._process_rows(rows)
49-
self.__worker.wait()
50-
51-
def _get_rows(self):
5252
with open(self.csv_file_path, newline=u"", encoding=u"utf8") as csv_file:
53-
return _create_dict_reader(csv_file)
53+
rows = _create_dict_reader(csv_file)
54+
self._process_rows(rows)
55+
self.__worker.wait()
5456

5557
def _process_rows(self, rows):
5658
for row in rows:
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
from code42cli.cmds.detectionlists.commands import DetectionListCommandFactory
2+
from code42cli.bulk import generate_template, run_bulk_process
3+
from code42cli.cmds.detectionlists.enums import (
4+
BulkCommandType,
5+
DetectionLists,
6+
DetectionListUserKeys,
7+
)
8+
from code42cli.util import print_error
9+
10+
11+
class DetectionListHandlers(object):
12+
"""Handlers DTO for passing in specific detection list functions.
13+
14+
Args:
15+
add (callable): A function that adds an employee to the list.
16+
remove (callable): A function that removes an employee from the list.
17+
load_add (callable): A function that loads the add-related `ArgConfig`s.
18+
"""
19+
20+
def __init__(self, add=None, remove=None, load_add=None):
21+
self.add_employee = add
22+
self.remove_employee = remove
23+
self.load_add_description = load_add
24+
25+
26+
class UserDoesNotExistError(Exception):
27+
"""An error to represent a username that is not in our system."""
28+
29+
def __init__(self, username):
30+
super(UserDoesNotExistError, self).__init__(u"User '{}' does not exist.".format(username))
31+
32+
33+
class DetectionList(object):
34+
"""An object representing a Code42 detection list. Use this class by passing in handlers for
35+
adding and removing employees. This class will handle the bulk-related commands and some
36+
shared help texts.
37+
38+
Args:
39+
list_name (str): An option from the DetectionLists enum. For convenience, use one of the
40+
given `classmethods`.
41+
handlers (DetectionListHandlers): A DTO containing implementations for adding / removing
42+
users from specific lists.
43+
cmd_factory (DetectionListCommandFactory): A factory that creates detection list commands.
44+
"""
45+
46+
def __init__(self, list_name, handlers, cmd_factory=None):
47+
self.name = list_name
48+
self.handlers = handlers
49+
self.factory = cmd_factory or DetectionListCommandFactory(list_name)
50+
51+
@classmethod
52+
def create_high_risk_list(cls, handlers):
53+
"""Creates a high risk detection list.
54+
55+
Args:
56+
handlers (DetectionListHandlers): A DTO containing implementations for adding /
57+
removing users from specific lists.
58+
59+
Returns:
60+
DetectionList: A high-risk employee detection list.
61+
"""
62+
return cls(DetectionLists.HIGH_RISK_EMPLOYEE, handlers)
63+
64+
def load_subcommands(self):
65+
"""Loads high risk employee related subcommands"""
66+
bulk = self.factory.create_bulk_command(lambda: self._load_bulk_subcommands())
67+
add = self.factory.create_add_command(
68+
self.handlers.add_employee, self.handlers.load_add_description
69+
)
70+
return [bulk, add]
71+
72+
def _load_bulk_subcommands(self):
73+
generate_template_cmd = self.factory.create_bulk_generate_template_command(
74+
self.generate_csv_file
75+
)
76+
add = self.factory.create_bulk_add_command(self.bulk_add_employees)
77+
return [generate_template_cmd, add]
78+
79+
def generate_csv_file(self, cmd, path=None):
80+
"""Generates a csv template a user would need to fill-in for bulk adding users to the
81+
detection list.
82+
83+
Args:
84+
cmd (str): An option from the `BulkCommandType` enum specifying which type of csv to
85+
generate.
86+
path (str, optional): A path to put the file after it's generated. If None, will use
87+
the current working directory. Defaults to None.
88+
"""
89+
handler = None
90+
if cmd == BulkCommandType.ADD:
91+
handler = self.handlers.add_employee
92+
generate_template(handler, path)
93+
94+
def bulk_add_employees(self, sdk, profile, csv_file):
95+
"""Takes a csv file with each row representing an employee and adds them all to a
96+
detection list in a bulk fashion.
97+
98+
Args:
99+
sdk (py42.sdk.SDKClient): The py42 sdk.
100+
profile (Code42Profile): The profile under which to execute this command.
101+
csv_file (str): The path to the csv file containing rows of users.
102+
"""
103+
run_bulk_process(csv_file, lambda **kwargs: self._add_employee(sdk, profile, **kwargs))
104+
105+
def _add_employee(self, sdk, profile, **kwargs):
106+
if (
107+
kwargs.has_key(DetectionListUserKeys.CLOUD_ALIAS)
108+
and type(kwargs[DetectionListUserKeys.CLOUD_ALIAS]) != list
109+
):
110+
kwargs[DetectionListUserKeys.CLOUD_ALIAS] = kwargs[
111+
DetectionListUserKeys.CLOUD_ALIAS
112+
].split()
113+
114+
self.handlers.add_employee(sdk, profile, **kwargs)
115+
116+
117+
def load_user_descriptions(argument_collection):
118+
"""Loads the arg descriptions related to updating fields about a detection list user, such as
119+
notes or cloud aliases.
120+
121+
Args:
122+
argument_collection (ArgConfigCollection): The arg configs off the command that needs its
123+
user descriptions loaded.
124+
"""
125+
username = argument_collection.arg_configs[DetectionListUserKeys.USERNAME]
126+
cloud_alias = argument_collection.arg_configs[DetectionListUserKeys.CLOUD_ALIAS]
127+
notes = argument_collection.arg_configs[DetectionListUserKeys.NOTES]
128+
129+
username.set_help(u"The code42 username of the user you want to add.")
130+
cloud_alias.set_help(u"Alternative emails addresses for other cloud services.")
131+
cloud_alias.as_multi_val_param()
132+
notes.set_help(u"Notes about the employee.")
133+
134+
135+
def get_user_id(sdk, username):
136+
"""Returns the user's UID (referred to by `user_id` in detection lists). If the user does not
137+
exist, it prints an error and exits.
138+
139+
Args:
140+
sdk (py42.sdk.SDKClient): The py42 sdk.
141+
username (str or unicode): The username of the user to get an ID for.
142+
143+
Returns:
144+
str: The user ID for the user with the given username.
145+
"""
146+
users = sdk.users.get_by_username(username)[u"users"]
147+
if not users:
148+
print_error(str(UserDoesNotExistError(username)))
149+
exit(1)
150+
return users[0][u"userUid"]
151+
152+
153+
def update_user(sdk, user_id, cloud_alias=None, risk_tag=None, notes=None):
154+
"""Updates a detection list user.
155+
156+
Args:
157+
user_id (str): The ID of the user to update. This is their `userUid` found from
158+
`sdk.users.get_by_username()`.
159+
cloud_alias (iter[str]): A list of cloud aliases to add to the user.
160+
risk_tag (iter[str]): A list of risk tags associated with user.
161+
notes (str): Notes about the user.
162+
"""
163+
if cloud_alias:
164+
sdk.detectionlists.add_user_cloud_aliases(user_id, cloud_alias)
165+
if risk_tag:
166+
sdk.detectionlists.add_user_risk_tags(user_id, risk_tag)
167+
if notes:
168+
sdk.detectionlists.update_user_notes(user_id, notes)

src/code42cli/cmds/detectionlists/commands.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
def create_usage_prefix(detection_list_name):
6-
return u"code42 detection-list {}".format(detection_list_name)
6+
return u"code42 {}".format(detection_list_name)
77

88

99
def create_bulk_usage_prefix(detection_list_name):
@@ -36,7 +36,7 @@ def create_bulk_generate_template_command(self, handler):
3636
return Command(
3737
u"generate-template",
3838
u"Generate the necessary csv template needed for bulk adding users.",
39-
u"{} gen-template <cmd> <optional args>".format(self._bulk_usage_prefix),
39+
u"{} generate-template <cmd> <optional args>".format(self._bulk_usage_prefix),
4040
handler=handler,
4141
arg_customizer=DetectionListCommandFactory._load_bulk_generate_template_description,
4242
)
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
class DetectionLists(object):
22
DEPARTING_EMPLOYEE = u"departing-employee"
3-
HIGH_RISK = u"high-risk"
3+
HIGH_RISK_EMPLOYEE = u"high-risk-employee"
44

55

66
class BulkCommandType(object):
77
ADD = u"add"
88

99
def __iter__(self):
1010
return iter([self.ADD])
11+
12+
13+
class DetectionListUserKeys(object):
14+
CLOUD_ALIAS = u"cloud_alias"
15+
USERNAME = u"username"
16+
NOTES = u"notes"
17+
RISK_TAG = u"risk_tag"

src/code42cli/cmds/detectionlists/high_risk.py

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)