63 lines
1.8 KiB
Python
63 lines
1.8 KiB
Python
"""Make superadmin an explicit flag instead of an implied null tenant.
|
|
|
|
Before this migration, `is_superadmin(user)` was `user.tenant_id is None`. Any
|
|
code path that created a user without a tenant therefore created a platform
|
|
superadmin — including the public signup endpoint, which the frontend calls with
|
|
no tenant header at all. The flag makes the privilege something a row states
|
|
rather than something the absence of a value implies.
|
|
|
|
Backfill is deliberately conservative: only accounts that are BOTH tenant-less
|
|
AND hold the seeded platform 'superadmin' role are marked. Any other tenant-less
|
|
account is left as a non-superadmin and is reported by the accompanying audit
|
|
query in Phase 1.1 — those are the accounts that should not exist.
|
|
|
|
Revision ID: a1f0c2d3e401
|
|
Revises: 7b2c4d5e6f77
|
|
"""
|
|
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "a1f0c2d3e401"
|
|
down_revision: Union[str, Sequence[str], None] = "7b2c4d5e6f77"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"users",
|
|
sa.Column(
|
|
"is_superadmin",
|
|
sa.Boolean(),
|
|
nullable=False,
|
|
server_default=sa.false(),
|
|
),
|
|
)
|
|
|
|
op.execute(
|
|
"""
|
|
UPDATE users
|
|
SET is_superadmin = true
|
|
WHERE tenant_id IS NULL
|
|
AND role_id IN (
|
|
SELECT id FROM roles
|
|
WHERE role_name = 'superadmin' AND tenant_id IS NULL
|
|
)
|
|
"""
|
|
)
|
|
|
|
op.create_index(
|
|
"ix_users_is_superadmin",
|
|
"users",
|
|
["is_superadmin"],
|
|
postgresql_where=sa.text("is_superadmin"),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_users_is_superadmin", table_name="users")
|
|
op.drop_column("users", "is_superadmin")
|