Files
saas_backend/alembic/versions/a1f0c2d3e401_explicit_superadmin_flag.py
T

63 lines
1.8 KiB
Python
Raw Normal View History

2026-08-31 20:04:12 -04:00
"""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")