- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
149 lines
5.0 KiB
Python
149 lines
5.0 KiB
Python
"""add_rbac_enhancements
|
|
|
|
Revision ID: fa7d4d10e358
|
|
Revises: 4951b2e50581
|
|
Create Date: 2025-11-13 16:51:25.010057
|
|
|
|
SQLite-compatible version
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "fa7d4d10e358"
|
|
down_revision: str | None = "4951b2e50581"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: 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")
|