diff --git a/oks_cli/user.py b/oks_cli/user.py index ae67ef3..e19bdfd 100644 --- a/oks_cli/user.py +++ b/oks_cli/user.py @@ -1,169 +1,49 @@ import click -from datetime import datetime -import dateutil.parser -import human_readable import prettytable -import json +from prettytable import TableStyle -from nacl.public import PrivateKey, SealedBox -from nacl.encoding import Base64Encoder +from .utils import do_request, print_output, ctx_update, login_profile, profile_completer, \ + find_project_id_by_name, project_completer -from .utils import do_request, print_output, find_project_id_by_name, ctx_update, login_profile, profile_completer, project_completer, JSONClickException -# DEIFNE THE USER COMMAND GROUP -@click.group(help="EIM users related commands.") +@click.group(help="User related commands.") @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer) -@click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer) +@click.option("--profile", help="Configuration profile to use", shell_complete=profile_completer) @click.pass_context def user(ctx, project_name, profile): - """Group of commands related to project management.""" + """Group of commands related to user management.""" ctx_update(ctx, project_name, None, profile) -# LIST USERS -@user.command('list', help="List EIM users") -@click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json") + +@user.command('types', help="List available user types") @click.option('--project-name', '-p', required=False, help="Project Name", shell_complete=project_completer) -@click.option('--profile', help="Configuration profile to use") +@click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format") +@click.option('--plain', is_flag=True, help="Plain table format") +@click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer) @click.pass_context -def user_list(ctx, output, project_name, profile): - """List users""" +def user_types(ctx, project_name, output, plain, profile): + """Display available user types.""" project_name, _, profile = ctx_update(ctx, project_name, None, profile) login_profile(profile) project_id = find_project_id_by_name(project_name) - data = do_request("GET", f'projects/{project_id}/eim_users') + data = do_request("GET", f'projects/{project_id}/eim_users/types') if output: print_output(data, output) return - field_names = ["USER", "ACCESS KEY", "STATE", "CREATED", "EXPIRATION DATE"] table = prettytable.PrettyTable() - table.field_names = field_names - - for user in data: - access_keys = user.get("AccessKeys", []) - access_key = access_keys[0] if access_keys else {} - - - state = access_key.get("State", "N/A") - if state == 'ACTIVE': - state = click.style(state, fg='green') - elif state == "INACTIVE": - state = click.style(state, fg='red') - - - row = [ - user.get("UserName"), - access_key.get("AccessKeyId", "N/A"), - state - ] - - if "CreationDate" in access_key: - created_at = dateutil.parser.parse(access_key.get("CreationDate")) - now = datetime.now(tz=created_at.tzinfo) - row.append(human_readable.date_time(now - created_at)) - else: - row.append("N/A") + table.field_names = ["USER TYPE", "DESCRIPTION"] - if "ExpirationDate" in access_key: - exp_at = dateutil.parser.parse(access_key.get("ExpirationDate")) - now = datetime.now(tz=exp_at.tzinfo) - row.append(human_readable.date_time(now - exp_at)) - else: - row.append("N/A") + if plain: + table.set_style(TableStyle.PLAIN_COLUMNS) - table.add_row(row) + for entry in data: + table.add_row([ + entry.get("UserType", ""), + entry.get("Description") or "-" + ]) click.echo(table) - - -@user.command('create', help="Create a new EIM user") -@click.option('--project-name', '-p', help="Name of project", type=click.STRING, shell_complete=project_completer) -@click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format, by default is json") -@click.option('--profile', help="Configuration profile to use") -@click.option('--user', '-u', required=True, help="OKS User type") -@click.option('--ttl', type=click.STRING, help="TTL in human readable format (5h, 1d, 1w), by default is 1w") -@click.option('--nacl', is_flag=True, help="Use public key encryption on wire") -@click.pass_context -def user_create(ctx, project_name, output, profile, user, ttl, nacl): - """Create a new EIM user.""" - project_name, _, profile = ctx_update(ctx, project_name, None, profile) - login_profile(profile) - - project_id = find_project_id_by_name(project_name) - - params = { - "user": user - } - if ttl: - params["ttl"] = ttl - - if nacl: - ephemeral = PrivateKey.generate() - unsealbox = SealedBox(ephemeral) - - headers = { - 'x-encrypt-nacl': ephemeral.public_key.encode(Base64Encoder).decode('ascii') - } - - raw_data = do_request( - "POST", - f'projects/{project_id}/eim_users', - params=params, - headers=headers - ) - - decrypted = unsealbox.decrypt( - raw_data.get("Data").encode('ascii'), - encoder=Base64Encoder - ).decode('ascii') - - data = json.loads(decrypted) - - # format decrypted errors the same way as the api errors. - if "Errors" in data: - response_context = raw_data.get("ResponseContext") - errors = [] - for error in data.get("Errors", []): - error["Code"] = str(data.get("Code")) - errors.append(error) - - raise JSONClickException(json.dumps({"Errors": errors,"ResponseContext": response_context}, separators=(",", ":"))) - - else: - data = do_request( - "POST", - f'projects/{project_id}/eim_users', - params=params - ) - - print_output(data, output) - -# DELETE USER -@user.command('delete', help="Delete an EIM user") -@click.option('--project-name', '-p', required=False, help="Project name", shell_complete=project_completer) -@click.option('--user', '-u', required=True, help="User name") -@click.option('--output', '-o', type=click.Choice(["json", "yaml"]), help="Specify output format") -@click.option('--dry-run', is_flag=True, help="Run without any action") -@click.option('--force', is_flag=True, help="Force deletion without confirmation") -@click.option('--profile', help="Configuration profile to use", shell_complete=profile_completer) -@click.pass_context -def user_delete(ctx, project_name, user, output, dry_run, force, profile): - """CLI command to delete an EIM user.""" - - project_name, _, profile = ctx_update(ctx, project_name, None, profile) - login_profile(profile) - - project_id = find_project_id_by_name(project_name) - - if dry_run: - message = {"message": f"Dry run: The user '{user}' would be deleted."} - print_output(message, output) - return - - if force or click.confirm(f"Are you sure you want to delete the user '{user}'?", abort=True): - data = do_request("DELETE", f"projects/{project_id}/eim_users/{user}") - print_output(data, output) - diff --git a/oks_cli/utils.py b/oks_cli/utils.py index a0e719a..813966a 100644 --- a/oks_cli/utils.py +++ b/oks_cli/utils.py @@ -79,12 +79,8 @@ def find_response_object(data): return response["IP"] elif key == "Nets": return response["Nets"] - elif key == "EimUsers": - return response["EimUsers"] - elif key == "EimUser": - return response["EimUser"] - elif key == "Data": - return response + elif key == "EimUserTypes": + return response["EimUserTypes"] raise click.ClickException("The API response format is incorrect.")