diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 472064a1..a5b3e18c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,6 @@ jobs: 127.0.0.1 core 127.0.0.1 alerts 127.0.0.1 alert-rules - 127.0.0.1 detection-lists 127.0.0.1 audit-log 127.0.0.1 file-events 127.0.0.1 storage diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac452e4..861005dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ 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. +## 1.17.0 - 2023-08-04 + +### Removed + +- Removed the following command groups following deprecation: + - `detection-lists` + - `departing-employee` + - `high-risk-employee` +- APIs were replaced by the `watchlists` commands + ## 1.16.6 - 2023-04-12 ### Fixed diff --git a/README.md b/README.md index a3bb56d6..f265ba14 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ 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. Similarly, - there is `code42 departing-employee`. +* `code42 watchlists` is a collection of tools for managing your employee watchlists. ## Requirements @@ -212,38 +211,6 @@ To get the results of a saved search, use the `--saved-search` option with your code42 security-data search --saved-search ``` -## Detection Lists - -You can both add and remove employees from detection lists using the CLI. This example uses `high-risk-employee`. - -```bash -code42 high-risk-employee add user@example.com --notes "These are notes" -code42 high-risk-employee remove user@example.com -``` - -Detection lists include a `bulk` command. To add employees to a list, you can pass in a csv file. First, generate the -csv file for the desired command by executing the `generate-template` command: - -```bash -code42 high-risk-employee bulk generate-template add -``` - -Notice that `generate-template` takes a `cmd` parameter for determining what type of template to generate. In the -example above, we give it the value `add` to generate a file for bulk adding users to the high risk employee list. - -Next, fill out the csv file with all the users and then pass it in as a parameter to `bulk add`: - -```bash -code42 high-risk-employee bulk add users_to_add.csv -``` - -Note that for `bulk remove`, the file only has to be an end-line delimited list of users with one line per user. - -## Known Issues - -In `security-data`, only the first 10,000 of each set of events containing the exact same insertion timestamp is -reported. - ## Troubleshooting If you keep getting prompted for your password, try resetting with `code42 profile reset-pw`. diff --git a/docs/commands.md b/docs/commands.md index 4ebf3509..79c54b17 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -17,8 +17,6 @@ Trusted Activities Users Watchlists - (DEPRECATED) Departing Employee - (DEPRECATED) High Risk Employee ``` * [Alert Rules](commands/alertrules.rst) @@ -32,5 +30,3 @@ * [Trusted Activities](commands/trustedactivities.rst) * [Users](commands/users.rst) * [Watchlists](commands/watchlists.rst) -* [(DEPRECATED) Departing Employee](commands/departingemployee.rst) -* [(DEPRECATED) High Risk Employee](commands/highriskemployee.rst) diff --git a/docs/commands/departingemployee.rst b/docs/commands/departingemployee.rst deleted file mode 100644 index a1c3ac5e..00000000 --- a/docs/commands/departingemployee.rst +++ /dev/null @@ -1,3 +0,0 @@ -.. click:: code42cli.cmds.departing_employee:departing_employee - :prog: departing-employee - :nested: full diff --git a/docs/commands/highriskemployee.rst b/docs/commands/highriskemployee.rst deleted file mode 100644 index 4fd75700..00000000 --- a/docs/commands/highriskemployee.rst +++ /dev/null @@ -1,3 +0,0 @@ -.. click:: code42cli.cmds.high_risk_employee:high_risk_employee - :prog: high-risk-employee - :nested: full diff --git a/docs/guides.md b/docs/guides.md index 86e9bc4f..df4ddc01 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -19,7 +19,6 @@ Add and manage cases Perform bulk actions Manage watchlist members - (DEPRECATED) Manage detection list users ``` * [Get started with the Code42 command-line interface (CLI)](userguides/gettingstarted.md) @@ -35,4 +34,3 @@ * [Add and manage cases](userguides/cases.md) * [Perform bulk actions](userguides/bulkcommands.md) * [Manage watchlist members](userguides/watchlists.md) -* [(DEPRECATED) Manage detection list users](userguides/detectionlists.md) diff --git a/docs/userguides/detectionlists.md b/docs/userguides/detectionlists.md deleted file mode 100644 index ce96c547..00000000 --- a/docs/userguides/detectionlists.md +++ /dev/null @@ -1,62 +0,0 @@ -# (DEPRECATED) Manage Detection List Users - -```{eval-rst} -.. note:: - - Detection Lists have been replaced by Watchlists. - - Functionality for adding users to Departing Employee and High Risk Employee categories has been migrated to the :code:`code42 watchlists` command group. - - Functionality for listing and managing User Risk Profiles (e.g. adding Cloud Aliases, Notes, and Start/End dates to a user profile) has been migrated to the :code:`code42 users` command group. -``` - -Use the `departing-employee` commands to add employees to or remove employees from the Departing Employees list. Use the `high-risk-employee` commands to add employees to or remove employees from the High Risk list, or update risk tags for those users. - -To see a list of all the users currently in your organization: -- Export a list from the [Users action menu](https://support.code42.com/Administrator/Cloud/Code42_console_reference/Users_reference#Action_menu). -- Use the [CLI users commands](./users.md). - -## Get CSV template -To add multiple users to the Departing Employees list: - -1. Generate a CSV template. Below is an example command for generating a template to use to add employees to the Departing -Employees list. Once generated, the CSV file is saved to your current working directory. - -```bash -code42 departing-employee bulk generate-template add -``` - -2. Use the CSV template to enter the employees' information. Only the Code42 username is required. If added, -the departure date must be in yyyy-MM-dd format. Note: you are only able to add departure dates during the `add` -operation. If you don't include `--departure-date`, you can only add one later by removing and then re-adding the -employee. - -3. Save the CSV file. - -## Add users to the Departing Employees list - -Once you have entered the employees' information in the CSV file, use the `bulk add` command with the CSV file path to -add multiple users at once. For example: - -```bash -code42 departing-employee bulk add /Users/astrid.ludwig/add_departing_employee.csv -``` - -## Remove users -You can remove one or more users from the High Risk Employees list. Use `code42 departing-employee remove` to remove a -single user. - -To remove multiple users at once: - -1. Create a CSV file with one username per line. - -2. Save the file to your current working directory. - -3. Use the `bulk remove` command. For example: - -```bash -code42 high-risk-employee bulk remove /Users/matt.allen/remove_high_risk_employee.csv -``` - -Learn more about the [Departing Employee](../commands/departingemployee.md) and -[High Risk Employee](../commands/highriskemployee.md) commands. diff --git a/src/code42cli/__version__.py b/src/code42cli/__version__.py index 032e9cb4..30244104 100644 --- a/src/code42cli/__version__.py +++ b/src/code42cli/__version__.py @@ -1 +1 @@ -__version__ = "1.16.6" +__version__ = "1.17.0" diff --git a/src/code42cli/cmds/departing_employee.py b/src/code42cli/cmds/departing_employee.py deleted file mode 100644 index 5a683f45..00000000 --- a/src/code42cli/cmds/departing_employee.py +++ /dev/null @@ -1,183 +0,0 @@ -import click -from py42.services.detectionlists.departing_employee import DepartingEmployeeFilters - -from code42cli.bulk import generate_template_cmd_factory -from code42cli.bulk import run_bulk_process -from code42cli.click_ext.groups import OrderedGroup -from code42cli.cmds.detectionlists import ALL_FILTER -from code42cli.cmds.detectionlists import get_choices -from code42cli.cmds.detectionlists import handle_filter_choice -from code42cli.cmds.detectionlists import list_employees -from code42cli.cmds.detectionlists import update_user -from code42cli.cmds.detectionlists.options import cloud_alias_option -from code42cli.cmds.detectionlists.options import notes_option -from code42cli.cmds.detectionlists.options import username_arg -from code42cli.cmds.shared import get_user_id -from code42cli.errors import Code42CLIError -from code42cli.file_readers import read_csv_arg -from code42cli.options import format_option -from code42cli.options import sdk_options -from code42cli.util import deprecation_warning - - -def _get_filter_choices(): - filters = DepartingEmployeeFilters.choices() - return get_choices(filters) - - -DEPRECATION_TEXT = "(DEPRECATED): Use `code42 watchlists` commands instead." - -DATE_FORMAT = "%Y-%m-%d" -filter_option = click.option( - "--filter", - help=f"Departing employee filter options. Defaults to {ALL_FILTER}.", - type=click.Choice(_get_filter_choices()), - default=ALL_FILTER, - callback=lambda ctx, param, arg: handle_filter_choice(arg), -) - - -@click.group( - cls=OrderedGroup, - help=f"{DEPRECATION_TEXT}\n\nAdd and remove employees from the Departing Employees detection list.", -) -@sdk_options(hidden=True) -def departing_employee(state): - pass - - -@departing_employee.command( - "list", - help=f"{DEPRECATION_TEXT}\n\nLists the users on the Departing Employees list.", -) -@sdk_options() -@format_option -@filter_option -def _list(state, format, filter): - deprecation_warning(DEPRECATION_TEXT) - employee_generator = _get_departing_employees(state.sdk, filter) - list_employees( - employee_generator, - format, - {"departureDate": "Departure Date"}, - ) - - -@departing_employee.command( - help=f"{DEPRECATION_TEXT}\n\nAdd a user to the Departing Employees detection list." -) -@username_arg -@click.option( - "--departure-date", - help="The date the employee is departing. Format: yyyy-MM-dd.", - type=click.DateTime(formats=[DATE_FORMAT]), -) -@cloud_alias_option -@notes_option -@sdk_options() -def add(state, username, cloud_alias, departure_date, notes): - - deprecation_warning(DEPRECATION_TEXT) - _add_departing_employee(state.sdk, username, cloud_alias, departure_date, notes) - - -@departing_employee.command( - help=f"{DEPRECATION_TEXT}\n\nRemove a user from the Departing Employees detection list." -) -@username_arg -@sdk_options() -def remove(state, username): - deprecation_warning(DEPRECATION_TEXT) - _remove_departing_employee(state.sdk, username) - - -@departing_employee.group( - cls=OrderedGroup, - help=f"{DEPRECATION_TEXT}\n\nTools for executing bulk departing employee actions.", -) -@sdk_options(hidden=True) -def bulk(state): - pass - - -DEPARTING_EMPLOYEE_CSV_HEADERS = ["username", "cloud_alias", "departure_date", "notes"] - -REMOVE_EMPLOYEE_HEADERS = ["username"] - -departing_employee_generate_template = generate_template_cmd_factory( - group_name="departing_employee", - commands_dict={ - "add": DEPARTING_EMPLOYEE_CSV_HEADERS, - "remove": REMOVE_EMPLOYEE_HEADERS, - }, -) -bulk.add_command(departing_employee_generate_template) - - -@bulk.command( - name="add", - help=f"{DEPRECATION_TEXT}\n\nBulk add users to the departing employees detection list using " - f"a CSV file with format: {','.join(DEPARTING_EMPLOYEE_CSV_HEADERS)}.", -) -@read_csv_arg(headers=DEPARTING_EMPLOYEE_CSV_HEADERS) -@sdk_options() -def bulk_add(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk # Force initialization of py42 to only happen once. - - def handle_row(username, cloud_alias, departure_date, notes): - if departure_date: - try: - departure_date = click.DateTime(formats=[DATE_FORMAT]).convert( - departure_date, None, None - ) - except click.exceptions.BadParameter: - message = ( - f"Invalid date {departure_date}, valid date format {DATE_FORMAT}." - ) - raise Code42CLIError(message) - _add_departing_employee(sdk, username, cloud_alias, departure_date, notes) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Adding users to the Departing Employees detection list:", - ) - - -@bulk.command( - name="remove", - help=f"{DEPRECATION_TEXT}\n\nBulk remove users from the departing employees detection list " - f"using a CSV file with format {','.join(REMOVE_EMPLOYEE_HEADERS)}.", -) -@read_csv_arg(headers=REMOVE_EMPLOYEE_HEADERS) -@sdk_options() -def bulk_remove(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk - - def handle_row(username): - _remove_departing_employee(sdk, username) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Removing users from the Departing Employees detection list:", - ) - - -def _get_departing_employees(sdk, filter): - return sdk.detectionlists.departing_employee.get_all(filter) - - -def _add_departing_employee(sdk, username, cloud_alias, departure_date, notes): - if departure_date: - departure_date = departure_date.strftime(DATE_FORMAT) - user_id = get_user_id(sdk, username) - sdk.detectionlists.departing_employee.add(user_id, departure_date) - update_user(sdk, username, cloud_alias=cloud_alias, notes=notes) - - -def _remove_departing_employee(sdk, username): - user_id = get_user_id(sdk, username) - sdk.detectionlists.departing_employee.remove(user_id) diff --git a/src/code42cli/cmds/detectionlists/__init__.py b/src/code42cli/cmds/detectionlists/__init__.py deleted file mode 100644 index ddc039d4..00000000 --- a/src/code42cli/cmds/detectionlists/__init__.py +++ /dev/null @@ -1,95 +0,0 @@ -import click -from py42.services.detectionlists import _DetectionListFilters - -from code42cli.cmds.shared import get_user_id -from code42cli.output_formats import OutputFormat -from code42cli.output_formats import OutputFormatter - - -ALL_FILTER = "ALL" - - -def get_choices(filters): - filters.remove(_DetectionListFilters.OPEN) - filters.append(ALL_FILTER) - return filters - - -def handle_filter_choice(choice): - if choice == ALL_FILTER: - return _DetectionListFilters.OPEN - return choice - - -def list_employees(employee_generator, output_format, additional_header_items=None): - additional_header_items = additional_header_items or {} - header = {"userName": "Username", "notes": "Notes", **additional_header_items} - employee_list = [] - for employees in employee_generator: - for employee in employees["items"]: - if employee.get("notes") and output_format == OutputFormat.TABLE: - employee["notes"] = ( - employee["notes"].replace("\n", "\\n").replace("\t", "\\t") - ) - employee_list.append(employee) - if employee_list: - formatter = OutputFormatter(output_format, header) - formatter.echo_formatted_list(employee_list) - else: - click.echo("No users found.") - - -def update_user(sdk, username, cloud_alias=None, risk_tag=None, notes=None): - """Updates a detection list user. - - Args: - sdk (py42.sdk.SDKClient): py42 sdk. - username (str): The username of the user to update. - cloud_alias (str): A cloud alias to add to the user. - risk_tag (iter[str]): A list of risk tags associated with user. - notes (str): Notes about the user. - """ - user_id = get_user_id(sdk, username) - _update_cloud_alias(sdk, user_id, cloud_alias) - _update_risk_tags(sdk, username, risk_tag) - _update_notes(sdk, user_id, notes) - - -def _update_cloud_alias(sdk, user_id, cloud_alias): - if cloud_alias: - profile = sdk.detectionlists.get_user_by_id(user_id) - cloud_aliases = profile.data.get("cloudUsernames") or [] - for alias in cloud_aliases: - if alias != profile["userName"]: - sdk.detectionlists.remove_user_cloud_alias(user_id, alias) - sdk.detectionlists.add_user_cloud_alias(user_id, cloud_alias) - - -def _update_risk_tags(sdk, username, risk_tag): - if risk_tag: - add_risk_tags(sdk, username, risk_tag) - - -def _update_notes(sdk, user_id, notes): - if notes: - sdk.detectionlists.update_user_notes(user_id, notes) - - -def add_risk_tags(sdk, 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, 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 handle_list_args(list_arg): - """Converts str args to a list. Useful for `bulk` commands which don't use click's argument - parsing but instead pass in values from files, such as in the form "item1 item2".""" - if isinstance(list_arg, str): - return list_arg.split() - return list_arg diff --git a/src/code42cli/cmds/detectionlists/options.py b/src/code42cli/cmds/detectionlists/options.py deleted file mode 100644 index da538e93..00000000 --- a/src/code42cli/cmds/detectionlists/options.py +++ /dev/null @@ -1,11 +0,0 @@ -import click - -username_arg = click.argument("username") -cloud_alias_option = click.option( - "--cloud-alias", - help="If the employee has an email alias other than their Code42 username " - "that they use for cloud services such as Google Drive, OneDrive, or Box, " - "add and monitor the alias. WARNING: Adding a cloud alias will override any " - "existing cloud alias for this user.", -) -notes_option = click.option("--notes", help="Optional notes about the employee.") diff --git a/src/code42cli/cmds/high_risk_employee.py b/src/code42cli/cmds/high_risk_employee.py deleted file mode 100644 index a5153d1b..00000000 --- a/src/code42cli/cmds/high_risk_employee.py +++ /dev/null @@ -1,240 +0,0 @@ -import click -from py42.clients.detectionlists import RiskTags -from py42.services.detectionlists.high_risk_employee import HighRiskEmployeeFilters - -from code42cli.bulk import generate_template_cmd_factory -from code42cli.bulk import run_bulk_process -from code42cli.click_ext.groups import OrderedGroup -from code42cli.cmds.detectionlists import add_risk_tags as _add_risk_tags -from code42cli.cmds.detectionlists import ALL_FILTER -from code42cli.cmds.detectionlists import get_choices -from code42cli.cmds.detectionlists import handle_filter_choice -from code42cli.cmds.detectionlists import handle_list_args -from code42cli.cmds.detectionlists import list_employees -from code42cli.cmds.detectionlists import remove_risk_tags as _remove_risk_tags -from code42cli.cmds.detectionlists import update_user -from code42cli.cmds.detectionlists.options import cloud_alias_option -from code42cli.cmds.detectionlists.options import notes_option -from code42cli.cmds.detectionlists.options import username_arg -from code42cli.cmds.shared import get_user_id -from code42cli.file_readers import read_csv_arg -from code42cli.options import format_option -from code42cli.options import sdk_options -from code42cli.util import deprecation_warning - -DEPRECATION_TEXT = "(DEPRECATED): Use `code42 watchlists` commands instead." - - -def _get_filter_choices(): - filters = HighRiskEmployeeFilters.choices() - return get_choices(filters) - - -filter_option = click.option( - "--filter", - help=f"High risk employee filter options. Defaults to {ALL_FILTER}.", - type=click.Choice(_get_filter_choices()), - default=ALL_FILTER, - callback=lambda ctx, param, arg: handle_filter_choice(arg), -) - - -risk_tag_option = click.option( - "-t", - "--risk-tag", - multiple=True, - type=click.Choice(RiskTags.choices()), - help="Risk tags associated with the employee.", -) - - -@click.group( - cls=OrderedGroup, - help=f"{DEPRECATION_TEXT}\n\nAdd and remove employees from the High Risk Employees detection list.", -) -@sdk_options(hidden=True) -def high_risk_employee(state): - pass - - -@high_risk_employee.command( - "list", - help=f"{DEPRECATION_TEXT}\n\nLists the employees on the High Risk Employee list.", -) -@sdk_options() -@format_option -@filter_option -def _list(state, format, filter): - deprecation_warning(DEPRECATION_TEXT) - employee_generator = _get_high_risk_employees(state.sdk, filter) - list_employees(employee_generator, format) - - -@high_risk_employee.command( - help=f"{DEPRECATION_TEXT}\n\nAdd a user to the high risk employees detection list." -) -@cloud_alias_option -@notes_option -@risk_tag_option -@username_arg -@sdk_options() -def add(state, username, cloud_alias, risk_tag, notes): - deprecation_warning(DEPRECATION_TEXT) - _add_high_risk_employee(state.sdk, username, cloud_alias, risk_tag, notes) - - -@high_risk_employee.command( - help=f"{DEPRECATION_TEXT}\n\nRemove a user from the high risk employees detection list." -) -@username_arg -@sdk_options() -def remove(state, username): - deprecation_warning(DEPRECATION_TEXT) - _remove_high_risk_employee(state.sdk, username) - - -@high_risk_employee.command( - help=f"{DEPRECATION_TEXT}\n\nAssociates risk tags with a user." -) -@username_arg -@risk_tag_option -@sdk_options() -def add_risk_tags(state, username, risk_tag): - deprecation_warning(DEPRECATION_TEXT) - _add_risk_tags(state.sdk, username, risk_tag) - - -@high_risk_employee.command( - help=f"{DEPRECATION_TEXT}\n\nDisassociates risk tags from a user." -) -@username_arg -@risk_tag_option -@sdk_options() -def remove_risk_tags(state, username, risk_tag): - deprecation_warning(DEPRECATION_TEXT) - _remove_risk_tags(state.sdk, username, risk_tag) - - -@high_risk_employee.group( - cls=OrderedGroup, - help=f"{DEPRECATION_TEXT}\n\nTools for executing high risk employee actions in bulk.", -) -@sdk_options(hidden=True) -def bulk(state): - pass - - -HIGH_RISK_EMPLOYEE_CSV_HEADERS = ["username", "cloud_alias", "risk_tag", "notes"] -RISK_TAG_CSV_HEADERS = ["username", "tag"] -REMOVE_EMPLOYEE_HEADERS = ["username"] - -high_risk_employee_generate_template = generate_template_cmd_factory( - group_name="high_risk_employee", - commands_dict={ - "add": HIGH_RISK_EMPLOYEE_CSV_HEADERS, - "remove": REMOVE_EMPLOYEE_HEADERS, - "add-risk-tags": RISK_TAG_CSV_HEADERS, - "remove-risk-tags": RISK_TAG_CSV_HEADERS, - }, -) -bulk.add_command(high_risk_employee_generate_template) - - -@bulk.command( - name="add", - help=f"{DEPRECATION_TEXT}\n\nBulk add users to the high risk employees detection list using a " - f"CSV file with format: {','.join(HIGH_RISK_EMPLOYEE_CSV_HEADERS)}.", -) -@read_csv_arg(headers=HIGH_RISK_EMPLOYEE_CSV_HEADERS) -@sdk_options() -def bulk_add(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk - - def handle_row(username, cloud_alias, risk_tag, notes): - _add_high_risk_employee(sdk, username, cloud_alias, risk_tag, notes) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Adding users to high risk employee detection list:", - ) - - -@bulk.command( - name="remove", - help=f"{DEPRECATION_TEXT}\n\nBulk remove users from the high risk employees detection list " - f"using a CSV file with format {','.join(REMOVE_EMPLOYEE_HEADERS)}.", -) -@read_csv_arg(headers=REMOVE_EMPLOYEE_HEADERS) -@sdk_options() -def bulk_remove(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk - - def handle_row(username): - _remove_high_risk_employee(sdk, username) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Removing users from high risk employee detection list:", - ) - - -@bulk.command( - name="add-risk-tags", - help=f"{DEPRECATION_TEXT}\n\nAdds risk tags to users in bulk using a CSV file with format: " - f"{','.join(RISK_TAG_CSV_HEADERS)}.", -) -@read_csv_arg(headers=RISK_TAG_CSV_HEADERS) -@sdk_options() -def bulk_add_risk_tags(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk - - def handle_row(username, tag): - _add_risk_tags(sdk, username, tag) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Adding risk tags to users:", - ) - - -@bulk.command( - name="remove-risk-tags", - help=f"{DEPRECATION_TEXT}\n\nRemoves risk tags from users in bulk using a CSV file with " - f"format: {','.join(RISK_TAG_CSV_HEADERS)}.", -) -@read_csv_arg(headers=RISK_TAG_CSV_HEADERS) -@sdk_options() -def bulk_remove_risk_tags(state, csv_rows): - deprecation_warning(DEPRECATION_TEXT) - sdk = state.sdk - - def handle_row(username, tag): - _remove_risk_tags(sdk, username, tag) - - run_bulk_process( - handle_row, - csv_rows, - progress_label="Removing risk tags from users:", - ) - - -def _get_high_risk_employees(sdk, filter): - return sdk.detectionlists.high_risk_employee.get_all(filter) - - -def _add_high_risk_employee(sdk, username, cloud_alias, risk_tag, notes): - risk_tag = handle_list_args(risk_tag) - user_id = get_user_id(sdk, username) - sdk.detectionlists.high_risk_employee.add(user_id) - update_user(sdk, username, cloud_alias=cloud_alias, risk_tag=risk_tag, notes=notes) - - -def _remove_high_risk_employee(sdk, username): - user_id = get_user_id(sdk, username) - sdk.detectionlists.high_risk_employee.remove(user_id) diff --git a/src/code42cli/cmds/shared.py b/src/code42cli/cmds/shared.py index 4fa7a29b..e87da7d8 100644 --- a/src/code42cli/cmds/shared.py +++ b/src/code42cli/cmds/shared.py @@ -5,7 +5,7 @@ @lru_cache(maxsize=None) def get_user_id(sdk, username): - """Returns the user's UID (referred to by `user_id` in detection lists). + """Returns the user's UID. Raises `UserDoesNotExistError` if the user doesn't exist in the Code42 server. Args: diff --git a/src/code42cli/main.py b/src/code42cli/main.py index 5427c653..9be3d29f 100644 --- a/src/code42cli/main.py +++ b/src/code42cli/main.py @@ -16,9 +16,7 @@ from code42cli.cmds.alerts import alerts from code42cli.cmds.auditlogs import audit_logs from code42cli.cmds.cases import cases -from code42cli.cmds.departing_employee import departing_employee from code42cli.cmds.devices import devices -from code42cli.cmds.high_risk_employee import high_risk_employee from code42cli.cmds.legal_hold import legal_hold from code42cli.cmds.profile import profile from code42cli.cmds.securitydata import security_data @@ -88,9 +86,7 @@ def cli(state, python, script_dir): cli.add_command(alert_rules) cli.add_command(audit_logs) cli.add_command(cases) -cli.add_command(departing_employee) cli.add_command(devices) -cli.add_command(high_risk_employee) cli.add_command(legal_hold) cli.add_command(profile) cli.add_command(security_data) diff --git a/tests/cmds/conftest.py b/tests/cmds/conftest.py index 3dd9fea5..c60fdbf3 100644 --- a/tests/cmds/conftest.py +++ b/tests/cmds/conftest.py @@ -3,7 +3,6 @@ import threading import pytest -from py42.exceptions import Py42UserAlreadyAddedError from py42.exceptions import Py42UserNotOnListError from py42.sdk import SDKClient from requests import HTTPError @@ -81,11 +80,6 @@ def custom_error(mocker): return err -@pytest.fixture -def user_already_added_error(custom_error): - return Py42UserAlreadyAddedError(custom_error, TEST_ID, "detection list") - - def get_filter_value_from_json(json, filter_index): return json_module.loads(str(json))["filters"][filter_index]["value"] diff --git a/tests/cmds/detectionlists/__init__.py b/tests/cmds/detectionlists/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/cmds/detectionlists/test_init.py b/tests/cmds/detectionlists/test_init.py deleted file mode 100644 index c07f3c40..00000000 --- a/tests/cmds/detectionlists/test_init.py +++ /dev/null @@ -1,52 +0,0 @@ -import pytest -from tests.conftest import create_mock_response - -from code42cli.cmds.detectionlists import update_user - -MOCK_USER_ID = "USER-ID" -MOCK_USER_NAME = "test@example.com" -MOCK_ALIAS = "alias@example" -MOCK_USER_PROFILE_RESPONSE = f""" -{{ - "type$": "USER_V2", - "tenantId": "TENANT-ID", - "userId": "{MOCK_USER_ID}", - "userName": "{MOCK_USER_NAME}", - "displayName": "Test", - "notes": "Notes", - "cloudUsernames": ["{MOCK_ALIAS}", "{MOCK_USER_NAME}"], - "riskFactors": ["HIGH_IMPACT_EMPLOYEE"] -}} -""" - - -@pytest.fixture -def user_response_with_cloud_aliases(mocker): - return create_mock_response(mocker, data=MOCK_USER_PROFILE_RESPONSE) - - -@pytest.fixture -def mock_user_id(mocker): - mock = mocker.patch("code42cli.cmds.detectionlists.get_user_id") - mock.return_value = MOCK_USER_ID - return mock - - -def test_update_user_when_given_cloud_alias_add_cloud_alias( - sdk, user_response_with_cloud_aliases, mock_user_id -): - sdk.detectionlists.get_user_by_id.return_value = user_response_with_cloud_aliases - update_user(sdk, MOCK_USER_NAME, cloud_alias="new.alias@exaple.com") - sdk.detectionlists.add_user_cloud_alias.assert_called_once_with( - MOCK_USER_ID, "new.alias@exaple.com" - ) - - -def test_update_user_when_given_cloud_alias_first_removes_old_alias( - sdk, user_response_with_cloud_aliases, mock_user_id -): - sdk.detectionlists.get_user_by_id.return_value = user_response_with_cloud_aliases - update_user(sdk, MOCK_USER_NAME, cloud_alias="new.alias@exaple.com") - sdk.detectionlists.remove_user_cloud_alias.assert_called_once_with( - MOCK_USER_ID, MOCK_ALIAS - ) diff --git a/tests/cmds/test_departing_employee.py b/tests/cmds/test_departing_employee.py deleted file mode 100644 index d682709b..00000000 --- a/tests/cmds/test_departing_employee.py +++ /dev/null @@ -1,441 +0,0 @@ -import json - -import pytest -from py42.services.detectionlists.departing_employee import DepartingEmployeeFilters -from tests.cmds.conftest import get_generator_for_get_all -from tests.cmds.conftest import get_user_not_on_list_side_effect -from tests.cmds.conftest import thread_safe_side_effect -from tests.conftest import TEST_ID - -from .conftest import TEST_EMPLOYEE -from code42cli.main import cli - - -DEPARTING_EMPLOYEE_ITEM = """{ - "type$": "DEPARTING_EMPLOYEE_V2", - "tenantId": "1111111-af5b-4231-9d8e-000000000", - "userId": "TEST USER UID", - "userName": "test.testerson@example.com", - "displayName": "Testerson", - "notes": "Leaving for competitor", - "createdAt": "2020-06-23T19:57:37.1345130Z", - "status": "OPEN", - "cloudUsernames": ["cloud@example.com"], - "departureDate": "2020-07-07" -} -""" -DEPARTING_EMPLOYEE_COMMAND = "departing-employee" - - -@pytest.fixture() -def mock_get_all_empty_state(mocker, cli_state_with_user): - generator = get_generator_for_get_all(mocker, None) - cli_state_with_user.sdk.detectionlists.departing_employee.get_all.side_effect = ( - generator - ) - return cli_state_with_user - - -@pytest.fixture() -def mock_get_all_state(mocker, cli_state_with_user): - generator = get_generator_for_get_all(mocker, DEPARTING_EMPLOYEE_ITEM) - cli_state_with_user.sdk.detectionlists.departing_employee.get_all.side_effect = ( - generator - ) - return cli_state_with_user - - -def test_list_departing_employees_lists_expected_properties(runner, mock_get_all_state): - res = runner.invoke(cli, ["departing-employee", "list"], obj=mock_get_all_state) - assert "Username" in res.output - assert "Notes" in res.output - assert "test.testerson@example.com" in res.output - assert "Leaving for competitor" in res.output - assert "Departure Date" in res.output - assert "2020-07-07" in res.output - - -def test_list_departing_employees_converts_all_to_open(runner, mock_get_all_state): - runner.invoke( - cli, ["departing-employee", "list", "--filter", "ALL"], obj=mock_get_all_state - ) - mock_get_all_state.sdk.detectionlists.departing_employee.get_all.assert_called_once_with( - DepartingEmployeeFilters.OPEN - ) - - -def test_list_departing_employees_when_given_raw_json_lists_expected_properties( - runner, mock_get_all_state -): - res = runner.invoke( - cli, ["departing-employee", "list", "-f", "RAW-JSON"], obj=mock_get_all_state - ) - assert "userName" in res.output - assert "notes" in res.output - assert "test.testerson@example.com" in res.output - assert "Leaving for competitor" in res.output - assert "cloudUsernames" in res.output - assert "cloud@example.com" in res.output - assert "departureDate" in res.output - assert "2020-07-07" in res.output - - -def test_list_departing_employees_when_no_employees_echos_expected_message( - runner, mock_get_all_empty_state -): - res = runner.invoke( - cli, ["departing-employee", "list"], obj=mock_get_all_empty_state - ) - assert "No users found." in res.output - - -def test_list_departing_employees_when_table_format_and_notes_contains_newlines_escapes_them( - runner, mocker, cli_state_with_user -): - new_line_text = str(DEPARTING_EMPLOYEE_ITEM).replace( - "Leaving for competitor", r"Line1\nLine2" - ) - generator = get_generator_for_get_all(mocker, new_line_text) - cli_state_with_user.sdk.detectionlists.departing_employee.get_all.side_effect = ( - generator - ) - res = runner.invoke(cli, ["departing-employee", "list"], obj=cli_state_with_user) - assert "Line1\\nLine2" in res.output - - -def test_list_departing_employees_uses_filter_option(runner, mock_get_all_state): - runner.invoke( - cli, - [ - "departing-employee", - "list", - "--filter", - DepartingEmployeeFilters.EXFILTRATION_30_DAYS, - ], - obj=mock_get_all_state, - ) - mock_get_all_state.sdk.detectionlists.departing_employee.get_all.assert_called_once_with( - DepartingEmployeeFilters.EXFILTRATION_30_DAYS - ) - - -def test_list_departing_employees_handles_employees_with_no_notes( - runner, mocker, cli_state_with_user -): - hr_json = json.loads(DEPARTING_EMPLOYEE_ITEM) - hr_json["notes"] = None - new_text = json.dumps(hr_json) - generator = get_generator_for_get_all(mocker, new_text) - cli_state_with_user.sdk.detectionlists.departing_employee.get_all.side_effect = ( - generator - ) - res = runner.invoke(cli, ["departing-employee", "list"], obj=cli_state_with_user) - assert "None" in res.output - - -def test_add_departing_employee_when_given_cloud_alias_adds_alias( - runner, cli_state_with_user -): - alias = "departing employee alias" - runner.invoke( - cli, - ["departing-employee", "add", TEST_EMPLOYEE, "--cloud-alias", alias], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.add_user_cloud_alias.assert_called_once_with( - TEST_ID, alias - ) - - -def test_add_departing_employee_when_given_notes_updates_notes( - runner, cli_state_with_user, profile -): - notes = "is leaving" - runner.invoke( - cli, - ["departing-employee", "add", TEST_EMPLOYEE, "--notes", notes], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.update_user_notes.assert_called_once_with( - TEST_ID, notes - ) - - -def test_add_departing_employee_adds( - runner, - cli_state_with_user, -): - departure_date = "2020-02-02" - runner.invoke( - cli, - [ - "departing-employee", - "add", - TEST_EMPLOYEE, - "--departure-date", - departure_date, - ], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.departing_employee.add.assert_called_once_with( - TEST_ID, "2020-02-02" - ) - - -def test_add_departing_employee_when_user_does_not_exist_exits( - runner, cli_state_without_user -): - result = runner.invoke( - cli, ["departing-employee", "add", TEST_EMPLOYEE], obj=cli_state_without_user - ) - assert result.exit_code == 1 - assert ( - f"User '{TEST_EMPLOYEE}' does not exist or you do not have permission to view them." - in result.output - ) - - -def test_add_departing_employee_when_user_already_exits_with_correct_message( - runner, cli_state_with_user, user_already_added_error -): - def add_user(user): - raise user_already_added_error - - cli_state_with_user.sdk.detectionlists.departing_employee.add.side_effect = add_user - result = runner.invoke( - cli, ["departing-employee", "add", TEST_EMPLOYEE], obj=cli_state_with_user - ) - assert result.exit_code == 1 - assert f"'{TEST_EMPLOYEE}' is already on the departing-employee list." - - -def test_remove_departing_employee_calls_remove(runner, cli_state_with_user): - runner.invoke( - cli, ["departing-employee", "remove", TEST_EMPLOYEE], obj=cli_state_with_user - ) - cli_state_with_user.sdk.detectionlists.departing_employee.remove.assert_called_once_with( - TEST_ID - ) - - -def test_remove_departing_employee_when_user_does_not_exist_exits( - runner, cli_state_without_user -): - result = runner.invoke( - cli, ["departing-employee", "remove", TEST_EMPLOYEE], obj=cli_state_without_user - ) - assert result.exit_code == 1 - assert ( - f"User '{TEST_EMPLOYEE}' does not exist or you do not have permission to view them." - in result.output - ) - - -def test_add_bulk_users_calls_expected_py42_methods(runner, cli_state): - de_add_user = thread_safe_side_effect() - add_user_cloud_alias = thread_safe_side_effect() - update_user_notes = thread_safe_side_effect() - - cli_state.sdk.detectionlists.departing_employee.add.side_effect = de_add_user - cli_state.sdk.detectionlists.add_user_cloud_alias.side_effect = add_user_cloud_alias - cli_state.sdk.detectionlists.update_user_notes.side_effect = update_user_notes - - with runner.isolated_filesystem(): - with open("test_add.csv", "w") as csv: - csv.writelines( - [ - "username,cloud_alias,departure_date,notes\n", - "test_user,test_alias,2020-01-01,test_note\n", - "test_user_2,test_alias_2,2020-02-01,test_note_2\n", - "test_user_3,,,\n", - "test_user_3,,2020-30-02,\n", - "test_user_3,,20-02-2020,\n", - ] - ) - runner.invoke( - cli, ["departing-employee", "bulk", "add", "test_add.csv"], obj=cli_state - ) - de_add_user_call_args = [call[1] for call in de_add_user.call_args_list] - assert de_add_user.call_count == 3 - assert "2020-01-01" in de_add_user_call_args - assert "2020-02-01" in de_add_user_call_args - assert None in de_add_user_call_args - - add_user_cloud_alias_call_args = [ - call[1] for call in add_user_cloud_alias.call_args_list - ] - assert add_user_cloud_alias.call_count == 2 - assert "test_alias" in add_user_cloud_alias_call_args - assert "test_alias_2" in add_user_cloud_alias_call_args - - update_user_notes_call_args = [call[1] for call in update_user_notes.call_args_list] - assert update_user_notes.call_count == 2 - assert "test_note" in update_user_notes_call_args - assert "test_note_2" in update_user_notes_call_args - - -def test_remove_bulk_users_uses_expected_arguments(runner, mocker, cli_state_with_user): - bulk_processor = mocker.patch("code42cli.cmds.departing_employee.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines(["username\n", "test_user1\n", "test_user2\n"]) - runner.invoke( - cli, - ["departing-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state_with_user, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test_user1"}, - {"username": "test_user2"}, - ] - - -def test_remove_bulk_users_uses_expected_arguments_when_no_header( - runner, mocker, cli_state_with_user -): - bulk_processor = mocker.patch("code42cli.cmds.departing_employee.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines(["test_user1\n", "test_user2\n"]) - runner.invoke( - cli, - ["departing-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state_with_user, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test_user1"}, - {"username": "test_user2"}, - ] - - -def test_remove_bulk_users_uses_expected_arguments_when_extra_columns( - runner, mocker, cli_state_with_user -): - bulk_processor = mocker.patch("code42cli.cmds.departing_employee.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines( - [ - "username,test_column\n", - "test_user1,test_value1\n", - "test_user2,test_value2\n", - ] - ) - runner.invoke( - cli, - ["departing-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state_with_user, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test_user1"}, - {"username": "test_user2"}, - ] - - -def test_remove_bulk_users_uses_expected_arguments_when_flat_file( - runner, mocker, cli_state_with_user -): - bulk_processor = mocker.patch("code42cli.cmds.departing_employee.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.txt", "w") as csv: - csv.writelines(["# username\n", "test_user1\n", "test_user2\n"]) - runner.invoke( - cli, - ["departing-employee", "bulk", "remove", "test_remove.txt"], - obj=cli_state_with_user, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test_user1"}, - {"username": "test_user2"}, - ] - - -def test_add_departing_employee_when_invalid_date_validation_raises_error( - runner, cli_state_with_user -): - # day is out of range for month - departure_date = "2020-02-30" - result = runner.invoke( - cli, - [ - "departing-employee", - "add", - TEST_EMPLOYEE, - "--departure-date", - departure_date, - ], - obj=cli_state_with_user, - ) - assert result.exit_code == 2 - assert ( - "Invalid value for '--departure-date': '2020-02-30' does not match the format '%Y-%m-%d'" - in result.output # invalid datetime format - ) or ( - "Invalid value for '--departure-date': invalid datetime format" in result.output - ) - - -def test_add_departing_employee_when_invalid_date_format_validation_raises_error( - runner, cli_state_with_user -): - departure_date = "2020-30-01" - result = runner.invoke( - cli, - [ - "departing-employee", - "add", - TEST_EMPLOYEE, - "--departure-date", - departure_date, - ], - obj=cli_state_with_user, - ) - assert result.exit_code == 2 - assert ( - "Invalid value for '--departure-date': '2020-30-01' does not match the format '%Y-%m-%d'" - in result.output - ) or ( - "Invalid value for '--departure-date': invalid datetime format" in result.output - ) - - -def test_remove_departing_employee_when_user_not_on_list_prints_expected_error( - mocker, runner, cli_state -): - cli_state.sdk.detectionlists.departing_employee.remove.side_effect = ( - get_user_not_on_list_side_effect(mocker, "departing-employee") - ) - test_username = "test@example.com" - result = runner.invoke( - cli, ["departing-employee", "remove", test_username], obj=cli_state - ) - assert ( - f"User with ID '{TEST_ID}' is not currently on the departing-employee list." - in result.output - ) - - -@pytest.mark.parametrize( - "command, error_msg", - [ - (f"{DEPARTING_EMPLOYEE_COMMAND} add", "Missing argument 'USERNAME'."), - ( - f"{DEPARTING_EMPLOYEE_COMMAND} remove", - "Missing argument 'USERNAME'.", - ), - ( - f"{DEPARTING_EMPLOYEE_COMMAND} bulk add", - "Missing argument 'CSV_FILE'.", - ), - ( - f"{DEPARTING_EMPLOYEE_COMMAND} bulk remove", - "Missing argument 'CSV_FILE'.", - ), - ], -) -def test_departing_employee_command_when_missing_required_parameters_returns_error( - command, error_msg, cli_state, runner -): - result = runner.invoke(cli, command.split(" "), obj=cli_state) - assert result.exit_code == 2 - assert error_msg in "".join(result.output) diff --git a/tests/cmds/test_high_risk_employee.py b/tests/cmds/test_high_risk_employee.py deleted file mode 100644 index 136e9239..00000000 --- a/tests/cmds/test_high_risk_employee.py +++ /dev/null @@ -1,440 +0,0 @@ -import json - -import pytest -from py42.services.detectionlists.high_risk_employee import HighRiskEmployeeFilters -from tests.cmds.conftest import get_generator_for_get_all -from tests.cmds.conftest import get_user_not_on_list_side_effect -from tests.cmds.conftest import TEST_EMPLOYEE -from tests.cmds.conftest import thread_safe_side_effect -from tests.conftest import TEST_ID - -from code42cli.main import cli - -_NAMESPACE = "code42cli.cmds.high_risk_employee" - - -HIGH_RISK_EMPLOYEE_ITEM = """{ - "type$": "HIGH_RISK_EMPLOYEE_V2", - "tenantId": "1111111-af5b-4231-9d8e-000000000", - "userId": "TEST USER UID", - "userName": "test.testerson@example.com", - "displayName": "Testerson", - "notes": "Leaving for competitor", - "createdAt": "2020-06-23T19:57:37.1345130Z", - "status": "OPEN", - "cloudUsernames": ["cloud@example.com"], - "riskFactors": ["PERFORMANCE_CONCERNS"] -} -""" -HR_EMPLOYEE_COMMAND = "high-risk-employee" - - -@pytest.fixture() -def mock_get_all_empty_state(mocker, cli_state_with_user): - generator = get_generator_for_get_all(mocker, None) - cli_state_with_user.sdk.detectionlists.high_risk_employee.get_all.side_effect = ( - generator - ) - return cli_state_with_user - - -@pytest.fixture() -def mock_get_all_state(mocker, cli_state_with_user): - generator = get_generator_for_get_all(mocker, HIGH_RISK_EMPLOYEE_ITEM) - cli_state_with_user.sdk.detectionlists.high_risk_employee.get_all.side_effect = ( - generator - ) - return cli_state_with_user - - -def test_list_high_risk_employees_lists_expected_properties(runner, mock_get_all_state): - res = runner.invoke(cli, ["high-risk-employee", "list"], obj=mock_get_all_state) - assert "Username" in res.output - assert "Notes" in res.output - assert "test.testerson@example.com" in res.output - - -def test_list_high_risk_employees_converts_all_to_open(runner, mock_get_all_state): - runner.invoke( - cli, ["high-risk-employee", "list", "--filter", "ALL"], obj=mock_get_all_state - ) - mock_get_all_state.sdk.detectionlists.high_risk_employee.get_all.assert_called_once_with( - HighRiskEmployeeFilters.OPEN - ) - - -def test_list_high_risk_employees_when_given_raw_json_lists_expected_properties( - runner, mock_get_all_state -): - res = runner.invoke( - cli, ["high-risk-employee", "list", "-f", "RAW-JSON"], obj=mock_get_all_state - ) - assert "userName" in res.output - assert "notes" in res.output - assert "test.testerson@example.com" in res.output - assert "Leaving for competitor" in res.output - assert "cloudUsernames" in res.output - assert "cloud@example.com" in res.output - assert "riskFactors" in res.output - assert "PERFORMANCE_CONCERNS" in res.output - - -def test_list_high_risk_employees_when_no_employees_echos_expected_message( - runner, mock_get_all_empty_state -): - res = runner.invoke( - cli, ["high-risk-employee", "list"], obj=mock_get_all_empty_state - ) - assert "No users found." in res.output - - -def test_list_high_risk_employees_uses_filter_option(runner, mock_get_all_state): - runner.invoke( - cli, - [ - "high-risk-employee", - "list", - "--filter", - HighRiskEmployeeFilters.EXFILTRATION_30_DAYS, - ], - obj=mock_get_all_state, - ) - mock_get_all_state.sdk.detectionlists.high_risk_employee.get_all.assert_called_once_with( - HighRiskEmployeeFilters.EXFILTRATION_30_DAYS, - ) - - -def test_list_high_risk_employees_when_table_format_and_notes_contains_newlines_escapes_them( - runner, mocker, cli_state_with_user -): - new_line_text = str(HIGH_RISK_EMPLOYEE_ITEM).replace( - "Leaving for competitor", r"Line1\nLine2" - ) - generator = get_generator_for_get_all(mocker, new_line_text) - cli_state_with_user.sdk.detectionlists.high_risk_employee.get_all.side_effect = ( - generator - ) - res = runner.invoke(cli, ["high-risk-employee", "list"], obj=cli_state_with_user) - assert "Line1\\nLine2" in res.output - - -def test_list_high_risk_employees_handles_employees_with_no_notes( - runner, mocker, cli_state_with_user -): - hr_json = json.loads(HIGH_RISK_EMPLOYEE_ITEM) - hr_json["notes"] = None - new_text = json.dumps(hr_json) - generator = get_generator_for_get_all(mocker, new_text) - cli_state_with_user.sdk.detectionlists.high_risk_employee.get_all.side_effect = ( - generator - ) - res = runner.invoke(cli, ["high-risk-employee", "list"], obj=cli_state_with_user) - assert "None" in res.output - - -def test_add_high_risk_employee_adds(runner, cli_state_with_user): - runner.invoke( - cli, ["high-risk-employee", "add", TEST_EMPLOYEE], obj=cli_state_with_user - ) - cli_state_with_user.sdk.detectionlists.high_risk_employee.add.assert_called_once_with( - TEST_ID - ) - - -def test_add_high_risk_employee_when_given_cloud_alias_adds_alias( - runner, cli_state_with_user -): - alias = "risk employee alias" - runner.invoke( - cli, - ["high-risk-employee", "add", TEST_EMPLOYEE, "--cloud-alias", alias], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.add_user_cloud_alias.assert_called_once_with( - TEST_ID, alias - ) - - -def test_add_high_risk_employee_when_given_risk_tags_adds_tags( - runner, cli_state_with_user -): - runner.invoke( - cli, - [ - "high-risk-employee", - "add", - TEST_EMPLOYEE, - "-t", - "FLIGHT_RISK", - "-t", - "ELEVATED_ACCESS_PRIVILEGES", - "-t", - "POOR_SECURITY_PRACTICES", - ], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.add_user_risk_tags.assert_called_once_with( - TEST_ID, - ("FLIGHT_RISK", "ELEVATED_ACCESS_PRIVILEGES", "POOR_SECURITY_PRACTICES"), - ) - - -def test_add_high_risk_employee_when_given_notes_updates_notes( - runner, cli_state_with_user -): - notes = "being risky" - runner.invoke( - cli, - ["high-risk-employee", "add", TEST_EMPLOYEE, "--notes", notes], - obj=cli_state_with_user, - ) - cli_state_with_user.sdk.detectionlists.update_user_notes.assert_called_once_with( - TEST_ID, notes - ) - - -def test_add_high_risk_employee_when_user_does_not_exist_exits_with_correct_message( - runner, cli_state_without_user -): - result = runner.invoke( - cli, ["high-risk-employee", "add", TEST_EMPLOYEE], obj=cli_state_without_user - ) - assert result.exit_code == 1 - assert ( - f"User '{TEST_EMPLOYEE}' does not exist or you do not have permission to view them." - in result.output - ) - - -def test_add_high_risk_employee_when_user_already_added_exits_with_correct_message( - runner, cli_state_with_user, user_already_added_error -): - def add_user(user): - raise user_already_added_error - - cli_state_with_user.sdk.detectionlists.high_risk_employee.add.side_effect = add_user - - result = runner.invoke( - cli, ["high-risk-employee", "add", TEST_EMPLOYEE], obj=cli_state_with_user - ) - assert result.exit_code == 1 - assert "User with ID TEST_ID is already on the detection list" in result.output - - -def test_remove_high_risk_employee_calls_remove(runner, cli_state_with_user): - runner.invoke( - cli, ["high-risk-employee", "remove", TEST_EMPLOYEE], obj=cli_state_with_user - ) - cli_state_with_user.sdk.detectionlists.high_risk_employee.remove.assert_called_once_with( - TEST_ID - ) - - -def test_remove_high_risk_employee_when_user_does_not_exist_exits_with_correct_message( - runner, cli_state_without_user -): - result = runner.invoke( - cli, ["high-risk-employee", "remove", TEST_EMPLOYEE], obj=cli_state_without_user - ) - assert result.exit_code == 1 - assert ( - f"User '{TEST_EMPLOYEE}' does not exist or you do not have permission to view them." - in result.output - ) - - -def test_bulk_add_employees_calls_expected_py42_methods(runner, cli_state): - add_user_cloud_alias = thread_safe_side_effect() - add_user_risk_tags = thread_safe_side_effect() - update_user_notes = thread_safe_side_effect() - hre_add_user = thread_safe_side_effect() - - cli_state.sdk.detectionlists.add_user_cloud_alias.side_effect = add_user_cloud_alias - cli_state.sdk.detectionlists.add_user_risk_tags.side_effect = add_user_risk_tags - cli_state.sdk.detectionlists.update_user_notes.side_effect = update_user_notes - cli_state.sdk.detectionlists.high_risk_employee.add.side_effect = hre_add_user - - with runner.isolated_filesystem(): - with open("test_add.csv", "w") as csv: - csv.writelines( - [ - "username,cloud_alias,risk_tag,notes\n", - "test_user,test_alias,test_tag_1 test_tag_2,test_note\n", - "test_user_2,test_alias_2,test_tag_3,test_note_2\n", - "test_user_3,,,\n", - ] - ) - runner.invoke( - cli, ["high-risk-employee", "bulk", "add", "test_add.csv"], obj=cli_state - ) - alias_args = [call[1] for call in add_user_cloud_alias.call_args_list] - assert add_user_cloud_alias.call_count == 2 - assert "test_alias" in alias_args - assert "test_alias_2" in alias_args - - add_risk_tags_call_args = [call[1] for call in add_user_risk_tags.call_args_list] - assert add_user_risk_tags.call_count == 2 - assert ["test_tag_1", "test_tag_2"] in add_risk_tags_call_args - assert ["test_tag_3"] in add_risk_tags_call_args - - add_notes_call_args = [call[1] for call in update_user_notes.call_args_list] - assert update_user_notes.call_count == 2 - assert "test_note" in add_notes_call_args - assert "test_note_2" in add_notes_call_args - - assert hre_add_user.call_count == 3 - - -def test_bulk_remove_employees_uses_expected_arguments(runner, cli_state, mocker): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines(["username\n", "test@example.com\n", "test2@example.com"]) - runner.invoke( - cli, - ["high-risk-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com"}, - {"username": "test2@example.com"}, - ] - - -def test_bulk_remove_employees_uses_expected_arguments_when_extra_columns( - runner, cli_state, mocker -): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines( - [ - "username,test_column\n", - "test@example.com,test_value1\n", - "test2@example.com,test_value2\n", - ] - ) - runner.invoke( - cli, - ["high-risk-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com"}, - {"username": "test2@example.com"}, - ] - - -def test_bulk_remove_employees_uses_expected_arguments_when_flat_file( - runner, cli_state, mocker -): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.txt", "w") as csv: - csv.writelines(["# username\n", "test@example.com\n", "test2@example.com"]) - runner.invoke( - cli, - ["high-risk-employee", "bulk", "remove", "test_remove.txt"], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com"}, - {"username": "test2@example.com"}, - ] - - -def test_bulk_remove_employees_uses_expected_arguments_when_no_header( - runner, cli_state, mocker -): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove.csv", "w") as csv: - csv.writelines(["test@example.com\n", "test2@example.com"]) - runner.invoke( - cli, - ["high-risk-employee", "bulk", "remove", "test_remove.csv"], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com"}, - {"username": "test2@example.com"}, - ] - - -def test_bulk_add_risk_tags_uses_expected_arguments(runner, cli_state, mocker): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_add_risk_tags.csv", "w") as csv: - csv.writelines( - ["username,tag\n", "test@example.com,tag1\n", "test2@example.com,tag2"] - ) - runner.invoke( - cli, - ["high-risk-employee", "bulk", "add-risk-tags", "test_add_risk_tags.csv"], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com", "tag": "tag1"}, - {"username": "test2@example.com", "tag": "tag2"}, - ] - - -def test_bulk_remove_risk_tags_uses_expected_arguments(runner, cli_state, mocker): - bulk_processor = mocker.patch(f"{_NAMESPACE}.run_bulk_process") - with runner.isolated_filesystem(): - with open("test_remove_risk_tags.csv", "w") as csv: - csv.writelines( - ["username,tag\n", "test@example.com,tag1\n", "test2@example.com,tag2"] - ) - runner.invoke( - cli, - [ - "high-risk-employee", - "bulk", - "remove-risk-tags", - "test_remove_risk_tags.csv", - ], - obj=cli_state, - ) - assert bulk_processor.call_args[0][1] == [ - {"username": "test@example.com", "tag": "tag1"}, - {"username": "test2@example.com", "tag": "tag2"}, - ] - - -def test_remove_high_risk_employee_when_user_not_on_list_prints_expected_error( - mocker, runner, cli_state -): - cli_state.sdk.detectionlists.high_risk_employee.remove.side_effect = ( - get_user_not_on_list_side_effect(mocker, "high-risk-employee") - ) - test_username = "test@example.com" - result = runner.invoke( - cli, ["high-risk-employee", "remove", test_username], obj=cli_state - ) - assert ( - f"User with ID '{TEST_ID}' is not currently on the high-risk-employee list." - in result.output - ) - - -@pytest.mark.parametrize( - "command, error_msg", - [ - (f"{HR_EMPLOYEE_COMMAND} add", "Missing argument 'USERNAME'."), - (f"{HR_EMPLOYEE_COMMAND} remove", "Missing argument 'USERNAME'."), - (f"{HR_EMPLOYEE_COMMAND} bulk add", "Missing argument 'CSV_FILE'."), - (f"{HR_EMPLOYEE_COMMAND} bulk remove", "Missing argument 'CSV_FILE'."), - (f"{HR_EMPLOYEE_COMMAND} bulk add-risk-tags", "Missing argument 'CSV_FILE'."), - ( - f"{HR_EMPLOYEE_COMMAND} bulk remove-risk-tags", - "Missing argument 'CSV_FILE'.", - ), - ], -) -def test_hr_employee_command_when_missing_required_parameters_returns_error( - command, error_msg, runner, cli_state -): - result = runner.invoke(cli, command.split(" "), obj=cli_state) - assert result.exit_code == 2 - assert error_msg in "".join(result.output) diff --git a/tests/integration/test_departing_employee.py b/tests/integration/test_departing_employee.py deleted file mode 100644 index f3dca5be..00000000 --- a/tests/integration/test_departing_employee.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest -from tests.integration.conftest import append_profile -from tests.integration.util import assert_test_is_successful - - -@pytest.mark.integration -def test_departing_employee_list_command_returns_success_return_code( - runner, integration_test_profile -): - command = "departing-employee list" - assert_test_is_successful(runner, append_profile(command)) diff --git a/tests/integration/test_high_risk_employee.py b/tests/integration/test_high_risk_employee.py deleted file mode 100644 index 26aeb550..00000000 --- a/tests/integration/test_high_risk_employee.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest -from tests.integration.conftest import append_profile -from tests.integration.util import assert_test_is_successful - - -@pytest.mark.integration -def test_high_risk_employee_list_command_returns_success_return_code( - runner, integration_test_profile -): - command = "high-risk-employee list" - assert_test_is_successful(runner, append_profile(command))