Prepare GovOPlaN self-hosted release workflow
This commit is contained in:
@@ -4,15 +4,17 @@ from dataclasses import dataclass
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.admin.common import AdminValidationError
|
||||
from govoplan_core.core.access import CAPABILITY_ACCESS_TENANT_PROVISIONER, CreatedApiKeyRef, TenantAccessProvisioner, UserRef
|
||||
from govoplan_core.core.modules import ModuleContext
|
||||
from govoplan_core.core.runtime import configure_runtime, get_registry
|
||||
from govoplan_core.db.base import Base
|
||||
from govoplan_core.db.session import get_database
|
||||
from govoplan_core.core.module_management import load_startup_enabled_modules, startup_candidate_module_ids
|
||||
from govoplan_core.security.permissions import TENANT_SCOPES
|
||||
from govoplan_core.server.registry import available_module_manifests, build_platform_registry
|
||||
from govoplan_core.settings import settings
|
||||
from govoplan_access.backend.admin.service import ensure_default_roles, get_or_create_account
|
||||
from govoplan_access.backend.db.models import Role, SystemRoleAssignment, Tenant, User, UserRoleAssignment
|
||||
from govoplan_access.backend.security.api_keys import CreatedApiKey, create_api_key
|
||||
from govoplan_core.tenancy.scope import Tenant
|
||||
from govoplan_core.tenancy.scope import create_scope_tables
|
||||
|
||||
DEFAULT_SCOPES = sorted(TENANT_SCOPES)
|
||||
@@ -21,8 +23,8 @@ DEFAULT_SCOPES = sorted(TENANT_SCOPES)
|
||||
@dataclass(slots=True)
|
||||
class BootstrapResult:
|
||||
tenant: Tenant
|
||||
user: User
|
||||
created_api_key: CreatedApiKey | None
|
||||
user: UserRef
|
||||
created_api_key: CreatedApiKeyRef | None
|
||||
|
||||
|
||||
def create_all_tables() -> None:
|
||||
@@ -42,6 +44,21 @@ def create_all_tables() -> None:
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
|
||||
def _access_provisioner() -> TenantAccessProvisioner:
|
||||
registry = get_registry()
|
||||
if registry is None:
|
||||
registry = build_platform_registry(settings.enabled_modules)
|
||||
context = ModuleContext(registry=registry, settings=settings)
|
||||
registry.configure_capability_context(context)
|
||||
configure_runtime(context)
|
||||
if registry is None or not hasattr(registry, "has_capability") or not registry.has_capability(CAPABILITY_ACCESS_TENANT_PROVISIONER):
|
||||
raise RuntimeError("Development bootstrap requires the access tenant provisioner capability.")
|
||||
capability = registry.require_capability(CAPABILITY_ACCESS_TENANT_PROVISIONER)
|
||||
if not isinstance(capability, TenantAccessProvisioner):
|
||||
raise RuntimeError("Access tenant provisioner capability is invalid.")
|
||||
return capability
|
||||
|
||||
|
||||
def bootstrap_dev_data(
|
||||
session: Session,
|
||||
*,
|
||||
@@ -56,72 +73,16 @@ def bootstrap_dev_data(
|
||||
session.add(tenant)
|
||||
session.flush()
|
||||
|
||||
tenant_roles = ensure_default_roles(session, tenant)
|
||||
system_roles = ensure_default_roles(session, None)
|
||||
|
||||
account, _, _ = get_or_create_account(
|
||||
session,
|
||||
email=user_email,
|
||||
display_name="Development Admin",
|
||||
password=user_password,
|
||||
password_reset_required=False,
|
||||
)
|
||||
# Keep development credentials deterministic when an old create_all database
|
||||
# is reused. This is guarded by the explicit dev bootstrap setting.
|
||||
from govoplan_access.backend.security.passwords import hash_password
|
||||
|
||||
if not account.password_hash:
|
||||
account.password_hash = hash_password(user_password)
|
||||
account.is_active = True
|
||||
session.add(account)
|
||||
|
||||
user = session.query(User).filter(User.tenant_id == tenant.id, User.account_id == account.id).one_or_none()
|
||||
if user is None:
|
||||
user = User(
|
||||
tenant_id=tenant.id,
|
||||
account_id=account.id,
|
||||
email=account.email,
|
||||
display_name="Development Admin",
|
||||
is_tenant_admin=True, # legacy presentation flag; RBAC uses the owner role below.
|
||||
auth_provider=account.auth_provider,
|
||||
password_hash=account.password_hash,
|
||||
try:
|
||||
result = _access_provisioner().ensure_development_admin(
|
||||
session,
|
||||
tenant=tenant,
|
||||
user_email=user_email,
|
||||
user_password=user_password,
|
||||
api_key_secret=api_key_secret,
|
||||
scopes=DEFAULT_SCOPES,
|
||||
)
|
||||
session.add(user)
|
||||
session.flush()
|
||||
else:
|
||||
user.email = account.email
|
||||
user.password_hash = account.password_hash
|
||||
user.is_active = True
|
||||
session.add(user)
|
||||
|
||||
owner_role = tenant_roles["owner"]
|
||||
existing_assignment = session.query(UserRoleAssignment).filter(
|
||||
UserRoleAssignment.tenant_id == tenant.id,
|
||||
UserRoleAssignment.user_id == user.id,
|
||||
UserRoleAssignment.role_id == owner_role.id,
|
||||
).one_or_none()
|
||||
if existing_assignment is None:
|
||||
session.add(UserRoleAssignment(tenant_id=tenant.id, user_id=user.id, role_id=owner_role.id))
|
||||
|
||||
system_owner = system_roles["system_owner"]
|
||||
existing_system_assignment = session.query(SystemRoleAssignment).filter(
|
||||
SystemRoleAssignment.account_id == account.id,
|
||||
SystemRoleAssignment.role_id == system_owner.id,
|
||||
).one_or_none()
|
||||
if existing_system_assignment is None:
|
||||
session.add(SystemRoleAssignment(account_id=account.id, role_id=system_owner.id))
|
||||
|
||||
created_api_key = None
|
||||
if api_key_secret:
|
||||
existing = [key for key in user.api_keys if key.name == "Development API key" and key.revoked_at is None]
|
||||
if not existing:
|
||||
created_api_key = create_api_key(
|
||||
session,
|
||||
user=user,
|
||||
name="Development API key",
|
||||
scopes=DEFAULT_SCOPES,
|
||||
secret=api_key_secret,
|
||||
)
|
||||
|
||||
except AdminValidationError as exc:
|
||||
raise RuntimeError(str(exc)) from exc
|
||||
session.commit()
|
||||
return BootstrapResult(tenant=tenant, user=user, created_api_key=created_api_key)
|
||||
return BootstrapResult(tenant=tenant, user=result.user, created_api_key=result.created_api_key)
|
||||
|
||||
Reference in New Issue
Block a user