Skip to content

Commit f6e10ca

Browse files
authored
Feat: add confirmation for destructive operations (#13)
2 parents 89dc9ed + 85df60e commit f6e10ca

17 files changed

+342
-69
lines changed

compiler_admin/commands/convert.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from argparse import Namespace
2+
13
from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
24
from compiler_admin.services.google import (
35
GROUP_PARTNERS,
@@ -18,7 +20,7 @@
1820
ACCOUNT_TYPE_OU = {"contractor": OU_CONTRACTORS, "partner": OU_PARTNERS, "staff": OU_STAFF}
1921

2022

21-
def convert(username: str, account_type: str) -> int:
23+
def convert(args: Namespace) -> int:
2224
f"""Convert a user of one type to another.
2325
Args:
2426
username (str): The account to convert. Must exist already.
@@ -27,7 +29,13 @@ def convert(username: str, account_type: str) -> int:
2729
Returns:
2830
A value indicating if the operation succeeded or failed.
2931
"""
30-
account = user_account_name(username)
32+
if not hasattr(args, "username"):
33+
raise ValueError("username is required")
34+
if not hasattr(args, "account_type"):
35+
raise ValueError("account_type is required")
36+
37+
account = user_account_name(args.username)
38+
account_type = args.account_type
3139

3240
if not user_exists(account):
3341
print(f"User does not exist: {account}")

compiler_admin/commands/create.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
from argparse import Namespace
12
from typing import Sequence
23

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

67

7-
def create(username: str, *args: Sequence[str]) -> int:
8+
def create(args: Namespace, *extra: Sequence[str]) -> int:
89
"""Create a new user account in Compiler.
910
1011
The user's password is randomly generated and requires reset on first login.
@@ -18,15 +19,18 @@ def create(username: str, *args: Sequence[str]) -> int:
1819
Returns:
1920
A value indicating if the operation succeeded or failed.
2021
"""
21-
account = user_account_name(username)
22+
if not hasattr(args, "username"):
23+
raise ValueError("username is required")
24+
25+
account = user_account_name(args.username)
2226

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

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

29-
res = CallGAMCommand(("create", "user", account, "password", "random", "changepassword", *args))
33+
res = CallGAMCommand(("create", "user", account, "password", "random", "changepassword", *extra))
3034

3135
res += add_user_to_group(account, GROUP_TEAM)
3236

compiler_admin/commands/delete.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
1+
from argparse import Namespace
2+
13
from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
24
from compiler_admin.services.google import CallGAMCommand, user_account_name, user_exists
35

46

5-
def delete(username: str) -> int:
7+
def delete(args: Namespace) -> int:
68
"""Delete the user account.
79
810
Args:
911
username (str): The account to delete. Must exist and not be an alias.
1012
Returns:
1113
A value indicating if the operation succeeded or failed.
1214
"""
13-
account = user_account_name(username)
15+
if not hasattr(args, "username"):
16+
raise ValueError("username is required")
17+
18+
account = user_account_name(args.username)
1419

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

24+
if getattr(args, "force", False) is False:
25+
cont = input(f"Delete account {account}? (Y/n)")
26+
if not cont.lower().startswith("y"):
27+
print("Aborting delete.")
28+
return RESULT_SUCCESS
29+
1930
print(f"User exists, deleting: {account}")
2031

2132
res = CallGAMCommand(("delete", "user", account, "noactionifalias"))

compiler_admin/commands/init.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from argparse import Namespace
12
import os
23
from pathlib import Path
34
from shutil import rmtree
@@ -21,7 +22,7 @@ def _clean_config_dir(config_dir: Path) -> None:
2122
rmtree(path)
2223

2324

24-
def init(admin_user: str, gam: bool = False, gyb: bool = False) -> int:
25+
def init(args: Namespace) -> int:
2526
"""Initialize a new GAM project.
2627
2728
See https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM
@@ -36,17 +37,21 @@ def init(admin_user: str, gam: bool = False, gyb: bool = False) -> int:
3637
Returns:
3738
A value indicating if the operation succeeded or failed.
3839
"""
40+
if not hasattr(args, "admin_user"):
41+
raise ValueError("admin_user is required")
42+
43+
admin_user = args.admin_user
3944
res = RESULT_SUCCESS
4045

41-
if gam:
46+
if getattr(args, "gam", False):
4247
_clean_config_dir(GAM_CONFIG_PATH)
4348
# GAM is already installed via pyproject.toml
4449
res += CallGAMCommand(("config", "drive_dir", str(GAM_CONFIG_PATH), "verify"))
4550
res += CallGAMCommand(("create", "project"))
4651
res += CallGAMCommand(("oauth", "create"))
4752
res += CallGAMCommand(("user", admin_user, "check", "serviceaccount"))
4853

49-
if gyb:
54+
if getattr(args, "gyb", False):
5055
_clean_config_dir(GYB_CONFIG_PATH)
5156
# download GYB installer to config directory
5257
gyb = GYB_CONFIG_PATH / "gyb-install.sh"

compiler_admin/commands/offboard.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from argparse import Namespace
12
from tempfile import NamedTemporaryFile
23

34
from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
@@ -12,7 +13,7 @@
1213
)
1314

1415

15-
def offboard(username: str, alias: str = None) -> int:
16+
def offboard(args: Namespace) -> int:
1617
"""Fully offboard a user from Compiler.
1718
1819
Args:
@@ -22,17 +23,28 @@ def offboard(username: str, alias: str = None) -> int:
2223
Returns:
2324
A value indicating if the operation succeeded or failed.
2425
"""
26+
if not hasattr(args, "username"):
27+
raise ValueError("username is required")
28+
29+
username = args.username
2530
account = user_account_name(username)
2631

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

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

42+
if getattr(args, "force", False) is False:
43+
cont = input(f"Offboard account {account} {' (assigning alias to '+ alias_account +')' if alias else ''}? (Y/n)")
44+
if not cont.lower().startswith("y"):
45+
print("Aborting offboard.")
46+
return RESULT_SUCCESS
47+
3648
print(f"User exists, offboarding: {account}")
3749
res = RESULT_SUCCESS
3850

compiler_admin/commands/restore.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1+
from argparse import Namespace
12
import pathlib
23

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

67

7-
def restore(username: str) -> int:
8+
def restore(args: Namespace) -> int:
89
"""Restore an email backup from a prior offboarding.
910
1011
Args:
1112
username (str): the user account with a local email backup to restore.
1213
Returns:
1314
A value indicating if the operation succeeded or failed.
1415
"""
15-
account = user_account_name(username)
16+
if not hasattr(args, "username"):
17+
raise ValueError("username is required")
18+
19+
account = user_account_name(args.username)
1620
backup_dir = f"GYB-GMail-Backup-{account}"
1721

1822
if not pathlib.Path(backup_dir).exists():

compiler_admin/commands/signout.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
1+
from argparse import Namespace
2+
13
from compiler_admin.commands import RESULT_SUCCESS, RESULT_FAILURE
24
from compiler_admin.services.google import CallGAMCommand, user_account_name, user_exists
35

46

5-
def signout(username: str) -> int:
7+
def signout(args: Namespace) -> int:
68
"""Signs the user out from all active sessions.
79
810
Args:
911
username (str): The account to sign out. Must exist already.
1012
Returns:
1113
A value indicating if the operation succeeded or failed.
1214
"""
13-
account = user_account_name(username)
15+
if not hasattr(args, "username"):
16+
raise ValueError("username is required")
17+
18+
account = user_account_name(args.username)
1419

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

24+
if getattr(args, "force", False) is False:
25+
cont = input(f"Signout account {account} from all active sessions? (Y/n)")
26+
if not cont.lower().startswith("y"):
27+
print("Aborting signout.")
28+
return RESULT_SUCCESS
29+
1930
print(f"User exists, signing out from all active sessions: {account}")
2031

2132
res = CallGAMCommand(("user", account, "signout"))

compiler_admin/main.py

+18-9
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,23 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser:
4949
"account_type", choices=ACCOUNT_TYPE_OU.keys(), help="Target account type for this conversion."
5050
)
5151

52-
_subcmd("delete", help="Delete a user account.")
52+
delete_parser = _subcmd("delete", help="Delete a user account.")
53+
delete_parser.add_argument(
54+
"--force", action="store_true", default=False, help="Don't ask for confirmation before deletion."
55+
)
5356

5457
offboard_parser = _subcmd("offboard", help="Offboard a user account.")
5558
offboard_parser.add_argument("--alias", help="Account to assign username as an alias.")
59+
offboard_parser.add_argument(
60+
"--force", action="store_true", default=False, help="Don't ask for confirmation before offboarding."
61+
)
5662

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

59-
_subcmd("signout", help="Signs a user out from all active sessions.")
65+
signout_parser = _subcmd("signout", help="Signs a user out from all active sessions.")
66+
signout_parser.add_argument(
67+
"--force", action="store_true", default=False, help="Don't ask for confirmation before signout."
68+
)
6069

6170
if len(argv) == 0:
6271
argv = ["info"]
@@ -66,19 +75,19 @@ def _subcmd(name, help, add_username_arg=True) -> argparse.ArgumentParser:
6675
if args.command == "info":
6776
return info()
6877
elif args.command == "create":
69-
return create(args.username, *extra)
78+
return create(args, *extra)
7079
elif args.command == "convert":
71-
return convert(args.username, args.account_type)
80+
return convert(args)
7281
elif args.command == "delete":
73-
return delete(args.username)
82+
return delete(args)
7483
elif args.command == "init":
75-
return init(args.username, gam=args.gam, gyb=args.gyb)
84+
return init(args)
7685
elif args.command == "offboard":
77-
return offboard(args.username, args.alias)
86+
return offboard(args)
7887
elif args.command == "restore":
79-
return restore(args.username)
88+
return restore(args)
8089
elif args.command == "signout":
81-
return signout(args.username)
90+
return signout(args)
8291

8392

8493
if __name__ == "__main__":

0 commit comments

Comments
 (0)