Ejemplo n.º 1
0
def read_csv(file, headers):
    """Helper to read a csv file object into a list of dict rows.
    If CSV has a header row, all items in `headers` arg must be present in CSV or an
    error is raised. Any extra columns will get filtered out from resulting dicts.

    If no header row is present in CSV, column count must match `headers` arg length or
    else error is raised.
    """
    lines = file.readlines()
    first_line = lines[0].strip().split(",")

    # handle when first row has all of our expected headers
    if all(field in first_line for field in headers):
        reader = csv.DictReader(lines[1:], fieldnames=first_line)
        csv_rows = [{key: row[key] for key in headers} for row in reader]
        if not csv_rows:
            raise Code42CLIError("CSV contains no data rows.")
        return csv_rows

    # handle when first row has no expected headers
    elif all(field not in first_line for field in headers):
        #  only process header-less CSVs if we get exact expected column count
        if len(first_line) == len(headers):
            return list(csv.DictReader(lines, fieldnames=headers))
        else:
            raise Code42CLIError(
                "CSV data is ambiguous. Column count must match expected columns exactly when no "
                f"header row is present. Expected columns: {headers}")
    # handle when first row has some expected headers but not all
    else:
        missing = [field for field in headers if field not in first_line]
        raise Code42CLIError(f"Missing required columns in csv: {missing}")
Ejemplo n.º 2
0
def _validate_connection(authority_url, username, password, totp=None):
    try:
        return py42.sdk.from_local_account(authority_url, username, password, totp=totp)
    except SSLError as err:
        logger.log_error(err)
        raise LoggedCLIError(
            f"Problem connecting to {authority_url}, SSL certificate verification failed.\nUpdate profile with --disable-ssl-errors to bypass certificate checks (not recommended!)."
        )
    except ConnectionError as err:
        logger.log_error(err)
        raise LoggedCLIError(f"Problem connecting to {authority_url}.")
    except Py42UnauthorizedError as err:
        logger.log_error(err)
        if "LoginConfig: LOCAL_2FA" in str(err):
            if totp is None:
                totp = prompt(
                    "Multi-factor authentication required. Enter TOTP", type=TOTP()
                )
                return _validate_connection(authority_url, username, password, totp)
            else:
                raise Code42CLIError(
                    f"Invalid credentials or TOTP token for user {username}."
                )
        else:
            raise Code42CLIError(f"Invalid credentials for user {username}.")
    except Exception as err:
        logger.log_error(err)
        raise LoggedCLIError("Unknown problem validating connection.")
Ejemplo n.º 3
0
def _validate_connection(authority_url, username, password, totp=None):
    try:
        return py42.sdk.from_local_account(authority_url,
                                           username,
                                           password,
                                           totp=totp)
    except SSLError as err:
        logger.log_error(err)
        raise LoggedCLIError(
            f"Problem connecting to {authority_url}, SSL certificate verification failed.\nUpdate profile with --disable-ssl-errors to bypass certificate checks (not recommended!)."
        )
    except ConnectionError as err:
        logger.log_error(err)
        raise LoggedCLIError(f"Problem connecting to {authority_url}.")
    except Py42MFARequiredError:
        totp = prompt("Multi-factor authentication required. Enter TOTP",
                      type=int)
        return _validate_connection(authority_url, username, password, totp)
    except Py42UnauthorizedError as err:
        logger.log_error(err)
        if "INVALID_TIME_BASED_ONE_TIME_PASSWORD" in err.response.text:
            raise Code42CLIError(f"Invalid TOTP token for user {username}.")
        else:
            raise Code42CLIError(f"Invalid credentials for user {username}.")
    except Exception as err:
        logger.log_error(err)
        raise LoggedCLIError("Unknown problem validating connection.")
Ejemplo n.º 4
0
def validate_default_profile():
    if not default_profile_exists():
        existing_profiles = get_all_profiles()
        if not existing_profiles:
            raise Code42CLIError("No existing profile.", help=CREATE_PROFILE_HELP)
        else:
            raise Code42CLIError(
                "No default profile set.",
                help=_get_set_default_profile_help(existing_profiles),
            )
Ejemplo n.º 5
0
def _change_device_name(sdk, guid, name):
    try:
        device_settings = sdk.devices.get_settings(guid)
        device_settings.name = name
        sdk.devices.update_settings(device_settings)
    except exceptions.Py42ForbiddenError:
        raise Code42CLIError(
            f"You don't have the necessary permissions to rename the device with GUID '{guid}'."
        )
    except exceptions.Py42NotFoundError:
        raise Code42CLIError(f"The device with GUID '{guid}' was not found.")
Ejemplo n.º 6
0
 def handle_row(type, value, description):
     if type not in TrustedActivityType.choices():
         message = f"Invalid type {type}, valid types include {', '.join(TrustedActivityType.choices())}."
         raise Code42CLIError(message)
     if type is None:
         message = "'type' is a required field to create a trusted activity."
         raise Code42CLIError(message)
     if value is None:
         message = "'value' is a required field to create a trusted activity."
         raise Code42CLIError(message)
     sdk.trustedactivities.create(type, value, description)
Ejemplo n.º 7
0
def _change_device_activation(sdk, device_guid, cmd_str):
    try:
        device = sdk.devices.get_by_guid(device_guid)
        device_id = device.data["computerId"]
        if cmd_str == "reactivate":
            sdk.devices.reactivate(device_id)
        elif cmd_str == "deactivate":
            sdk.devices.deactivate(device_id)
        return device
    except exceptions.Py42NotFoundError:
        raise Code42CLIError(f"The device with GUID '{device_guid}' was not found.")
    except exceptions.Py42ForbiddenError:
        raise Code42CLIError(
            f"Unable to {cmd_str} the device with GUID '{device_guid}'."
        )
Ejemplo n.º 8
0
def test_reset_pw_if_credentials_invalid_password_not_saved(
    runner, user_agreement, mock_verify, mock_cliprofile_namespace
):
    mock_verify.side_effect = Code42CLIError("Invalid credentials for user")
    mock_cliprofile_namespace.profile_exists.return_value = False
    runner.invoke(cli, ["profile", "reset-pw"])
    assert not mock_cliprofile_namespace.set_password.call_count
Ejemplo n.º 9
0
def _list():
    """Show all existing stored profiles."""
    profiles = cliprofile.get_all_profiles()
    if not profiles:
        raise Code42CLIError("No existing profile.", help=CREATE_PROFILE_HELP)
    for c42profile in profiles:
        echo(str(c42profile))
Ejemplo n.º 10
0
def get_profile(profile_name=None):
    if profile_name is None:
        validate_default_profile()
    try:
        return _get_profile(profile_name)
    except NoConfigProfileError as ex:
        raise Code42CLIError(str(ex), help=CREATE_PROFILE_HELP)
Ejemplo n.º 11
0
    def get_formatted_output(self, dfs, columns=None, **kwargs):
        """
        Accepts a pandas DataFrame or list/generator of DataFrames and formats and yields
        the results line by line to the caller as a generator.

        Accepts an optional list of column names that filter columns in the yielded
        results.

        Any additional kwargs provided will be passed to the underlying format method
        if customizations are required.
        """
        if self.output_format == OutputFormat.TABLE:
            yield from self._iter_table(dfs, columns=columns, **kwargs)

        elif self.output_format == OutputFormat.CSV:
            yield from self._iter_csv(dfs, columns=columns, **kwargs)

        elif self.output_format == OutputFormat.JSON:
            kwargs = {"indent": 4, **kwargs}
            yield from self._iter_json(dfs, columns=columns, **kwargs)

        elif self.output_format == OutputFormat.RAW:
            yield from self._iter_json(dfs, columns=columns, **kwargs)

        else:
            raise Code42CLIError(
                f"DataFrameOutputFormatter received an invalid format: {self.output_format}"
            )
Ejemplo n.º 12
0
    def echo_formatted_dataframes(self,
                                  dfs,
                                  columns=None,
                                  force_pager=False,
                                  force_no_pager=False,
                                  **kwargs):
        """
        Accepts a pandas DataFrame or list/generator of DataFrames and formats and echos the
        result to stdout. If total lines > 10, results will be sent to pager. `force_pager`
        and `force_no_pager` can be set to override the pager logic based on line count.

        Accepts an optional list of column names that filter
        columns in the echoed results.

        Any additional kwargs provided will be passed to the underlying format method
        if customizations are required.
        """
        lines = self.get_formatted_output(dfs, columns=columns, **kwargs)
        try:
            # check for empty generator
            first = next(lines)
            lines = chain([first], lines)
        except StopIteration:
            click.echo("No results found.")
            return
        if force_pager and force_no_pager:
            raise Code42CLIError(
                "force_pager cannot be used with force_no_pager.")
        if force_pager:
            click.echo_via_pager(lines)
        elif force_no_pager:
            for line in lines:
                click.echo(line)
        else:
            self._echo_via_pager_if_over_threshold(lines)
Ejemplo n.º 13
0
def _verify_guid_type(device_guid):
    if device_guid is None:
        return
    try:
        int(device_guid)
        return device_guid
    except ValueError:
        raise Code42CLIError("Not a valid GUID.")
Ejemplo n.º 14
0
 def __init__(self, output_format, checkpoint_func=None):
     self.output_format = (output_format.upper()
                           if output_format else OutputFormat.TABLE)
     if self.output_format not in OutputFormat.choices():
         raise Code42CLIError(
             f"DataFrameOutputFormatter received an invalid format: {self.output_format}"
         )
     self.checkpoint_func = checkpoint_func or (lambda x: None)
Ejemplo n.º 15
0
def _get_role_id(sdk, role_name):
    try:
        roles_dataframe = DataFrame.from_records(
            sdk.users.get_available_roles().data, index="roleName")
        role_result = roles_dataframe.at[role_name, "roleId"]
        return str(role_result)  # extract the role ID from the series
    except KeyError:
        raise Code42CLIError(f"Role with name '{role_name}' not found.")
Ejemplo n.º 16
0
def remove_user(state, rule_id, username):
    """Remove a user from an alert rule."""
    try:
        _remove_user(state.sdk, rule_id, username)
    except Py42BadRequestError:
        raise Code42CLIError(
            "User {} is not currently assigned to rule-id {}.".format(
                username, rule_id))
Ejemplo n.º 17
0
 def delete(self, cursor_name):
     """Removes a single cursor from the store."""
     try:
         location = path.join(self._dir_path, cursor_name)
         os.remove(location)
     except FileNotFoundError:
         msg = f"No checkpoint named {cursor_name} exists for this profile."
         raise Code42CLIError(msg)
Ejemplo n.º 18
0
def remove(state, username):
    """Remove a user from the departing-employee detection list."""
    try:
        _remove_departing_employee(state.sdk, username)
    except Py42NotFoundError:
        raise Code42CLIError(
            "User {} is not currently on the departing-employee detection list."
            .format(username))
Ejemplo n.º 19
0
def add(state, case_number, event_id):
    """Associate a file event to a case, by event ID."""
    try:
        state.sdk.cases.file_events.add(case_number, event_id)
    except Py42UpdateClosedCaseError:
        raise
    except Py42CaseAlreadyHasEventError:
        raise
    except Py42BadRequestError:
        raise Code42CLIError("Invalid case-number or event-id.")
Ejemplo n.º 20
0
def remove(state, username):
    """Remove a user from the high risk employees detection list."""
    try:
        _remove_high_risk_employee(state.sdk, username)
    except Py42NotFoundError:
        raise Code42CLIError(
            "User {} is not currently on the high-risk-employee detection list.".format(
                username
            )
        )
Ejemplo n.º 21
0
def show(state, case_number, format, include_file_events):
    """Show case details."""
    formatter = OutputFormatter(format)
    try:
        response = state.sdk.cases.get(case_number)
        formatter.echo_formatted_list([response.data])
        if include_file_events:
            events = _get_file_events(state.sdk, case_number)
            _display_file_events(events)
    except Py42NotFoundError:
        raise Code42CLIError("Invalid case-number {}.".format(case_number))
Ejemplo n.º 22
0
def _get_rule_type_func(sdk, rule_type):
    if rule_type == AlertRuleTypes.EXFILTRATION:
        return sdk.alerts.rules.exfiltration.get
    elif rule_type == AlertRuleTypes.CLOUD_SHARE:
        return sdk.alerts.rules.cloudshare.get
    elif rule_type == AlertRuleTypes.FILE_TYPE_MISMATCH:
        return sdk.alerts.rules.filetypemismatch.get
    else:
        raise Code42CLIError(
            "Received an unknown rule type from server. You might need to update "
            "to a newer version of {}".format(PRODUCT_NAME))
Ejemplo n.º 23
0
def show_org(
    state,
    org_uid,
    format,
):
    """Show org details."""
    formatter = OutputFormatter(format)
    try:
        response = state.sdk.orgs.get_by_uid(org_uid)
        formatter.echo_formatted_list([response.data])
    except Py42NotFoundError:
        raise Code42CLIError(f"Invalid org UID {org_uid}.")
Ejemplo n.º 24
0
def _deactivate_device(sdk, device_guid, change_device_name, purge_date):
    device = sdk.devices.get_by_guid(device_guid)
    try:
        sdk.devices.deactivate(device.data["computerId"])
    except exceptions.Py42BadRequestError:
        raise Code42CLIError("The device {} is in legal hold.".format(device_guid))
    except exceptions.Py42NotFoundError:
        raise Code42CLIError("The device {} was not found.".format(device_guid))
    except exceptions.Py42ForbiddenError:
        raise Code42CLIError("Unable to deactivate {}.".format(device_guid))
    if purge_date:
        _update_cold_storage_purge_date(sdk, device_guid, purge_date)
    if change_device_name and not device.data["name"].startswith("deactivated_"):
        _change_device_name(
            sdk,
            device_guid,
            "deactivated_"
            + date.today().strftime("%Y-%m-%d")
            + "_"
            + device.data["name"],
        )
Ejemplo n.º 25
0
    def invoke(self, ctx):
        try:
            return super().invoke(ctx)

        except click.UsageError as err:
            self._suggest_cmd(err)

        except LoggedCLIError:
            raise

        except Code42CLIError as err:
            self.logger.log_error(str(err))
            raise

        except click.ClickException:
            raise

        except click.exceptions.Exit:
            raise

        except (
                UserDoesNotExistError,
                Py42UserAlreadyAddedError,
                Py42UserNotOnListError,
                Py42InvalidRuleOperationError,
                Py42LegalHoldNotFoundOrPermissionDeniedError,
                SyslogServerNetworkConnectionError,
                Py42CaseNameExistsError,
                Py42DescriptionLimitExceededError,
                Py42CaseAlreadyHasEventError,
                Py42UpdateClosedCaseError,
        ) as err:
            self.logger.log_error(err)
            raise Code42CLIError(str(err))

        except Py42ForbiddenError as err:
            self.logger.log_verbose_error(self._original_args,
                                          err.response.request)
            raise LoggedCLIError(
                "You do not have the necessary permissions to perform this task. "
                "Try using or creating a different profile.")

        except Py42HTTPError as err:
            self.logger.log_verbose_error(self._original_args,
                                          err.response.request)
            raise LoggedCLIError("Problem making request to server.")

        except OSError:
            raise

        except Exception:
            self.logger.log_verbose_error()
            raise LoggedCLIError("Unknown problem occurred.")
Ejemplo n.º 26
0
def validate_connection(authority_url, username, password):
    try:
        return py42.sdk.from_local_account(authority_url, username, password)
    except ConnectionError as err:
        logger.log_error(str(err))
        raise LoggedCLIError("Problem connecting to {}".format(authority_url))
    except Py42UnauthorizedError as err:
        logger.log_error(str(err))
        raise Code42CLIError("Invalid credentials for user {}".format(username))
    except Exception as err:
        logger.log_error(str(err))
        raise LoggedCLIError("Unknown problem validating connection.")
Ejemplo n.º 27
0
 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)
Ejemplo n.º 28
0
def file_events_list(state, case_number, format):
    """List all the file events associated with the case."""
    formatter = OutputFormatter(format, _get_events_header())
    try:
        response = state.sdk.cases.file_events.get_all(case_number)
    except Py42NotFoundError:
        raise Code42CLIError("Invalid case-number.")

    if not response["events"]:
        click.echo("No events found.")
    else:
        events = [event for event in response["events"]]
        formatter.echo_formatted_list(events)
Ejemplo n.º 29
0
def delete(profile_name):
    """Deletes a profile and its stored password (if any)."""
    try:
        cliprofile.get_profile(profile_name)
    except Code42CLIError:
        raise Code42CLIError(f"Profile '{profile_name}' does not exist.")
    message = (
        "\nDeleting this profile will also delete any stored passwords and checkpoints. "
        "Are you sure? (y/n): ")
    if cliprofile.is_default_profile(profile_name):
        message = f"\n'{profile_name}' is currently the default profile!\n{message}"
    if does_user_agree(message):
        cliprofile.delete_profile(profile_name)
        echo(f"Profile '{profile_name}' has been deleted.")
Ejemplo n.º 30
0
def _deactivate_device(sdk, device_guid, change_device_name, purge_date):
    try:
        device = _change_device_activation(sdk, device_guid, "deactivate")
    except exceptions.Py42BadRequestError:
        raise Code42CLIError(f"The device with GUID '{device_guid}' is in legal hold.")
    if purge_date:
        _update_cold_storage_purge_date(sdk, device_guid, purge_date)
    if change_device_name and not device.data["name"].startswith("deactivated_"):
        _change_device_name(
            sdk,
            device_guid,
            "deactivated_"
            + date.today().strftime("%Y-%m-%d")
            + "_"
            + device.data["name"],
        )