RBAC alembic
This commit is contained in:
151
alembic/versions/fa7d4d10e358_add_rbac_enhancements.py
Normal file
151
alembic/versions/fa7d4d10e358_add_rbac_enhancements.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
"""add_rbac_enhancements
|
||||||
|
|
||||||
|
Revision ID: fa7d4d10e358
|
||||||
|
Revises: 4951b2e50581
|
||||||
|
Create Date: 2025-11-13 16:51:25.010057
|
||||||
|
|
||||||
|
SQLite-compatible version
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'fa7d4d10e358'
|
||||||
|
down_revision: Union[str, None] = '4951b2e50581'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
"""Upgrade database schema for RBAC enhancements.
|
||||||
|
|
||||||
|
SQLite-compatible version using batch operations for table modifications.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# User table changes
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('is_email_verified', sa.Boolean(), nullable=False, server_default='false')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set existing active users as verified
|
||||||
|
op.execute("UPDATE users SET is_email_verified = TRUE WHERE is_active = TRUE")
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# VendorUser table changes (requires table recreation for SQLite)
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('vendor_users', schema=None) as batch_op:
|
||||||
|
# Add new columns
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('user_type', sa.String(length=20), nullable=False, server_default='member')
|
||||||
|
)
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('invitation_token', sa.String(length=100), nullable=True)
|
||||||
|
)
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('invitation_sent_at', sa.DateTime(), nullable=True)
|
||||||
|
)
|
||||||
|
batch_op.add_column(
|
||||||
|
sa.Column('invitation_accepted_at', sa.DateTime(), nullable=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create index on invitation_token
|
||||||
|
batch_op.create_index(
|
||||||
|
'idx_vendor_users_invitation_token',
|
||||||
|
['invitation_token']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Modify role_id to be nullable (this recreates the table in SQLite)
|
||||||
|
batch_op.alter_column(
|
||||||
|
'role_id',
|
||||||
|
existing_type=sa.Integer(),
|
||||||
|
nullable=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Change is_active default (this recreates the table in SQLite)
|
||||||
|
batch_op.alter_column(
|
||||||
|
'is_active',
|
||||||
|
existing_type=sa.Boolean(),
|
||||||
|
server_default='false'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set owners correctly (after table modifications)
|
||||||
|
# SQLite-compatible UPDATE with subquery
|
||||||
|
op.execute("""
|
||||||
|
UPDATE vendor_users
|
||||||
|
SET user_type = 'owner'
|
||||||
|
WHERE (vendor_id, user_id) IN (
|
||||||
|
SELECT id, owner_user_id
|
||||||
|
FROM vendors
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Set existing owners as active
|
||||||
|
op.execute("""
|
||||||
|
UPDATE vendor_users
|
||||||
|
SET is_active = TRUE
|
||||||
|
WHERE user_type = 'owner'
|
||||||
|
""")
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# Role table changes
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('roles', schema=None) as batch_op:
|
||||||
|
# Create index on vendor_id and name
|
||||||
|
batch_op.create_index(
|
||||||
|
'idx_roles_vendor_name',
|
||||||
|
['vendor_id', 'name']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Note: JSONB conversion only for PostgreSQL
|
||||||
|
# SQLite stores JSON as TEXT by default, no conversion needed
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
"""Downgrade database schema.
|
||||||
|
|
||||||
|
SQLite-compatible version using batch operations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# Role table changes
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('roles', schema=None) as batch_op:
|
||||||
|
batch_op.drop_index('idx_roles_vendor_name')
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# VendorUser table changes
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('vendor_users', schema=None) as batch_op:
|
||||||
|
# Revert is_active default
|
||||||
|
batch_op.alter_column(
|
||||||
|
'is_active',
|
||||||
|
existing_type=sa.Boolean(),
|
||||||
|
server_default='true'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Revert role_id to NOT NULL
|
||||||
|
# Note: This might fail if there are NULL values
|
||||||
|
batch_op.alter_column(
|
||||||
|
'role_id',
|
||||||
|
existing_type=sa.Integer(),
|
||||||
|
nullable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Drop indexes and columns
|
||||||
|
batch_op.drop_index('idx_vendor_users_invitation_token')
|
||||||
|
batch_op.drop_column('invitation_accepted_at')
|
||||||
|
batch_op.drop_column('invitation_sent_at')
|
||||||
|
batch_op.drop_column('invitation_token')
|
||||||
|
batch_op.drop_column('user_type')
|
||||||
|
|
||||||
|
# ========================================================================
|
||||||
|
# User table changes
|
||||||
|
# ========================================================================
|
||||||
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('is_email_verified')
|
||||||
Reference in New Issue
Block a user