Skip to content

Commit 44c62c1

Browse files
kiran-chaudharyJuliya Smith
andauthored
Add bulk counterparts for add-risk-tags and remove-risk-tags (#77)
* Added bulk feature in hre to add or remove risk-tags * Add bulk generate template for high risk employee * Added tests and rectify inheritance syntax * Added changelog * Docs * Refactor * add extra space * Update CL Co-authored-by: Juliya Smith <juliya.smith@code42.com>
1 parent 16343f0 commit 44c62c1

File tree

8 files changed

+370
-156
lines changed

8 files changed

+370
-156
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta
2020

2121
### Added
2222

23+
- `code42 high-risk-employee bulk` supports `add-risk-tags` and `remove-risk-tags`.
24+
- `code42 high-risk-employee bulk generate-template <cmd>` options `add-risk-tags` and `remove-risk-tags`.
25+
- `add-risk-tags` that takes a csv file with username and space separated risk tags.
26+
- `remove-risk-tags` that takes a csv file with username and space separated risk tags.
27+
2328
- Display, `Fuzzy suggestions`, valid keywords matching mistyped commands or arguments.
2429

2530
- `code42 alerts`:

src/code42cli/cmds/detectionlists/__init__.py

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from py42.exceptions import Py42BadRequestError
22

3-
from code42cli.bulk import generate_template, run_bulk_process, BulkCommandType
3+
from code42cli.bulk import generate_template, run_bulk_process
44
from code42cli.file_readers import create_csv_reader, create_flat_file_reader
55
from code42cli.errors import UserAlreadyAddedError, UserDoesNotExistError, UnknownRiskTagError
6-
from code42cli.cmds.detectionlists.commands import DetectionListCommandFactory
76
from code42cli.cmds.detectionlists.enums import DetectionLists, DetectionListUserKeys, RiskTags
7+
from code42cli.cmds.detectionlists.commands import DetectionListCommandFactory
8+
from code42cli.cmds.detectionlists.bulk import BulkDetectionList, BulkHighRiskEmployee
89

910

1011
def try_handle_user_already_added_error(bad_request_err, username_tried_adding, list_name):
@@ -31,6 +32,9 @@ def __init__(self, add=None, remove=None, load_add=None):
3132
self.remove_employee = remove
3233
self.load_add_description = load_add
3334

35+
def add_handler(self, attr_name, handler):
36+
self.__setattr__(attr_name, handler)
37+
3438

3539
class DetectionList(object):
3640
"""An object representing a Code42 detection list. Use this class by passing in handlers for
@@ -88,29 +92,49 @@ def load_subcommands(self):
8892
return [bulk, add, remove]
8993

9094
def _load_bulk_subcommands(self):
91-
generate_template_cmd = self.factory.create_bulk_generate_template_command(
92-
self.generate_template_file
93-
)
95+
9496
add = self.factory.create_bulk_add_command(self.bulk_add_employees)
9597
remove = self.factory.create_bulk_remove_command(self.bulk_remove_employees)
96-
return [generate_template_cmd, add, remove]
98+
commands = [add, remove]
99+
100+
if self.name == DetectionLists.HIGH_RISK_EMPLOYEE:
101+
commands.extend(self._get_risk_tags_bulk_subcommands())
102+
else:
103+
generate_template_cmd = self.factory.create_bulk_generate_template_command(
104+
self.generate_template_file
105+
)
106+
commands.append(generate_template_cmd)
107+
return commands
108+
109+
def _get_risk_tags_bulk_subcommands(self):
110+
bulk_add_risk_tags = self.factory.create_bulk_add_risk_tags_command(self.bulk_add_risk_tags)
111+
bulk_remove_risk_tags = self.factory.create_bulk_remove_risk_tags_command(
112+
self.bulk_remove_risk_tags
113+
)
114+
115+
self.handlers.add_handler(u"add_risk_tags", add_risk_tags)
116+
self.handlers.add_handler(u"remove_risk_tags", remove_risk_tags)
117+
generate_template_cmd = self.factory.create_hre_bulk_generate_template_command(
118+
self.generate_template_file
119+
)
120+
return [bulk_add_risk_tags, bulk_remove_risk_tags, generate_template_cmd]
97121

98122
def generate_template_file(self, cmd, path=None):
99123
"""Generates a template file a user would need to fill-in for bulk operating on the
100124
detection list.
101-
125+
102126
Args:
103127
cmd (str or unicode): An option from the `BulkCommandType` enum specifying which type of file to
104128
generate.
105129
path (str or unicode, optional): A path to put the file after it's generated. If None, will use
106130
the current working directory. Defaults to None.
107131
"""
108-
handler = None
109-
if cmd == BulkCommandType.ADD:
110-
handler = self.handlers.add_employee
111-
elif cmd == BulkCommandType.REMOVE:
112-
handler = self.handlers.remove_employee
113132

133+
if self.name == DetectionLists.HIGH_RISK_EMPLOYEE:
134+
detection_list = BulkHighRiskEmployee()
135+
else:
136+
detection_list = BulkDetectionList()
137+
handler = detection_list.get_handler(self.handlers, cmd)
114138
generate_template(handler, path)
115139

116140
def bulk_add_employees(self, sdk, profile, csv_file):
@@ -145,6 +169,14 @@ def _add_employee(self, sdk, profile, **kwargs):
145169
def _remove_employee(self, sdk, profile, *args, **kwargs):
146170
self.handlers.remove_employee(sdk, profile, *args, **kwargs)
147171

172+
def bulk_add_risk_tags(self, sdk, profile, csv_file):
173+
reader = create_csv_reader(csv_file)
174+
run_bulk_process(lambda **kwargs: add_risk_tags(sdk, profile, **kwargs), reader)
175+
176+
def bulk_remove_risk_tags(self, sdk, profile, csv_file):
177+
reader = create_csv_reader(csv_file)
178+
run_bulk_process(lambda **kwargs: remove_risk_tags(sdk, profile, **kwargs), reader)
179+
148180

149181
def load_username_description(argument_collection):
150182
"""Loads the arg descriptions for the `username` CLI parameter."""
@@ -224,3 +256,23 @@ def _try_handle_bad_risk_tag(tags):
224256
unknowns = [tag for tag in tags if tag not in options] if tags else None
225257
if unknowns:
226258
raise UnknownRiskTagError(unknowns)
259+
260+
261+
def handle_list_args(list_arg):
262+
"""Converts str args to a list. Useful for `bulk` commands which don't use `argparse` but
263+
instead pass in values from files, such as in the form "item1 item2"."""
264+
if list_arg and not isinstance(list_arg, list):
265+
return list_arg.split()
266+
return list_arg
267+
268+
269+
def add_risk_tags(sdk, profile, username, tag):
270+
risk_tag = handle_list_args(tag)
271+
user_id = get_user_id(sdk, username)
272+
try_add_risk_tags(sdk, user_id, risk_tag)
273+
274+
275+
def remove_risk_tags(sdk, profile, username, tag):
276+
risk_tag = handle_list_args(tag)
277+
user_id = get_user_id(sdk, username)
278+
try_remove_risk_tags(sdk, user_id, risk_tag)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from code42cli.bulk import BulkCommandType
2+
3+
4+
class HighRiskBulkCommandType(BulkCommandType):
5+
ADD_RISK_TAG = u"add-risk-tags"
6+
REMOVE_RISK_TAG = u"remove-risk-tags"
7+
8+
def __iter__(self):
9+
parent_items = list(super(HighRiskBulkCommandType, self).__iter__())
10+
return iter([parent_items[0], parent_items[1], self.ADD_RISK_TAG, self.REMOVE_RISK_TAG])
11+
12+
13+
class BulkDetectionList(object):
14+
15+
def __init__(self):
16+
self.type = BulkCommandType
17+
18+
def get_handler(self, handlers, cmd):
19+
handler = None
20+
if cmd == self.type.ADD:
21+
handler = handlers.add_employee
22+
elif cmd == self.type.REMOVE:
23+
handler = handlers.remove_employee
24+
return handler
25+
26+
27+
class BulkHighRiskEmployee(BulkDetectionList):
28+
29+
def __init__(self):
30+
super(BulkHighRiskEmployee, self).__init__()
31+
self.type = HighRiskBulkCommandType
32+
33+
def get_handler(self, handlers, cmd):
34+
handler = super(BulkHighRiskEmployee, self).get_handler(handlers, cmd)
35+
if not handler:
36+
if cmd == self.type.ADD_RISK_TAG:
37+
handler = handlers.add_risk_tags
38+
elif cmd == self.type.REMOVE_RISK_TAG:
39+
handler = handlers.remove_risk_tags
40+
41+
return handler

src/code42cli/cmds/detectionlists/commands.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from code42cli.bulk import BulkCommandType
22
from code42cli.commands import Command
3+
from code42cli.cmds.detectionlists.bulk import HighRiskBulkCommandType
34

45

56
def create_usage_prefix(detection_list_name):
@@ -52,6 +53,15 @@ def create_bulk_generate_template_command(self, handler):
5253
arg_customizer=DetectionListCommandFactory._load_bulk_generate_template_description,
5354
)
5455

56+
def create_hre_bulk_generate_template_command(self, handler):
57+
return Command(
58+
u"generate-template",
59+
u"Generate the necessary csv template needed for bulk adding users.",
60+
u"{} generate-template <cmd> <optional args>".format(self._bulk_usage_prefix),
61+
handler=handler,
62+
arg_customizer=DetectionListCommandFactory._load_hre_bulk_generate_template_description,
63+
)
64+
5565
def create_bulk_add_command(self, handler):
5666
return Command(
5767
BulkCommandType.ADD,
@@ -70,12 +80,36 @@ def create_bulk_remove_command(self, handler):
7080
arg_customizer=self._load_bulk_remove_description,
7181
)
7282

83+
def create_bulk_add_risk_tags_command(self, handler):
84+
return Command(
85+
u"add-risk-tags",
86+
u"Associates risk tags with a user in bulk.",
87+
u"{} {} <file>".format(self._bulk_usage_prefix, HighRiskBulkCommandType.ADD_RISK_TAG),
88+
handler=handler,
89+
arg_customizer=self._load_bulk_add_risk_tags_description,
90+
)
91+
92+
def create_bulk_remove_risk_tags_command(self, handler):
93+
return Command(
94+
u"remove-risk-tags",
95+
u"Disassociates risk tags from a user in bulk.",
96+
u"{} {} <file>".format(self._bulk_usage_prefix, HighRiskBulkCommandType.REMOVE_RISK_TAG),
97+
handler=handler,
98+
arg_customizer=self._load_bulk_remove_risk_tags_description,
99+
)
100+
73101
@staticmethod
74102
def _load_bulk_generate_template_description(argument_collection):
75103
cmd_type = argument_collection.arg_configs[u"cmd"]
76-
cmd_type.set_help(u"The type of command the template with be used for.")
104+
cmd_type.set_help(u"The type of command the template will be used for.")
77105
cmd_type.set_choices(BulkCommandType())
78106

107+
@staticmethod
108+
def _load_hre_bulk_generate_template_description(argument_collection):
109+
cmd_type = argument_collection.arg_configs[u"cmd"]
110+
cmd_type.set_help(u"The type of command the template will be used for.")
111+
cmd_type.set_choices(HighRiskBulkCommandType())
112+
79113
def _load_bulk_add_description(self, argument_collection):
80114
csv_file = argument_collection.arg_configs[u"csv_file"]
81115
csv_file.set_help(
@@ -91,3 +125,23 @@ def _load_bulk_remove_description(self, argument_collection):
91125
self._name
92126
)
93127
)
128+
129+
def _load_bulk_add_risk_tags_description(self, argument_collection):
130+
csv_file = argument_collection.arg_configs[u"csv_file"]
131+
csv_file.set_help(
132+
u"A file containing a ',' separated username with space-separated tags to add "
133+
u"to the {} detection list. "
134+
u"e.g. test@email.com,tag1 tag2 tag3".format(
135+
self._name
136+
)
137+
)
138+
139+
def _load_bulk_remove_risk_tags_description(self, argument_collection):
140+
csv_file = argument_collection.arg_configs[u"csv_file"]
141+
csv_file.set_help(
142+
u"A file containing a ',' separated username with space-separated tags to remove "
143+
u"from the {} detection list. "
144+
u"e.g. test@email.com,tag1 tag2 tag3".format(
145+
self._name
146+
)
147+
)
Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1+
from py42.exceptions import Py42BadRequestError
2+
3+
from code42cli.commands import Command
14
from code42cli.cmds.detectionlists import (
25
DetectionList,
36
DetectionListHandlers,
47
load_user_descriptions,
5-
load_username_description,
68
get_user_id,
79
update_user,
810
try_handle_user_already_added_error,
9-
try_add_risk_tags,
10-
try_remove_risk_tags,
11+
add_risk_tags,
12+
remove_risk_tags,
13+
load_username_description,
14+
handle_list_args,
1115
)
1216
from code42cli.cmds.detectionlists.enums import DetectionLists, DetectionListUserKeys, RiskTags
13-
from code42cli.commands import Command
14-
15-
from py42.exceptions import Py42BadRequestError
1617

1718

1819
def load_subcommands():
1920
handlers = _create_handlers()
2021
detection_list = DetectionList.create_high_risk_employee_list(handlers)
22+
2123
cmd_list = detection_list.load_subcommands()
2224
cmd_list.extend(
2325
[
@@ -26,14 +28,14 @@ def load_subcommands():
2628
u"Associates risk tags with a user.",
2729
u"code42 high-risk-employee add-risk-tags --username <username> --tag <risk-tags>",
2830
handler=add_risk_tags,
29-
arg_customizer=_load_risk_tag_mgmt_descriptions,
31+
arg_customizer=load_risk_tag_mgmt_descriptions,
3032
),
3133
Command(
3234
u"remove-risk-tags",
3335
u"Disassociates risk tags from a user.",
3436
u"code42 high-risk-employee remove-risk-tags --username <username> --tag <risk-tags>",
3537
handler=remove_risk_tags,
36-
arg_customizer=_load_risk_tag_mgmt_descriptions,
38+
arg_customizer=load_risk_tag_mgmt_descriptions,
3739
),
3840
]
3941
)
@@ -46,18 +48,6 @@ def _create_handlers():
4648
)
4749

4850

49-
def add_risk_tags(sdk, profile, username, tag):
50-
risk_tag = _handle_list_args(tag)
51-
user_id = get_user_id(sdk, username)
52-
try_add_risk_tags(sdk, user_id, risk_tag)
53-
54-
55-
def remove_risk_tags(sdk, profile, username, tag):
56-
risk_tag = _handle_list_args(tag)
57-
user_id = get_user_id(sdk, username)
58-
try_remove_risk_tags(sdk, user_id, risk_tag)
59-
60-
6151
def add_high_risk_employee(sdk, profile, username, cloud_alias=None, risk_tag=None, notes=None):
6252
"""Adds an employee to the high risk employee detection list.
6353
@@ -69,7 +59,7 @@ def add_high_risk_employee(sdk, profile, username, cloud_alias=None, risk_tag=No
6959
risk_tag (iter[str]): Risk tags associated with the employee.
7060
notes: (str): Notes about the employee.
7161
"""
72-
risk_tag = _handle_list_args(risk_tag)
62+
risk_tag = handle_list_args(risk_tag)
7363
user_id = get_user_id(sdk, username)
7464

7565
try:
@@ -93,7 +83,12 @@ def remove_high_risk_employee(sdk, profile, username):
9383
sdk.detectionlists.high_risk_employee.remove(user_id)
9484

9585

96-
def _load_risk_tag_description(argument_collection):
86+
def _load_add_description(argument_collection):
87+
load_user_descriptions(argument_collection)
88+
load_risk_tag_description(argument_collection)
89+
90+
91+
def load_risk_tag_description(argument_collection):
9792
risk_tag = (
9893
argument_collection.arg_configs.get(DetectionListUserKeys.RISK_TAG)
9994
or argument_collection.arg_configs[u"tag"]
@@ -105,19 +100,6 @@ def _load_risk_tag_description(argument_collection):
105100
)
106101

107102

108-
def _load_add_description(argument_collection):
109-
load_user_descriptions(argument_collection)
110-
_load_risk_tag_description(argument_collection)
111-
112-
113-
def _load_risk_tag_mgmt_descriptions(argument_collection):
103+
def load_risk_tag_mgmt_descriptions(argument_collection):
114104
load_username_description(argument_collection)
115-
_load_risk_tag_description(argument_collection)
116-
117-
118-
def _handle_list_args(list_arg):
119-
"""Converts str args to a list. Useful for `bulk` commands which don't use `argparse` but
120-
instead pass in values from files, such as in the form "item1 item2"."""
121-
if list_arg and type(list_arg) != list:
122-
return list_arg.split()
123-
return list_arg
105+
load_risk_tag_description(argument_collection)

0 commit comments

Comments
 (0)