diff --git a/src/api/tokens_cli.py b/src/api/tokens_cli.py new file mode 100644 index 0000000..287ac06 --- /dev/null +++ b/src/api/tokens_cli.py @@ -0,0 +1,57 @@ +import argparse +import os +import sys + +from src.api import tokens_db + + +def main(argv=None, env=None) -> int: + env = env if env is not None else os.environ + parser = argparse.ArgumentParser(prog="python -m src.api.tokens_cli") + sub = parser.add_subparsers(dest="cmd", required=True) + + p_create = sub.add_parser("create", help="Créer un token API") + p_create.add_argument("--label", required=True) + p_create.add_argument("--user-id", type=int, default=None) + + sub.add_parser("list", help="Lister les tokens") + + p_revoke = sub.add_parser("revoke", help="Révoquer un token") + p_revoke.add_argument("token_id", type=int) + + args = parser.parse_args(argv) + db_path = env["USERS_DB_PATH"] + tokens_db.init_schema(db_path) + + if args.cmd == "create": + token, token_id = tokens_db.create_token(db_path, args.label, args.user_id) + print(f"id={token_id} label={args.label}") + print(f"token (à conserver, ne sera plus affiché) : {token}") + return 0 + + if args.cmd == "list": + rows = tokens_db.list_tokens(db_path) + if not rows: + print("(aucun token)") + return 0 + print( + f"{'id':<4} {'label':<40} {'created_at':<26} {'last_used_at':<26} {'count':<7} revoked" + ) + for r in rows: + print( + f"{r['id']:<4} {r['label']:<40} {r['created_at']:<26} " + f"{(r['last_used_at'] or '-'):<26} {r['count_total']:<7} " + f"{r['revoked_at'] or ''}" + ) + return 0 + + if args.cmd == "revoke": + tokens_db.revoke_token(db_path, args.token_id) + print(f"token id={args.token_id} révoqué") + return 0 + + return 1 + + +if __name__ == "__main__": # pragma: no cover + sys.exit(main()) diff --git a/tests/api/test_tokens_cli.py b/tests/api/test_tokens_cli.py new file mode 100644 index 0000000..24b7d87 --- /dev/null +++ b/tests/api/test_tokens_cli.py @@ -0,0 +1,33 @@ +from src.api import tokens_cli, tokens_db + + +def _run(args, env): + return tokens_cli.main(args, env=env) + + +def test_create_prints_plaintext_token_once(temp_db, capsys): + rc = _run(["create", "--label", "alice"], env={"USERS_DB_PATH": str(temp_db)}) + out = capsys.readouterr().out + assert rc == 0 + assert "decpinfo_" in out + tokens = tokens_db.list_tokens(temp_db) + assert len(tokens) == 1 + assert tokens[0]["label"] == "alice" + + +def test_list_shows_tokens(temp_db, capsys): + tokens_db.create_token(temp_db, "alice") + tokens_db.create_token(temp_db, "bob") + rc = _run(["list"], env={"USERS_DB_PATH": str(temp_db)}) + out = capsys.readouterr().out + assert rc == 0 + assert "alice" in out + assert "bob" in out + + +def test_revoke_sets_revoked_at(temp_db, capsys): + _, token_id = tokens_db.create_token(temp_db, "alice") + rc = _run(["revoke", str(token_id)], env={"USERS_DB_PATH": str(temp_db)}) + assert rc == 0 + tokens = tokens_db.list_tokens(temp_db) + assert tokens[0]["revoked_at"] is not None