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

Feat: add confirmation for destructive operations #13

Merged
merged 4 commits into from
Mar 16, 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
12 changes: 10 additions & 2 deletions compiler_admin/commands/convert.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from argparse import Namespace

from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
from compiler_admin.services.google import (
GROUP_PARTNERS,
Expand All @@ -18,7 +20,7 @@
ACCOUNT_TYPE_OU = {"contractor": OU_CONTRACTORS, "partner": OU_PARTNERS, "staff": OU_STAFF}


def convert(username: str, account_type: str) -> int:
def convert(args: Namespace) -> int:
f"""Convert a user of one type to another.
Args:
username (str): The account to convert. Must exist already.
Expand All @@ -27,7 +29,13 @@ def convert(username: str, account_type: str) -> int:
Returns:
A value indicating if the operation succeeded or failed.
"""
account = user_account_name(username)
if not hasattr(args, "username"):
raise ValueError("username is required")
if not hasattr(args, "account_type"):
raise ValueError("account_type is required")

account = user_account_name(args.username)
account_type = args.account_type

if not user_exists(account):
print(f"User does not exist: {account}")
Expand Down
10 changes: 7 additions & 3 deletions compiler_admin/commands/create.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from argparse import Namespace
from typing import Sequence

from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
from compiler_admin.services.google import GROUP_TEAM, add_user_to_group, CallGAMCommand, user_account_name, user_exists


def create(username: str, *args: Sequence[str]) -> int:
def create(args: Namespace, *extra: Sequence[str]) -> int:
"""Create a new user account in Compiler.

The user's password is randomly generated and requires reset on first login.
Expand All @@ -18,15 +19,18 @@ def create(username: str, *args: Sequence[str]) -> int:
Returns:
A value indicating if the operation succeeded or failed.
"""
account = user_account_name(username)
if not hasattr(args, "username"):
raise ValueError("username is required")

account = user_account_name(args.username)

if user_exists(account):
print(f"User already exists: {account}")
return RESULT_FAILURE

print(f"User does not exist, continuing: {account}")

res = CallGAMCommand(("create", "user", account, "password", "random", "changepassword", *args))
res = CallGAMCommand(("create", "user", account, "password", "random", "changepassword", *extra))

res += add_user_to_group(account, GROUP_TEAM)

Expand Down
15 changes: 13 additions & 2 deletions compiler_admin/commands/delete.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
from argparse import Namespace

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


def delete(username: str) -> int:
def delete(args: Namespace) -> int:
"""Delete the user account.

Args:
username (str): The account to delete. Must exist and not be an alias.
Returns:
A value indicating if the operation succeeded or failed.
"""
account = user_account_name(username)
if not hasattr(args, "username"):
raise ValueError("username is required")

account = user_account_name(args.username)

if not user_exists(account):
print(f"User does not exist: {account}")
return RESULT_FAILURE

if getattr(args, "force", False) is False:
cont = input(f"Delete account {account}? (Y/n)")
if not cont.lower().startswith("y"):
print("Aborting delete.")
return RESULT_SUCCESS

print(f"User exists, deleting: {account}")

res = CallGAMCommand(("delete", "user", account, "noactionifalias"))
Expand Down
11 changes: 8 additions & 3 deletions compiler_admin/commands/init.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from argparse import Namespace
import os
from pathlib import Path
from shutil import rmtree
Expand All @@ -21,7 +22,7 @@ def _clean_config_dir(config_dir: Path) -> None:
rmtree(path)


def init(admin_user: str, gam: bool = False, gyb: bool = False) -> int:
def init(args: Namespace) -> int:
"""Initialize a new GAM project.

See https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM
Expand All @@ -36,17 +37,21 @@ def init(admin_user: str, gam: bool = False, gyb: bool = False) -> int:
Returns:
A value indicating if the operation succeeded or failed.
"""
if not hasattr(args, "admin_user"):
raise ValueError("admin_user is required")

admin_user = args.admin_user
res = RESULT_SUCCESS

if gam:
if getattr(args, "gam", False):
_clean_config_dir(GAM_CONFIG_PATH)
# GAM is already installed via pyproject.toml
res += CallGAMCommand(("config", "drive_dir", str(GAM_CONFIG_PATH), "verify"))
res += CallGAMCommand(("create", "project"))
res += CallGAMCommand(("oauth", "create"))
res += CallGAMCommand(("user", admin_user, "check", "serviceaccount"))

if gyb:
if getattr(args, "gyb", False):
_clean_config_dir(GYB_CONFIG_PATH)
# download GYB installer to config directory
gyb = GYB_CONFIG_PATH / "gyb-install.sh"
Expand Down
14 changes: 13 additions & 1 deletion compiler_admin/commands/offboard.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from argparse import Namespace
from tempfile import NamedTemporaryFile

from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
Expand All @@ -12,7 +13,7 @@
)


def offboard(username: str, alias: str = None) -> int:
def offboard(args: Namespace) -> int:
"""Fully offboard a user from Compiler.

Args:
Expand All @@ -22,17 +23,28 @@ def offboard(username: str, alias: str = None) -> int:
Returns:
A value indicating if the operation succeeded or failed.
"""
if not hasattr(args, "username"):
raise ValueError("username is required")

username = args.username
account = user_account_name(username)

if not user_exists(account):
print(f"User does not exist: {account}")
return RESULT_FAILURE

alias = getattr(args, "alias", None)
alias_account = user_account_name(alias)
if alias_account is not None and not user_exists(alias_account):
print(f"Alias target user does not exist: {alias_account}")
return RESULT_FAILURE

if getattr(args, "force", False) is False:
cont = input(f"Offboard account {account} {' (assigning alias to '+ alias_account +')' if alias else ''}? (Y/n)")
if not cont.lower().startswith("y"):
print("Aborting offboard.")
return RESULT_SUCCESS

print(f"User exists, offboarding: {account}")
res = RESULT_SUCCESS

Expand Down
8 changes: 6 additions & 2 deletions compiler_admin/commands/restore.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from argparse import Namespace
import pathlib

from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
from compiler_admin.services.google import USER_ARCHIVE, CallGYBCommand, user_account_name


def restore(username: str) -> int:
def restore(args: Namespace) -> int:
"""Restore an email backup from a prior offboarding.

Args:
username (str): the user account with a local email backup to restore.
Returns:
A value indicating if the operation succeeded or failed.
"""
account = user_account_name(username)
if not hasattr(args, "username"):
raise ValueError("username is required")

account = user_account_name(args.username)
backup_dir = f"GYB-GMail-Backup-{account}"

if not pathlib.Path(backup_dir).exists():
Expand Down
15 changes: 13 additions & 2 deletions compiler_admin/commands/signout.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
from argparse import Namespace

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


def signout(username: str) -> int:
def signout(args: Namespace) -> int:
"""Signs the user out from all active sessions.

Args:
username (str): The account to sign out. Must exist already.
Returns:
A value indicating if the operation succeeded or failed.
"""
account = user_account_name(username)
if not hasattr(args, "username"):
raise ValueError("username is required")

account = user_account_name(args.username)

if not user_exists(account):
print(f"User does not exist: {account}")
return RESULT_FAILURE

if getattr(args, "force", False) is False:
cont = input(f"Signout account {account} from all active sessions? (Y/n)")
if not cont.lower().startswith("y"):
print("Aborting signout.")
return RESULT_SUCCESS

print(f"User exists, signing out from all active sessions: {account}")

res = CallGAMCommand(("user", account, "signout"))
Expand Down
27 changes: 18 additions & 9 deletions compiler_admin/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,23 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser:
"account_type", choices=ACCOUNT_TYPE_OU.keys(), help="Target account type for this conversion."
)

_subcmd("delete", help="Delete a user account.")
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."
)

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(
"--force", action="store_true", default=False, help="Don't ask for confirmation before offboarding."
)

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

_subcmd("signout", help="Signs a user out from all active sessions.")
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."
)

if len(argv) == 0:
argv = ["info"]
Expand All @@ -66,19 +75,19 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser:
if args.command == "info":
return info()
elif args.command == "create":
return create(args.username, *extra)
return create(args, *extra)
elif args.command == "convert":
return convert(args.username, args.account_type)
return convert(args)
elif args.command == "delete":
return delete(args.username)
return delete(args)
elif args.command == "init":
return init(args.username, gam=args.gam, gyb=args.gyb)
return init(args)
elif args.command == "offboard":
return offboard(args.username, args.alias)
return offboard(args)
elif args.command == "restore":
return restore(args.username)
return restore(args)
elif args.command == "signout":
return signout(args.username)
return signout(args)


if __name__ == "__main__":
Expand Down
Loading
Loading