Skip to content

Commit 2546ac0

Browse files
implement user alias management (#355)
* implement user alias management * add exception handling for too long alias and for nonexistent user * update error handling to correctly report on too many aliases, and also to prevent adding an alias that is too long. * adding cmd 'list-aliases' and moving error handling into py42 * add empty list test for list-aliases Co-authored-by: Tora Kozic <tora.kozic@code42.com>
1 parent 9bbb119 commit 2546ac0

File tree

5 files changed

+446
-1
lines changed

5 files changed

+446
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
The intended audience of this file is for py42 consumers -- as such, changes that don't affect
99
how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here.
1010

11+
## Unreleased
12+
13+
### Added
14+
- `users add-alias`, `users remove-alias`, `users bulk add-alias`, and `users bulk remove-alias` for managing cloud aliases for users.
15+
1116
## 1.12.1 - 2022-01-21
1217

1318
### Fixed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"keyrings.alt==3.2.0",
4040
"ipython==7.16.3",
4141
"pandas>=1.1.3",
42-
"py42>=1.19.3",
42+
"py42>=1.21.1",
4343
],
4444
extras_require={
4545
"dev": [

src/code42cli/click_ext/groups.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from py42.exceptions import Py42ActiveLegalHoldError
88
from py42.exceptions import Py42CaseAlreadyHasEventError
99
from py42.exceptions import Py42CaseNameExistsError
10+
from py42.exceptions import Py42CloudAliasCharacterLimitExceededError
11+
from py42.exceptions import Py42CloudAliasLimitExceededError
1012
from py42.exceptions import Py42DescriptionLimitExceededError
1113
from py42.exceptions import Py42ForbiddenError
1214
from py42.exceptions import Py42HTTPError
@@ -86,6 +88,8 @@ def invoke(self, ctx):
8688
Py42TrustedActivityConflictError,
8789
Py42TrustedActivityInvalidCharacterError,
8890
Py42TrustedActivityIdNotFound,
91+
Py42CloudAliasLimitExceededError,
92+
Py42CloudAliasCharacterLimitExceededError,
8993
) as err:
9094
msg = err.args[0]
9195
self.logger.log_error(msg)

src/code42cli/cmds/users.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import click
44
from pandas import DataFrame
55
from pandas import json_normalize
6+
from py42.exceptions import Py42BadRequestError
67
from py42.exceptions import Py42NotFoundError
78

89
from code42cli.bulk import generate_template_cmd_factory
@@ -215,6 +216,8 @@ def reactivate(state, username):
215216

216217
_bulk_user_roles_headers = ["username", "role_name"]
217218

219+
_bulk_user_alias_headers = ["username", "alias"]
220+
218221

219222
@users.command(name="move")
220223
@username_option("The username of the user to move.", required=True)
@@ -226,6 +229,37 @@ def change_organization(state, username, org_id):
226229
_change_organization(state.sdk, username, org_id)
227230

228231

232+
@users.command()
233+
@click.argument("username")
234+
@click.argument("alias")
235+
@sdk_options()
236+
def add_alias(state, username, alias):
237+
"""Add a cloud alias for a given user."""
238+
_add_cloud_alias(state.sdk, username, alias)
239+
240+
241+
@users.command()
242+
@click.argument("username")
243+
@click.argument("alias")
244+
@sdk_options()
245+
def remove_alias(state, username, alias):
246+
"""Remove a cloud alias for a given user."""
247+
_remove_cloud_alias(state.sdk, username, alias)
248+
249+
250+
@users.command()
251+
@click.argument("username")
252+
@sdk_options()
253+
def list_aliases(state, username):
254+
"""List the cloud aliases for a given user."""
255+
user = _get_user(state.sdk, username)
256+
aliases = user["cloudUsernames"]
257+
if aliases:
258+
click.echo(aliases)
259+
else:
260+
click.echo(f"No cloud aliases for user '{username}' found.")
261+
262+
229263
@users.group(cls=OrderedGroup)
230264
@sdk_options(hidden=True)
231265
def orgs(state):
@@ -292,6 +326,8 @@ def bulk(state):
292326
commands_dict={
293327
"update": _bulk_user_update_headers,
294328
"move": _bulk_user_move_headers,
329+
"add-alias": _bulk_user_alias_headers,
330+
"remove-alias": _bulk_user_alias_headers,
295331
},
296332
help_message="Generate the CSV template needed for bulk user commands.",
297333
)
@@ -539,6 +575,86 @@ def handle_row(**row):
539575
formatter.echo_formatted_list(result_rows)
540576

541577

578+
@bulk.command(
579+
name="add-alias",
580+
help=f"Add aliases to a list of users from the provided CSV in format: {','.join(_bulk_user_alias_headers)}",
581+
)
582+
@read_csv_arg(headers=_bulk_user_alias_headers)
583+
@format_option
584+
@sdk_options()
585+
def bulk_add_alias(state, csv_rows, format):
586+
"""Bulk add aliases to users"""
587+
588+
# Initialize the SDK before starting any bulk processes
589+
# to prevent multiple instances and having to enter 2fa multiple times.
590+
sdk = state.sdk
591+
success_header = "alias added"
592+
593+
csv_rows[0][success_header] = "False"
594+
formatter = OutputFormatter(format, {key: key for key in csv_rows[0].keys()})
595+
stats = create_worker_stats(len(csv_rows))
596+
597+
def handle_row(**row):
598+
try:
599+
_add_cloud_alias(
600+
sdk, **{key: row[key] for key in row.keys() if key != success_header}
601+
)
602+
row[success_header] = "True"
603+
except Exception as err:
604+
row[success_header] = f"False: {err}"
605+
stats.increment_total_errors()
606+
return row
607+
608+
result_rows = run_bulk_process(
609+
handle_row,
610+
csv_rows,
611+
progress_label="Adding aliases to users:",
612+
stats=stats,
613+
raise_global_error=False,
614+
)
615+
formatter.echo_formatted_list(result_rows)
616+
617+
618+
@bulk.command(
619+
name="remove-alias",
620+
help=f"Remove aliases from a list of users from the provided CSV in format: {','.join(_bulk_user_alias_headers)}",
621+
)
622+
@read_csv_arg(headers=_bulk_user_alias_headers)
623+
@format_option
624+
@sdk_options()
625+
def bulk_remove_alias(state, csv_rows, format):
626+
"""Bulk remove aliases from users"""
627+
628+
# Initialize the SDK before starting any bulk processes
629+
# to prevent multiple instances and having to enter 2fa multiple times.
630+
sdk = state.sdk
631+
success_header = "alias removed"
632+
633+
csv_rows[0][success_header] = "False"
634+
formatter = OutputFormatter(format, {key: key for key in csv_rows[0].keys()})
635+
stats = create_worker_stats(len(csv_rows))
636+
637+
def handle_row(**row):
638+
try:
639+
_remove_cloud_alias(
640+
sdk, **{key: row[key] for key in row.keys() if key != success_header}
641+
)
642+
row[success_header] = "True"
643+
except Exception as err:
644+
row[success_header] = f"False: {err}"
645+
stats.increment_total_errors()
646+
return row
647+
648+
result_rows = run_bulk_process(
649+
handle_row,
650+
csv_rows,
651+
progress_label="Removing aliases from users:",
652+
stats=stats,
653+
raise_global_error=False,
654+
)
655+
formatter.echo_formatted_list(result_rows)
656+
657+
542658
def _add_user_role(sdk, username, role_name):
543659
user_id = _get_legacy_user_id(sdk, username)
544660
_get_role_id(sdk, role_name) # function provides role name validation
@@ -666,3 +782,21 @@ def _deactivate_user(sdk, username):
666782
def _reactivate_user(sdk, username):
667783
user_id = _get_legacy_user_id(sdk, username)
668784
sdk.users.reactivate(user_id)
785+
786+
787+
def _get_user(sdk, username):
788+
# use when retrieving the user information from the detectionlists module
789+
try:
790+
return sdk.detectionlists.get_user(username).data
791+
except Py42BadRequestError:
792+
raise UserDoesNotExistError(username)
793+
794+
795+
def _add_cloud_alias(sdk, username, alias):
796+
user = _get_user(sdk, username)
797+
sdk.detectionlists.add_user_cloud_alias(user["userId"], alias)
798+
799+
800+
def _remove_cloud_alias(sdk, username, alias):
801+
user = _get_user(sdk, username)
802+
sdk.detectionlists.remove_user_cloud_alias(user["userId"], alias)

0 commit comments

Comments
 (0)