Files

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()