"""Restricted interactive owner-account provisioning command.""" from __future__ import annotations import argparse import getpass import os import sys from pathlib import Path import pyotp from gewu.adapters.sqlite_identity import SQLiteIdentityStore def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("principal_id") parser.add_argument( "--project", action="append", dest="project_ids", required=True, metavar="PROJECT_ID" ) arguments = parser.parse_args() if not sys.stdin.isatty() or not sys.stdout.isatty(): parser.error("owner provisioning requires an interactive terminal") data_root = Path(os.environ.get("GEWU_DATA_ROOT", "./.data/gewu")).expanduser().absolute() encryption_key = os.environ.get("GEWU_OWNER_ENCRYPTION_KEY") if encryption_key is None: parser.error("GEWU_OWNER_ENCRYPTION_KEY must be configured outside the database") password = getpass.getpass("Owner password: ") confirmation = getpass.getpass("Confirm owner password: ") if password != confirmation: parser.error("owner passwords did not match") secret = pyotp.random_base32() store = SQLiteIdentityStore( data_root / "identity.sqlite3", authority_lock_path=data_root / "workflow/locks/canonical-writer.lock", owner_encryption_key=encryption_key.encode("ascii"), ) try: store.provision_owner( principal_id=arguments.principal_id, password=password, totp_secret=secret, project_ids=tuple(arguments.project_ids), ) except (ValueError, RuntimeError) as error: parser.error(str(error)) finally: password = "" confirmation = "" enrollment_uri = pyotp.TOTP(secret).provisioning_uri( name=arguments.principal_id, issuer_name="GEWU" ) print("Add this one-time enrollment URI to the owner authenticator:") print(enrollment_uri) print("The URI contains the TOTP seed. Store it securely; it will not be shown again.") return 0 if __name__ == "__main__": raise SystemExit(main())