Skip to content

Commit a43c987

Browse files
committed
feat(user): get or refresh backup codes
1 parent d5e8ef8 commit a43c987

File tree

5 files changed

+82
-1
lines changed

5 files changed

+82
-1
lines changed

compiler_admin/commands/user/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import click
22

3+
from compiler_admin.commands.user.backupcodes import backupcodes
34
from compiler_admin.commands.user.convert import convert
45
from compiler_admin.commands.user.create import create
56
from compiler_admin.commands.user.deactivate import deactivate
@@ -18,6 +19,7 @@ def user():
1819
pass
1920

2021

22+
user.add_command(backupcodes)
2123
user.add_command(convert)
2224
user.add_command(create)
2325
user.add_command(deactivate)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import click
2+
3+
from compiler_admin import RESULT_FAILURE
4+
from compiler_admin.services.google import (
5+
get_backup_codes,
6+
user_account_name,
7+
user_exists,
8+
)
9+
10+
11+
@click.command()
12+
@click.argument("username")
13+
def backupcodes(username: str, **kwargs):
14+
"""
15+
Get backup codes for the user, creating a new set if needed.
16+
"""
17+
account = user_account_name(username)
18+
19+
if not user_exists(account):
20+
click.echo(f"User does not exist: {account}")
21+
raise SystemExit(RESULT_FAILURE)
22+
23+
backup_codes = get_backup_codes(account)
24+
click.echo(backup_codes)

compiler_admin/services/google.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,26 @@ def add_user_to_group(username: str, group: str) -> int:
7777
return CallGAMCommand(("user", username, "add", "groups", "member", group))
7878

7979

80+
def get_backup_codes(username: str) -> str:
81+
if not user_exists(username):
82+
print(f"User does not exist: {username}")
83+
return ""
84+
85+
output = ""
86+
command = ("user", username, "show", "backupcodes")
87+
with NamedTemporaryFile("w+") as stdout:
88+
CallGAMCommand(command, stdout=stdout.name, stderr="stdout")
89+
output = "".join(stdout.readlines())
90+
91+
if "Show 0 Backup Verification Codes" in output:
92+
command = ("user", username, "update", "backupcodes")
93+
with NamedTemporaryFile("w+") as stdout:
94+
CallGAMCommand(command, stdout=stdout.name, stderr="stdout")
95+
output = "".join(stdout.readlines())
96+
97+
return output
98+
99+
80100
def move_user_ou(username: str, ou: str) -> int:
81101
"""Move a user into a new OU."""
82102
return CallGAMCommand(("update", "ou", ou, "move", username))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
from compiler_admin import RESULT_FAILURE, RESULT_SUCCESS
4+
from compiler_admin.commands.user.backupcodes import backupcodes, __name__ as MODULE
5+
6+
7+
@pytest.fixture
8+
def mock_google_user_exists(mock_google_user_exists):
9+
return mock_google_user_exists(MODULE)
10+
11+
12+
@pytest.fixture
13+
def mock_get_backup_codes(mocker):
14+
return mocker.patch(f"{MODULE}.get_backup_codes")
15+
16+
17+
def test_backupcodes_user_does_not_exist(cli_runner, mock_google_user_exists, mock_get_backup_codes):
18+
mock_google_user_exists.return_value = False
19+
20+
result = cli_runner.invoke(backupcodes, ["username"])
21+
22+
assert result.exit_code == RESULT_FAILURE
23+
mock_get_backup_codes.assert_not_called()
24+
25+
26+
def test_backupcodes_user_exists(cli_runner, mock_google_user_exists, mock_get_backup_codes):
27+
mock_google_user_exists.return_value = True
28+
mock_get_backup_codes.return_value = "1234"
29+
30+
result = cli_runner.invoke(backupcodes, ["username"])
31+
32+
assert result.exit_code == RESULT_SUCCESS
33+
mock_get_backup_codes.assert_called_once()

tests/commands/user/test_init.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from compiler_admin.commands.user import user
44

55

6-
@pytest.mark.parametrize("command", ["convert", "create", "deactivate", "delete", "offboard", "reset", "restore", "signout"])
6+
@pytest.mark.parametrize(
7+
"command", ["backupcodes", "convert", "create", "deactivate", "delete", "offboard", "reset", "restore", "signout"]
8+
)
79
def test_user_commands(command):
810
assert command in user.commands

0 commit comments

Comments
 (0)