diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b666d7f..16b7cf1b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The intended audience of this file is for py42 consumers -- as such, changes that don't affect how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here. -## Unreleased +## 0.5.0 - Unreleased ### Changed @@ -36,6 +36,8 @@ how a consumer would use the library (e.g. adding unit tests, updating documenta - `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. + - `add-risk-tags` that takes a username and risk tags. + - `remove-risk-tags` that takes a username and risk tags. - `code42 departing-employee` commands: - `bulk` with subcommands: - `add`: that takes a csv file of users. diff --git a/src/code42cli/__version__.py b/src/code42cli/__version__.py index cd1ee63b7..3d187266f 100644 --- a/src/code42cli/__version__.py +++ b/src/code42cli/__version__.py @@ -1 +1 @@ -__version__ = "0.4.4" +__version__ = "0.5.0" diff --git a/src/code42cli/cmds/detectionlists/__init__.py b/src/code42cli/cmds/detectionlists/__init__.py index 2a419d3d3..c9d1b5db9 100644 --- a/src/code42cli/cmds/detectionlists/__init__.py +++ b/src/code42cli/cmds/detectionlists/__init__.py @@ -83,7 +83,7 @@ def load_subcommands(self): self.handlers.add_employee, self.handlers.load_add_description ) remove = self.factory.create_remove_command( - self.handlers.remove_employee, _load_username_description + self.handlers.remove_employee, load_username_description ) return [bulk, add, remove] @@ -148,7 +148,9 @@ def _remove_employee(self, sdk, profile, *args, **kwargs): self.handlers.remove_employee(sdk, profile, *args, **kwargs) -def _load_username_description(argument_collection): + +def load_username_description(argument_collection): + """Loads the arg descriptions for the `username` CLI parameter.""" username = argument_collection.arg_configs[DetectionListUserKeys.USERNAME] username.set_help(u"A code42 username for an employee.") @@ -161,7 +163,7 @@ def load_user_descriptions(argument_collection): argument_collection (ArgConfigCollection): The arg configs off the command that needs its user descriptions loaded. """ - _load_username_description(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"An alternative email address for another cloud service.") diff --git a/src/code42cli/cmds/detectionlists/high_risk_employee.py b/src/code42cli/cmds/detectionlists/high_risk_employee.py index 7bc73caeb..2bd6d0d01 100644 --- a/src/code42cli/cmds/detectionlists/high_risk_employee.py +++ b/src/code42cli/cmds/detectionlists/high_risk_employee.py @@ -2,16 +2,36 @@ DetectionList, DetectionListHandlers, load_user_descriptions, + load_username_description, get_user_id, update_user, ) from code42cli.cmds.detectionlists.enums import DetectionListUserKeys +from code42cli.commands import Command def load_subcommands(): + handlers = _create_handlers() detection_list = DetectionList.create_high_risk_employee_list(handlers) - return detection_list.load_subcommands() + cmd_list = detection_list.load_subcommands() + cmd_list.extend( + [ + Command( + u"add-risk-tags", + u"Associates risk tags with a user.", + handler=add_risk_tags, + arg_customizer=_load_risk_tag_mgmt_descriptions, + ), + Command( + u"remove-risk-tags", + u"Disassociates risk tags from a user.", + handler=remove_risk_tags, + arg_customizer=_load_risk_tag_mgmt_descriptions, + ), + ] + ) + return cmd_list def _create_handlers(): @@ -20,6 +40,17 @@ def _create_handlers(): ) +def add_risk_tags(sdk, profile, username, risk_tag): + risk_tag = _handle_list_args(risk_tag) + user_id = get_user_id(sdk, username) + sdk.detectionlists.add_user_risk_tags(user_id, risk_tag) + +def remove_risk_tags(sdk, profile, username, risk_tag): + risk_tag = _handle_list_args(risk_tag) + user_id = get_user_id(sdk, username) + sdk.detectionlists.remove_user_risk_tags(user_id, risk_tag) + + def add_high_risk_employee(sdk, profile, username, cloud_alias=None, risk_tag=None, notes=None): """Adds an employee to the high risk employee detection list. @@ -31,9 +62,7 @@ def add_high_risk_employee(sdk, profile, username, cloud_alias=None, risk_tag=No risk_tag (iter[str]): Risk tags associated with the employee. notes: (str): Notes about the employee. """ - if risk_tag and type(risk_tag) != list: - risk_tag = risk_tag.split() - + risk_tag = _handle_list_args(risk_tag) user_id = get_user_id(sdk, username) update_user(sdk, user_id, cloud_alias, risk_tag, notes) sdk.detectionlists.high_risk_employee.add(user_id) @@ -51,8 +80,7 @@ def remove_high_risk_employee(sdk, profile, username): sdk.detectionlists.high_risk_employee.remove(user_id) -def _load_add_description(argument_collection): - load_user_descriptions(argument_collection) +def _load_risk_tag_description(argument_collection): risk_tag = argument_collection.arg_configs[DetectionListUserKeys.RISK_TAG] risk_tag.as_multi_val_param() risk_tag.set_help( @@ -66,3 +94,21 @@ def _load_add_description(argument_collection): u"POOR_SECURITY_PRACTICES, " u"CONTRACT_EMPLOYEE]" ) + + +def _load_add_description(argument_collection): + load_user_descriptions(argument_collection) + _load_risk_tag_description(argument_collection) + + +def _load_risk_tag_mgmt_descriptions(argument_collection): + load_username_description(argument_collection) + _load_risk_tag_description(argument_collection) + + +def _handle_list_args(list_arg): + """Converts str args to a list. Useful for `bulk` commands which don't use `argparse` but + instead pass in values from files, such as in the form "item1 item2".""" + if list_arg and type(list_arg) != list: + return list_arg.split() + return list_arg diff --git a/tests/cmds/detectionlists/test_high_risk_employee.py b/tests/cmds/detectionlists/test_high_risk_employee.py index 8706e66bc..baae317d3 100644 --- a/tests/cmds/detectionlists/test_high_risk_employee.py +++ b/tests/cmds/detectionlists/test_high_risk_employee.py @@ -4,6 +4,8 @@ from code42cli.cmds.detectionlists.high_risk_employee import ( add_high_risk_employee, remove_high_risk_employee, + add_risk_tags, + remove_risk_tags, ) from .conftest import TEST_ID @@ -74,3 +76,57 @@ def test_remove_high_risk_employee_when_user_does_not_exist_prints_error( except UserDoesNotExistError: capture = capsys.readouterr() assert str(UserDoesNotExistError(_EMPLOYEE)) in capture.out + + +def test_add_risk_tags_adds_tags(sdk_with_user, profile): + add_risk_tags(sdk_with_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + sdk_with_user.detectionlists.add_user_risk_tags.assert_called_once_with( + TEST_ID, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"] + ) + + +def test_add_risk_tags_when_given_space_delimited_str_adds_expected_tags(sdk_with_user, profile): + add_risk_tags(sdk_with_user, profile, _EMPLOYEE, "TAG_YOU_ARE_IT GROUND_IS_LAVA") + sdk_with_user.detectionlists.add_user_risk_tags.assert_called_once_with( + TEST_ID, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"] + ) + + +def test_add_risk_tags_when_user_does_not_exist_exits(sdk_without_user, profile): + with pytest.raises(UserDoesNotExistError): + add_risk_tags(sdk_without_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + + +def test_add_risk_tags_when_user_does_not_exist_prints_error(sdk_without_user, profile, capsys): + try: + add_risk_tags(sdk_without_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + except UserDoesNotExistError: + capture = capsys.readouterr() + assert str(UserDoesNotExistError(_EMPLOYEE)) in capture.out + + +def test_remove_risk_tags_adds_tags(sdk_with_user, profile): + remove_risk_tags(sdk_with_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + sdk_with_user.detectionlists.remove_user_risk_tags.assert_called_once_with( + TEST_ID, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"] + ) + + +def test_remove_risk_tags_when_given_space_delimited_str_adds_expected_tags(sdk_with_user, profile): + remove_risk_tags(sdk_with_user, profile, _EMPLOYEE, "TAG_YOU_ARE_IT GROUND_IS_LAVA") + sdk_with_user.detectionlists.remove_user_risk_tags.assert_called_once_with( + TEST_ID, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"] + ) + + +def test_remove_risk_tags_when_user_does_not_exist_exits(sdk_without_user, profile): + with pytest.raises(UserDoesNotExistError): + remove_risk_tags(sdk_without_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + + +def test_remove_risk_tags_when_user_does_not_exist_prints_error(sdk_without_user, profile, capsys): + try: + remove_risk_tags(sdk_without_user, profile, _EMPLOYEE, ["TAG_YOU_ARE_IT", "GROUND_IS_LAVA"]) + except UserDoesNotExistError: + capture = capsys.readouterr() + assert str(UserDoesNotExistError(_EMPLOYEE)) in capture.out