38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
"""Generate a human key once; deployment configuration contains only its digest.
|
|
|
|
Run locally in a private terminal. Do not redirect the complete output into deployment config.
|
|
"""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import secrets
|
|
import uuid
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--subject", required=True)
|
|
parser.add_argument("--role", choices=("viewer", "operator", "admin"), required=True)
|
|
parser.add_argument("--days", type=int, default=30)
|
|
args = parser.parse_args()
|
|
if not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9._@-]{0,127}", args.subject):
|
|
parser.error("subject must be 1-128 letters, digits, dots, underscores, @ or hyphens")
|
|
if args.subject == "legacy-operator" or not 1 <= args.days <= 365:
|
|
parser.error("reserved subject or expiry outside 1-365 days")
|
|
token = "mfop_" + secrets.token_urlsafe(32)
|
|
record = {
|
|
"credential_id": str(uuid.uuid4()),
|
|
"subject": args.subject,
|
|
"role": args.role,
|
|
"token_sha256": hashlib.sha256(token.encode()).hexdigest(),
|
|
"expires_at": (datetime.now(UTC) + timedelta(days=args.days)).isoformat(),
|
|
}
|
|
print(json.dumps({"one_time_key": token, "deployment_record": record}, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|