93 lines
3.9 KiB
Python
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())
|