API: CLI tokens_cli (create/list/revoke) (#78)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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())
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user