Files
UmbraTrans/poc/bootstrap_poc.py
2026-10-18 19:01:05 -07:00

93 lines
3.9 KiB
Python

# Copyright 2026 Umbra Tools (umbratools.com)
# SPDX-License-Identifier: Apache-2.0
"""Bring up a scratch environment for the compute proof of concept.
Creates a platform administrator and an organization in the scratch database,
using the same service path a deployment uses, so the proof of concept exercises
real authentication rather than a shortcut.
python poc/bootstrap_poc.py --dsn postgresql:///umbratrans_scratch --email poc@example.test
Prints the temporary password once. It is not stored anywhere and cannot be
recovered, which is the point.
"""
from __future__ import annotations
import argparse
import getpass
import os
import sys
from pathlib import Path
from uuid import uuid4
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from umbratrans_web.config import Environment, Settings # noqa: E402
from umbratrans_web.mailer import CapturingMailer # noqa: E402
from umbratrans_web.models import RoleName # noqa: E402
from umbratrans_web.postgres_repository import PostgresAccountRepository # noqa: E402
from umbratrans_web.service import AccountService, OperatorContext # noqa: E402
SCRATCH_MARKERS = ("scratch", "poc", "test", "tmp", "dev")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Bootstrap a scratch environment for the PoC")
parser.add_argument("--dsn", required=True)
parser.add_argument("--email", default="poc-admin@example.test")
parser.add_argument("--username", default="poc.admin")
parser.add_argument("--organization", default="Umbra PoC")
args = parser.parse_args(argv)
name = args.dsn.rsplit("/", 1)[-1].split("?", 1)[0]
if not any(marker in name.lower() for marker in SCRATCH_MARKERS):
print(f"refusing to write to '{name}': it does not look like a scratch database", file=sys.stderr)
return 2
os.environ["UMBRATRANS_ENV"] = "test"
settings = Settings(environment=Environment.TEST, database_dsn=args.dsn)
repository = PostgresAccountRepository.from_dsn(
args.dsn, min_size=1, max_size=3, application_name="umbratrans-poc-bootstrap"
)
service = AccountService(repository, settings, mailer=CapturingMailer())
try:
# An organization to own the work, created directly because the API path
# for it needs a platform administrator and that is what we are creating.
with repository._pool.connection() as connection, connection.transaction(): # noqa: SLF001
organization_id = uuid4()
connection.execute(
"INSERT INTO public.organizations (id, name, slug) VALUES (%s, %s, %s)",
(organization_id, args.organization, f"poc-{organization_id.hex[:8]}"),
)
# The operator path, which is how a first administrator is created. It
# is the only path that can make one, and it takes the password from a
# hidden prompt rather than from an argument, so no password can appear
# in a shell history or a process listing.
view = service.create_account(
enrollment_mode="temporary_password",
username=args.username,
display_name="PoC Administrator",
email=args.email,
role=str(RoleName.PLATFORM_ADMIN),
organization_id=organization_id,
password=getpass.getpass("temporary password for the PoC administrator: "),
operator=OperatorContext(source="operator-cli", interactive=True),
)
print(f"organization : {organization_id}")
print(f"email : {args.email}")
print(f"username : {args.username}")
print(f"account : {view.id}")
print()
print("The temporary password is shown once and is not stored. Sign in with it and")
print("replace it, then run poc/run_poc.py.")
finally:
repository.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())