65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import secrets
|
|
from pathlib import Path
|
|
|
|
from relay_gateway.keys import hash_token, CHANNELS_FOR_LEVEL
|
|
|
|
|
|
def _read(path: Path) -> dict:
|
|
try:
|
|
return json.loads(Path(path).read_text(encoding="utf-8"))
|
|
except (FileNotFoundError, json.JSONDecodeError):
|
|
return {}
|
|
|
|
|
|
def _write(path: Path, body: dict) -> None:
|
|
Path(path).write_text(json.dumps(body, indent=2), encoding="utf-8")
|
|
|
|
|
|
def add_key(path: Path, *, name: str, level: str) -> str:
|
|
if level not in CHANNELS_FOR_LEVEL:
|
|
raise ValueError(f"level must be one of {sorted(CHANNELS_FOR_LEVEL)}")
|
|
token = secrets.token_urlsafe(32)
|
|
body = _read(path)
|
|
body[hash_token(token)] = {"name": name, "level": level}
|
|
_write(path, body)
|
|
return token
|
|
|
|
|
|
def list_keys(path: Path) -> list[dict]:
|
|
return [{"hash": h, **meta} for h, meta in _read(path).items()]
|
|
|
|
|
|
def revoke(path: Path, name: str) -> int:
|
|
body = _read(path)
|
|
to_remove = [h for h, meta in body.items() if meta.get("name") == name]
|
|
for h in to_remove:
|
|
del body[h]
|
|
_write(path, body)
|
|
return len(to_remove)
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description="Manage relay gateway keys")
|
|
ap.add_argument("--file", required=True, type=Path)
|
|
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
a = sub.add_parser("add"); a.add_argument("--name", required=True); a.add_argument("--level", required=True)
|
|
sub.add_parser("list")
|
|
r = sub.add_parser("revoke"); r.add_argument("--name", required=True)
|
|
args = ap.parse_args()
|
|
if args.cmd == "add":
|
|
token = add_key(args.file, name=args.name, level=args.level)
|
|
print(f"Token for {args.name!r} (level={args.level}) — store it now, shown once:\n{token}")
|
|
elif args.cmd == "list":
|
|
for e in list_keys(args.file):
|
|
print(f"{e['name']:20s} {e['level']:4s} {e['hash'][:12]}…")
|
|
elif args.cmd == "revoke":
|
|
print(f"Removed {revoke(args.file, args.name)} key(s) named {args.name!r}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|