"""add system:role:view permission + grant it to admin Revision ID: j4k5l6m7n8o9 Revises: e1f2a3b4c5d6 Create Date: 2026-08-25 """ import hashlib from collections.abc import Sequence import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. revision: str = "j4k5l6m7n8o9" down_revision: str | Sequence[str] | None = "e1f2a3b4c5d6" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None # ── seed constants ─────────────────────────────────────────────── ADMIN_ROLE_ID = "0000000000000000000000000A" PERMISSION_CODE = "system:role:view" PERMISSION_NAME = "查看角色管理" MODULE_CODE = "system" PERMISSION_DESCRIPTION = "查看角色管理与权限配置" CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" def _deterministic_permission_id(code: str) -> str: """Stable 26-char ULID-shaped id derived from permission_code. Mirrors ``e1f2a3b4c5d6_rebuild_baseline._deterministic_permission_id`` so the id for a given code matches what the baseline would derive. """ digest = hashlib.sha256( f"model-platform-permission-v1:{code}".encode() ).digest() value = int.from_bytes(b"\x00" * 6 + digest[:10], byteorder="big") encoded = ["0"] * 26 for index in range(25, -1, -1): encoded[index] = CROCKFORD_BASE32[value & 31] value >>= 5 return "".join(encoded) def upgrade() -> None: """Add system:role:view permission and grant it to admin only.""" permission_id = _deterministic_permission_id(PERMISSION_CODE) permissions_table = sa.table( "permissions", sa.column("permission_id", sa.CHAR(26)), sa.column("permission_code", sa.String(128)), sa.column("permission_name", sa.String(100)), sa.column("module_code", sa.String(64)), sa.column("description", sa.String(500)), ) op.bulk_insert( permissions_table, [ { "permission_id": permission_id, "permission_code": PERMISSION_CODE, "permission_name": PERMISSION_NAME, "module_code": MODULE_CODE, "description": PERMISSION_DESCRIPTION, } ], ) # admin 持有全部 system:*(沿用基线约定);developer 不授予 system:*. role_permissions_table = sa.table( "role_permissions", sa.column("role_id", sa.CHAR(26)), sa.column("permission_id", sa.CHAR(26)), ) op.bulk_insert( role_permissions_table, [ { "role_id": ADMIN_ROLE_ID, "permission_id": permission_id, } ], ) def downgrade() -> None: """Remove admin's system:role:view grant and soft-delete the permission.""" permission_id = _deterministic_permission_id(PERMISSION_CODE) # 软删 role_permissions 关联行(与后端 soft-delete 约定一致). role_permissions_table = sa.table( "role_permissions", sa.column("role_id", sa.CHAR(26)), sa.column("permission_id", sa.CHAR(26)), sa.column("is_deleted", sa.Integer), sa.column("deleted_at", sa.DateTime), ) op.execute( role_permissions_table.update() .where(role_permissions_table.c.role_id == ADMIN_ROLE_ID) .where(role_permissions_table.c.permission_id == permission_id) .where(role_permissions_table.c.is_deleted == 0) .values(is_deleted=1, deleted_at=sa.text("CURRENT_TIMESTAMP(3)")) ) # 软删 permissions 行(基线表含 is_deleted,优先软删除). permissions_table = sa.table( "permissions", sa.column("permission_id", sa.CHAR(26)), sa.column("is_deleted", sa.Integer), sa.column("deleted_at", sa.DateTime), ) op.execute( permissions_table.update() .where(permissions_table.c.permission_id == permission_id) .where(permissions_table.c.is_deleted == 0) .values(is_deleted=1, deleted_at=sa.text("CURRENT_TIMESTAMP(3)")) )