Skip to content

Users command: Functionality to add/remove roles + Users user guide #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 27, 2021
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ 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

### Added

- New command `code42 users add-role` to add a user role to a single user.

- New command `code42 users remove-role` to remove a user role from a single user.

## 1.6.1 - 2021-05-27

### Fixed
Expand Down
54 changes: 49 additions & 5 deletions src/code42cli/cmds/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from code42cli.click_ext.groups import OrderedGroup
from code42cli.click_ext.options import incompatible_with
from code42cli.errors import Code42CLIError
from code42cli.errors import UserDoesNotExistError
from code42cli.options import format_option
from code42cli.options import sdk_options
from code42cli.output_formats import DataFrameOutputFormatter
Expand All @@ -21,9 +22,6 @@ def users(state):
"--org-uid",
help="Limit users to only those in the organization you specify. Note that child orgs are included.",
)
role_name_option = click.option(
"--role-name", help="Limit results to only users having the specified role.",
)
active_option = click.option(
"--active", is_flag=True, help="Limits results to only active users.", default=None,
)
Expand All @@ -35,9 +33,17 @@ def users(state):
)


def role_name_option(help):
return click.option("--role-name", help=help)


def username_option(help):
return click.option("--username", help=help)


@users.command(name="list")
@org_uid_option
@role_name_option
@role_name_option("Limit results to only users having the specified role.")
@active_option
@inactive_option
@format_option
Expand All @@ -60,6 +66,44 @@ def list_users(state, org_uid, role_name, active, inactive, format):
formatter.echo_formatted_dataframe(df)


@users.command()
@username_option("Username of the target user.")
@role_name_option("Name of role to add.")
@sdk_options()
def add_role(state, username, role_name):
"""Add the specified role to the user with the specified username."""
_add_user_role(state.sdk, username, role_name)


@users.command()
@role_name_option("Name of role to remove.")
@username_option("Username of the target user.")
@sdk_options()
def remove_role(state, username, role_name):
"""Remove the specified role to the user with the specified username."""
_remove_user_role(state.sdk, role_name, username)


def _add_user_role(sdk, username, role_name):
user_id = _get_user_id(sdk, username)
_get_role_id(sdk, role_name) # function provides role name validation
sdk.users.add_role(user_id, role_name)


def _remove_user_role(sdk, role_name, username):
user_id = _get_user_id(sdk, username)
_get_role_id(sdk, role_name) # function provides role name validation
sdk.users.remove_role(user_id, role_name)


def _get_user_id(sdk, username):
user = sdk.users.get_by_username(username)["users"]
if len(user) == 0:
raise UserDoesNotExistError(username)
user_id = user[0]["userId"]
return user_id


def _get_role_id(sdk, role_name):
try:
roles_dataframe = DataFrame.from_records(
Expand All @@ -68,7 +112,7 @@ def _get_role_id(sdk, role_name):
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.")
raise Code42CLIError(f"Role with name '{role_name}' not found.")


def _get_users_dataframe(sdk, columns, org_uid, role_id, active):
Expand Down
110 changes: 110 additions & 0 deletions tests/cmds/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
}
]
}
TEST_EMPTY_USERS_RESPONSE = {"users": []}
TEST_USERNAME = TEST_USERS_RESPONSE["users"][0]["username"]
TEST_USER_ID = TEST_USERS_RESPONSE["users"][0]["userId"]
TEST_ROLE_NAME = TEST_ROLE_RETURN_DATA["data"][0]["roleName"]


def _create_py42_response(mocker, text):
Expand All @@ -56,6 +60,16 @@ def get_all_users_success(cli_state):
cli_state.sdk.users.get_all.return_value = get_all_users_generator()


@pytest.fixture
def get_user_id_success(cli_state):
cli_state.sdk.users.get_by_username.return_value = TEST_USERS_RESPONSE


@pytest.fixture
def get_user_id_failure(cli_state):
cli_state.sdk.users.get_by_username.return_value = TEST_EMPTY_USERS_RESPONSE


@pytest.fixture
def get_available_roles_success(cli_state, get_available_roles_response):
cli_state.sdk.users.get_available_roles.return_value = get_available_roles_response
Expand Down Expand Up @@ -153,3 +167,99 @@ def test_list_users_when_given_excluding_active_and_inactive_uses_active_equals_
cli_state.sdk.users.get_all.assert_called_once_with(
active=None, org_uid=None, role_id=None
)


def test_add_user_role_adds(
runner, cli_state, get_user_id_success, get_available_roles_success
):
command = [
"users",
"add-role",
"--username",
"[email protected]",
"--role-name",
"Customer Cloud Admin",
]
runner.invoke(cli, command, obj=cli_state)
cli_state.sdk.users.add_role.assert_called_once_with(TEST_USER_ID, TEST_ROLE_NAME)


def test_add_user_role_raises_error_when_role_does_not_exist(
runner, cli_state, get_user_id_success, get_available_roles_success
):
command = [
"users",
"add-role",
"--username",
"[email protected]",
"--role-name",
"test",
]
result = runner.invoke(cli, command, obj=cli_state)
assert result.exit_code == 1
assert "Role with name 'test' not found." in result.output


def test_add_user_role_raises_error_when_username_does_not_exist(
runner, cli_state, get_user_id_failure, get_available_roles_success
):
command = [
"users",
"add-role",
"--username",
"[email protected]",
"--role-name",
"Desktop User",
]
result = runner.invoke(cli, command, obj=cli_state)
assert result.exit_code == 1
assert "User '[email protected]' does not exist." in result.output


def test_remove_user_role_removes(
runner, cli_state, get_user_id_success, get_available_roles_success
):
command = [
"users",
"remove-role",
"--username",
"[email protected]",
"--role-name",
"Customer Cloud Admin",
]
runner.invoke(cli, command, obj=cli_state)
cli_state.sdk.users.remove_role.assert_called_once_with(
TEST_USER_ID, TEST_ROLE_NAME
)


def test_remove_user_role_raises_error_when_role_does_not_exist(
runner, cli_state, get_user_id_success, get_available_roles_success
):
command = [
"users",
"remove-role",
"--username",
"[email protected]",
"--role-name",
"test",
]
result = runner.invoke(cli, command, obj=cli_state)
assert result.exit_code == 1
assert "Role with name 'test' not found." in result.output


def test_remove_user_role_raises_error_when_username_does_not_exist(
runner, cli_state, get_user_id_failure, get_available_roles_success
):
command = [
"users",
"remove-role",
"--username",
"[email protected]",
"--role-name",
"Desktop User",
]
result = runner.invoke(cli, command, obj=cli_state)
assert result.exit_code == 1
assert "User '[email protected]' does not exist." in result.output