Skip to content
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

Refactor: organize user commands #18

Merged
merged 6 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 42 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,17 @@ Built on top of [GAMADV-XTD3](https://github.com/taers232c/GAMADV-XTD3) and [GYB

```bash
$ compiler-admin -h
usage: compiler-admin [-h] [-v] {info,init,create,convert,delete,offboard,reset-password,restore,signout} ...
usage: compiler-admin [-h] [-v] {info,init,user} ...

positional arguments:
{info,init,create,convert,delete,offboard,reset-password,restore,signout}
info Print configuration and debugging information.
init Initialize a new admin project. This command should be run once before any others.
create Create a new user in the Compiler domain.
convert Convert a user account to a new type.
delete Delete a user account.
offboard Offboard a user account.
reset-password Reset a user's password to a randomly generated string.
restore Restore an email backup from a prior offboarding.
signout Signs a user out from all active sessions.
{info,init,user} The command to run
info Print configuration and debugging information.
init Initialize a new admin project. This command should be run once before any others.
user Work with users in the Compiler org.

options:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-h, --help show this help message and exit
-v, --version show program's version number and exit
```

## Getting started
Expand Down Expand Up @@ -60,11 +54,34 @@ The `init` commands follows the steps in the [GAMADV-XTD3 Wiki](https://github.c

Additionally, GYB is used for Gmail backup/restore. See the [GYB Wiki](https://github.com/GAM-team/got-your-back/wiki) for more information.

## Creating a user
## Working with users

The following commands are available to work with users in the Compiler domain:

```bash
$ compiler-admin user -h
usage: compiler-admin user [-h] {create,convert,delete,offboard,reset-password,restore,signout} ...

positional arguments:
{create,convert,delete,offboard,reset-password,restore,signout}
The user command to run.
create Create a new user in the Compiler domain.
convert Convert a user account to a new type.
delete Delete a user account.
offboard Offboard a user account.
reset-password Reset a user's password to a randomly generated string.
restore Restore an email backup from a prior offboarding.
signout Signs a user out from all active sessions.

options:
-h, --help show this help message and exit
```

### Creating a user

```bash
$ compiler-admin create -h
usage: compiler-admin create [-h] [--notify NOTIFY] username
$ compiler-admin user create -h
usage: compiler-admin user create [-h] [--notify NOTIFY] username

positional arguments:
username A Compiler user account name, sans domain.
Expand All @@ -76,11 +93,11 @@ options:

Additional options are passed through to GAM, see more about [GAM user create](https://github.com/taers232c/GAMADV-XTD3/wiki/Users#create-a-user)

## Convert a user
### Convert a user

```bash
$ compiler-admin convert -h
usage: compiler-admin convert [-h] username {contractor,partner,staff}
$ compiler-admin user convert -h
usage: compiler-admin user convert [-h] username {contractor,partner,staff}

positional arguments:
username A Compiler user account name, sans domain.
Expand All @@ -91,11 +108,11 @@ options:
-h, --help show this help message and exit
```

## Offboarding a user
### Offboarding a user

```bash
$ compiler-admin offboard -h
usage: compiler-admin offboard [-h] [--alias ALIAS] [--force] username
$ compiler-admin user offboard -h
usage: compiler-admin user offboard [-h] [--alias ALIAS] [--force] username

positional arguments:
username A Compiler user account name, sans domain.
Expand All @@ -108,13 +125,13 @@ options:

This script creates a local backup of `USER`'s inbox, see [Restore](#restore-an-email-backup)

## Restore an email backup
### Restore an email backup

Retore a backup from a prior [Offboarding](#offboarding-a-user) into the `[email protected]` account.

```bash
$ compiler-admin restore -h
usage: compiler-admin restore [-h] username
$ compiler-admin user restore -h
usage: compiler-admin user restore [-h] username

positional arguments:
username The user's account name, sans domain.
Expand Down
2 changes: 1 addition & 1 deletion compiler_admin/commands/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from compiler_admin.services.google import CallGAMCommand, CallGYBCommand


def info() -> int:
def info(*args, **kwargs) -> int:
"""Print information about this package and the GAM environment.

Returns:
Expand Down
2 changes: 1 addition & 1 deletion compiler_admin/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def _clean_config_dir(config_dir: Path) -> None:
rmtree(path)


def init(args: Namespace) -> int:
def init(args: Namespace, *extras) -> int:
"""Initialize a new GAM project.

See https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM
Expand Down
18 changes: 18 additions & 0 deletions compiler_admin/commands/user/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from argparse import Namespace

from compiler_admin.commands.user.create import create # noqa: F401
from compiler_admin.commands.user.convert import convert # noqa: F401
from compiler_admin.commands.user.delete import delete # noqa: F401
from compiler_admin.commands.user.offboard import offboard # noqa: F401
from compiler_admin.commands.user.reset_password import reset_password # noqa: F401
from compiler_admin.commands.user.restore import restore # noqa: F401
from compiler_admin.commands.user.signout import signout # noqa: F401


def user(args: Namespace, *extra):
# try to call the subcommand function directly from local symbols
# if the subcommand function was imported above, it should exist in locals()
if args.subcommand in locals():
locals()[args.subcommand](args, *extra)
else:
raise ValueError(f"Unknown user subcommand: {args.subcommand}")
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from tempfile import NamedTemporaryFile

from compiler_admin import RESULT_SUCCESS, RESULT_FAILURE
from compiler_admin.commands.delete import delete
from compiler_admin.commands.signout import signout
from compiler_admin.commands.user.delete import delete
from compiler_admin.commands.user.signout import signout
from compiler_admin.services.google import (
USER_ARCHIVE,
CallGAMCommand,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from argparse import Namespace

from compiler_admin import RESULT_SUCCESS, RESULT_FAILURE
from compiler_admin.commands.signout import signout
from compiler_admin.commands.user.signout import signout
from compiler_admin.services.google import USER_HELLO, CallGAMCommand, user_account_name, user_exists


Expand Down
112 changes: 51 additions & 61 deletions compiler_admin/main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import argparse
from argparse import ArgumentParser, _SubParsersAction
import sys

from compiler_admin import __version__ as version
from compiler_admin.commands.create import create
from compiler_admin.commands.convert import ACCOUNT_TYPE_OU, convert
from compiler_admin.commands.delete import delete
from compiler_admin.commands.info import info
from compiler_admin.commands.init import init
from compiler_admin.commands.offboard import offboard
from compiler_admin.commands.reset_password import reset_password
from compiler_admin.commands.restore import restore
from compiler_admin.commands.signout import signout
from compiler_admin.commands.user import user
from compiler_admin.commands.user.convert import ACCOUNT_TYPE_OU


def add_sub_cmd(cmd: _SubParsersAction, subcmd, help) -> ArgumentParser:
"""Helper creates a new subcommand parser."""
return cmd.add_parser(subcmd, help=help)


def add_sub_cmd_username(cmd: _SubParsersAction, subcmd, help) -> ArgumentParser:
"""Helper creates a new subcommand parser with a required username arg."""
return add_username_arg(add_sub_cmd(cmd, subcmd, help=help))


def add_username_arg(cmd: ArgumentParser) -> ArgumentParser:
cmd.add_argument("username", help="A Compiler user account name, sans domain.")
return cmd


def main(argv=None):
argv = argv if argv is not None else sys.argv[1:]
parser = argparse.ArgumentParser(prog="compiler-admin")
parser = ArgumentParser(prog="compiler-admin")

# https://stackoverflow.com/a/8521644/812183
parser.add_argument(
Expand All @@ -25,76 +35,56 @@ def main(argv=None):
version=f"%(prog)s {version}",
)

subparsers = parser.add_subparsers(dest="command")
cmd_parsers = parser.add_subparsers(dest="command", help="The command to run")

def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser:
"""Helper creates a new subcommand parser."""
parser = subparsers.add_parser(name, help=help)
if add_username_arg is True:
parser.add_argument("username", help="A Compiler user account name, sans domain.")
return parser
info_cmd = add_sub_cmd(cmd_parsers, "info", help="Print configuration and debugging information.")
info_cmd.set_defaults(func=info)

_subcmd("info", help="Print configuration and debugging information.", add_username_arg=False)

init_parser = _subcmd(
"init",
help="Initialize a new admin project. This command should be run once before any others.",
init_cmd = add_sub_cmd_username(
cmd_parsers, "init", help="Initialize a new admin project. This command should be run once before any others."
)
init_parser.add_argument("--gam", action="store_true", help="If provided, initialize a new GAM project.")
init_parser.add_argument("--gyb", action="store_true", help="If provided, initialize a new GYB project.")
init_cmd.add_argument("--gam", action="store_true", help="If provided, initialize a new GAM project.")
init_cmd.add_argument("--gyb", action="store_true", help="If provided, initialize a new GYB project.")
init_cmd.set_defaults(func=init)

create_parser = _subcmd("create", help="Create a new user in the Compiler domain.")
create_parser.add_argument("--notify", help="An email address to send the newly created account info.")
user_cmd = add_sub_cmd(cmd_parsers, "user", help="Work with users in the Compiler org.")
user_cmd.set_defaults(func=user)
user_subcmds = user_cmd.add_subparsers(dest="subcommand", help="The user command to run.")

convert_parser = _subcmd("convert", help="Convert a user account to a new type.")
convert_parser.add_argument(
"account_type", choices=ACCOUNT_TYPE_OU.keys(), help="Target account type for this conversion."
)
user_create = add_sub_cmd_username(user_subcmds, "create", help="Create a new user in the Compiler domain.")
user_create.add_argument("--notify", help="An email address to send the newly created account info.")

delete_parser = _subcmd("delete", help="Delete a user account.")
delete_parser.add_argument(
"--force", action="store_true", default=False, help="Don't ask for confirmation before deletion."
)
user_convert = add_sub_cmd_username(user_subcmds, "convert", help="Convert a user account to a new type.")
user_convert.add_argument("account_type", choices=ACCOUNT_TYPE_OU.keys(), help="Target account type for this conversion.")

offboard_parser = _subcmd("offboard", help="Offboard a user account.")
offboard_parser.add_argument("--alias", help="Account to assign username as an alias.")
offboard_parser.add_argument(
user_delete = add_sub_cmd_username(user_subcmds, "delete", help="Delete a user account.")
user_delete.add_argument("--force", action="store_true", default=False, help="Don't ask for confirmation before deletion.")

user_offboard = add_sub_cmd_username(user_subcmds, "offboard", help="Offboard a user account.")
user_offboard.add_argument("--alias", help="Account to assign username as an alias.")
user_offboard.add_argument(
"--force", action="store_true", default=False, help="Don't ask for confirmation before offboarding."
)

reset_parser = _subcmd("reset-password", help="Reset a user's password to a randomly generated string.")
reset_parser.add_argument("--notify", help="An email address to send the newly generated password.")
user_reset = add_sub_cmd_username(
user_subcmds, "reset-password", help="Reset a user's password to a randomly generated string."
)
user_reset.add_argument("--notify", help="An email address to send the newly generated password.")

_subcmd("restore", help="Restore an email backup from a prior offboarding.")
add_sub_cmd_username(user_subcmds, "restore", help="Restore an email backup from a prior offboarding.")

signout_parser = _subcmd("signout", help="Signs a user out from all active sessions.")
signout_parser.add_argument(
"--force", action="store_true", default=False, help="Don't ask for confirmation before signout."
)
user_signout = add_sub_cmd_username(user_subcmds, "signout", help="Signs a user out from all active sessions.")
user_signout.add_argument("--force", action="store_true", default=False, help="Don't ask for confirmation before signout.")

if len(argv) == 0:
argv = ["info"]

args, extra = parser.parse_known_args(argv)

if args.command == "info":
return info()
elif args.command == "create":
return create(args, *extra)
elif args.command == "convert":
return convert(args)
elif args.command == "delete":
return delete(args)
elif args.command == "init":
return init(args)
elif args.command == "offboard":
return offboard(args)
elif args.command == "restore":
return restore(args)
elif args.command == "reset-password":
return reset_password(args)
elif args.command == "signout":
return signout(args)
if args.func:
return args.func(args, *extra)
else:
raise ValueError("Unrecognized command")


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions tests/commands/test_info.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from compiler_admin import __version__ as version, RESULT_SUCCESS
from compiler_admin import RESULT_SUCCESS
from compiler_admin.commands.info import info, __name__ as MODULE
from compiler_admin.services.google import DOMAIN

Expand Down Expand Up @@ -28,7 +28,7 @@ def test_info_e2e(capfd):
captured = capfd.readouterr()

assert res == RESULT_SUCCESS
assert f"compiler-admin: {version}" in captured.out
assert "compiler-admin:" in captured.out
assert "GAMADV-XTD3" in captured.out
assert f"Primary Domain: {DOMAIN}" in captured.out
assert "Got Your Back" in captured.out
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.convert import convert, __name__ as MODULE
from compiler_admin.commands.user.convert import convert, __name__ as MODULE


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.create import create, __name__ as MODULE
from compiler_admin.commands.user.create import create, __name__ as MODULE
from compiler_admin.services.google import USER_HELLO


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.delete import delete, __name__ as MODULE
from compiler_admin.commands.user.delete import delete, __name__ as MODULE


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.offboard import offboard, __name__ as MODULE
from compiler_admin.commands.user.offboard import offboard, __name__ as MODULE


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.reset_password import reset_password, __name__ as MODULE
from compiler_admin.commands.user.reset_password import reset_password, __name__ as MODULE
from compiler_admin.services.google import USER_HELLO


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
from compiler_admin.commands.restore import restore, __name__ as MODULE
from compiler_admin.commands.user.restore import restore, __name__ as MODULE


@pytest.fixture
Expand Down
Loading
Loading