"""Phase 14: Enterprise Readiness — api_keys and sso_configs tables. Revision ID: b040ent Revises: b039exec Create Date: 2026-05-20 """ from alembic import op import sqlalchemy as sa revision = "b040ent" down_revision = "b039exec" branch_labels = None depends_on = None def upgrade() -> None: conn = op.get_bind() # ── api_keys ────────────────────────────────────────────────────────────── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(200) NOT NULL, description TEXT, key_prefix VARCHAR(13) NOT NULL, key_hash VARCHAR(64) NOT NULL UNIQUE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, scopes JSONB NOT NULL DEFAULT '["read"]', last_used_at TIMESTAMP WITHOUT TIME ZONE, expires_at TIMESTAMP WITHOUT TIME ZONE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now() ) """)) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_api_keys_user_id ON api_keys (user_id)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_api_keys_key_hash ON api_keys (key_hash)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_api_keys_active ON api_keys (is_active)" )) # ── sso_configs ─────────────────────────────────────────────────────────── conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS sso_configs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), is_enabled BOOLEAN NOT NULL DEFAULT FALSE, provider_name VARCHAR(200), sp_entity_id VARCHAR(500), sp_acs_url VARCHAR(500), sp_slo_url VARCHAR(500), sp_certificate TEXT, sp_private_key TEXT, idp_entity_id VARCHAR(500), idp_sso_url VARCHAR(500), idp_slo_url VARCHAR(500), idp_certificate TEXT, attr_email VARCHAR(200) DEFAULT 'email', attr_username VARCHAR(200) DEFAULT 'username', attr_role VARCHAR(200) DEFAULT 'role', default_role VARCHAR(50) DEFAULT 'viewer', auto_provision BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(), updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now() ) """)) def downgrade() -> None: conn = op.get_bind() conn.execute(sa.text("DROP TABLE IF EXISTS api_keys CASCADE")) conn.execute(sa.text("DROP TABLE IF EXISTS sso_configs CASCADE"))