移除QA预设角色并收紧角色模板编辑
This commit is contained in:
@@ -126,14 +126,6 @@ def _insert_system_templates() -> None:
|
||||
"IMP",
|
||||
'{"IMP": {"subjects:create": false, "subjects:list": true, "subjects:read": true, "subjects:update": false, "subjects:delete": false, "visits:create": false, "visits:list": true, "visits:read": true, "visits:update": false, "visits:delete": false, "risk_issues:create": false, "risk_issues:list": true, "risk_issues:read": true, "risk_issues:update": false, "risk_issues:delete": false, "finance_contracts:create": false, "finance_contracts:list": true, "finance_contracts:read": true, "finance_contracts:update": false, "finance_contracts:delete": false, "fees_contracts:create": false, "fees_contracts:list": true, "fees_contracts:read": true, "fees_contracts:update": false, "fees_contracts:delete": false, "fees_payments:create": false, "fees_payments:update": false, "fees_payments:delete": false, "project_members:create": false, "project_members:list": false, "project_members:candidates": false, "project_members:update": false, "project_members:delete": false, "sites:create": false, "sites:list": true, "sites:read": true, "sites:update": false, "sites:delete": false, "ethics:create": false, "ethics:list": false, "ethics:read": false, "ethics:update": false, "ethics:delete": false, "feasibility:create": false, "feasibility:list": false, "feasibility:read": false, "feasibility:update": false, "feasibility:delete": false, "budget:create": false, "budget:list": true, "budget:read": true, "budget:update": false, "budget:delete": false, "timeline:create": false, "timeline:list": false, "timeline:read": false, "timeline:update": false, "timeline:delete": false, "permissions:read": false, "permissions:update": false, "overview:read": true, "monitoring_issues:create": false, "monitoring_issues:list": false, "monitoring_issues:read": false, "monitoring_issues:update": false, "monitoring_issues:delete": false, "monitoring_issues:close": false, "monitoring_issues:history": false, "monitoring_audit:create": false, "monitoring_audit:read": false, "monitoring_audit:update": false, "monitoring_audit:delete": false, "drug_shipments:create": true, "drug_shipments:list": true, "drug_shipments:read": true, "drug_shipments:update": true, "drug_shipments:delete": false, "materials:create": true, "materials:list": true, "materials:read": true, "materials:update": true, "materials:delete": false, "subject_pds:create": false, "subject_pds:list": false, "subject_pds:read": false, "subject_pds:update": false, "audit_logs:list": false, "audit_logs:read": false, "audit_logs:export": false, "knowledge_notes:create": false, "knowledge_notes:list": true, "knowledge_notes:read": true, "knowledge_notes:update": false, "knowledge_notes:delete": false, "subject_history:list": false, "subject_history:read": false, "subject_history:timeline": false, "subject_history:export": false, "subject_history:search": false, "milestones:list": true, "milestones:update": false, "attachments:create": false, "attachments:read": true, "attachments:update": false, "attachments:delete": false, "fees_attachments:create": false, "fees_attachments:read": false, "fees_attachments:delete": false, "faq:create": false, "faq:read": true, "faq:update": false, "faq:delete": false, "faq_category:create": false, "faq_category:read": true, "faq_category:update": false, "faq_category:delete": false, "faq_reply:create": false, "faq_reply:delete": false, "dashboard:read": true, "subject_histories:create": false, "subject_histories:list": true, "subject_histories:read": true, "subject_histories:update": false, "subject_histories:delete": false, "material_equipments:create": false, "material_equipments:list": true, "material_equipments:read": true, "material_equipments:update": false, "material_equipments:delete": false, "documents:create": false, "documents:read": true, "documents:update": false, "documents:delete": false}}',
|
||||
),
|
||||
(
|
||||
"0c907f29-dc88-4878-9c66-15cbc22f4e13",
|
||||
"质量保证",
|
||||
"质量检查和审计人员,以查询权限为主",
|
||||
"QA",
|
||||
"QA",
|
||||
'{"QA": {"subjects:create": false, "subjects:list": true, "subjects:read": true, "subjects:update": false, "subjects:delete": false, "visits:create": false, "visits:list": true, "visits:read": true, "visits:update": false, "visits:delete": false, "risk_issues:create": false, "risk_issues:list": true, "risk_issues:read": true, "risk_issues:update": false, "risk_issues:delete": false, "finance_contracts:create": false, "finance_contracts:list": false, "finance_contracts:read": false, "finance_contracts:update": false, "finance_contracts:delete": false, "fees_contracts:create": false, "fees_contracts:list": false, "fees_contracts:read": false, "fees_contracts:update": false, "fees_contracts:delete": false, "fees_payments:create": false, "fees_payments:update": false, "fees_payments:delete": false, "project_members:create": false, "project_members:list": false, "project_members:candidates": false, "project_members:update": false, "project_members:delete": false, "sites:create": false, "sites:list": true, "sites:read": true, "sites:update": false, "sites:delete": false, "ethics:create": false, "ethics:list": false, "ethics:read": false, "ethics:update": false, "ethics:delete": false, "feasibility:create": false, "feasibility:list": false, "feasibility:read": false, "feasibility:update": false, "feasibility:delete": false, "budget:create": false, "budget:list": false, "budget:read": false, "budget:update": false, "budget:delete": false, "timeline:create": false, "timeline:list": false, "timeline:read": false, "timeline:update": false, "timeline:delete": false, "permissions:read": false, "permissions:update": false, "overview:read": true, "monitoring_issues:create": false, "monitoring_issues:list": false, "monitoring_issues:read": false, "monitoring_issues:update": false, "monitoring_issues:delete": false, "monitoring_issues:close": false, "monitoring_issues:history": false, "monitoring_audit:create": false, "monitoring_audit:read": true, "monitoring_audit:update": false, "monitoring_audit:delete": false, "drug_shipments:create": false, "drug_shipments:list": true, "drug_shipments:read": true, "drug_shipments:update": false, "drug_shipments:delete": false, "materials:create": false, "materials:list": true, "materials:read": true, "materials:update": false, "materials:delete": false, "subject_pds:create": false, "subject_pds:list": false, "subject_pds:read": false, "subject_pds:update": false, "audit_logs:list": true, "audit_logs:read": true, "audit_logs:export": true, "knowledge_notes:create": false, "knowledge_notes:list": true, "knowledge_notes:read": true, "knowledge_notes:update": false, "knowledge_notes:delete": false, "subject_history:list": false, "subject_history:read": false, "subject_history:timeline": false, "subject_history:export": false, "subject_history:search": false, "milestones:list": true, "milestones:update": false, "attachments:create": false, "attachments:read": true, "attachments:update": false, "attachments:delete": false, "fees_attachments:create": false, "fees_attachments:read": false, "fees_attachments:delete": false, "faq:create": false, "faq:read": true, "faq:update": false, "faq:delete": false, "faq_category:create": false, "faq_category:read": true, "faq_category:update": false, "faq_category:delete": false, "faq_reply:create": false, "faq_reply:delete": false, "dashboard:read": true, "subject_histories:create": false, "subject_histories:list": true, "subject_histories:read": true, "subject_histories:update": false, "subject_histories:delete": false, "material_equipments:create": false, "material_equipments:list": true, "material_equipments:read": true, "material_equipments:update": false, "material_equipments:delete": false, "documents:create": false, "documents:read": true, "documents:update": false, "documents:delete": false}}',
|
||||
),
|
||||
]
|
||||
|
||||
for tid, name, desc, category, recommended_roles, perms_json in templates:
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
"""remove qa role
|
||||
|
||||
Revision ID: 20260521_01
|
||||
Revises: 20260520_01
|
||||
Create Date: 2026-05-21 09:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
revision: str = "20260521_01"
|
||||
down_revision: Union[str, None] = "20260520_01"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
USER_ROLE_VALUES = ("ADMIN", "PM", "CRA", "PV", "IMP", "MEDICAL_REVIEW")
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("CREATE TEMP TABLE qa_user_ids AS SELECT id FROM users WHERE role = 'QA'")
|
||||
op.execute("DELETE FROM permission_access_logs WHERE role = 'QA'")
|
||||
op.execute("DELETE FROM api_endpoint_permissions WHERE role = 'QA'")
|
||||
op.execute("DELETE FROM study_members WHERE role_in_study = 'QA'")
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE studies
|
||||
SET active_roles = (
|
||||
SELECT COALESCE(json_agg(role), '[]'::json)
|
||||
FROM json_array_elements_text(active_roles) AS role
|
||||
WHERE role <> 'QA'
|
||||
)
|
||||
WHERE active_roles::text LIKE '%QA%'
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
DELETE FROM permission_template_versions
|
||||
WHERE template_id IN (
|
||||
SELECT id FROM permission_templates
|
||||
WHERE category = 'QA' OR recommended_roles = 'QA' OR permissions ? 'QA'
|
||||
)
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
DELETE FROM permission_templates
|
||||
WHERE category = 'QA' OR recommended_roles = 'QA' OR permissions ? 'QA'
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE permission_templates
|
||||
SET created_by = NULL
|
||||
WHERE created_by IN (SELECT id FROM qa_user_ids)
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
DELETE FROM audit_logs
|
||||
WHERE operator_role = 'QA'
|
||||
OR operator_id IN (SELECT id FROM users WHERE role = 'QA')
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
DO $$
|
||||
DECLARE
|
||||
rel record;
|
||||
BEGIN
|
||||
FOR rel IN
|
||||
SELECT
|
||||
quote_ident(ns.nspname) AS schema_name,
|
||||
quote_ident(cls.relname) AS table_name,
|
||||
quote_ident(att.attname) AS column_name,
|
||||
att.attnotnull AS not_null
|
||||
FROM pg_constraint con
|
||||
JOIN pg_class cls ON cls.oid = con.conrelid
|
||||
JOIN pg_namespace ns ON ns.oid = cls.relnamespace
|
||||
JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = ANY(con.conkey)
|
||||
WHERE con.contype = 'f'
|
||||
AND con.confrelid = 'users'::regclass
|
||||
AND array_length(con.conkey, 1) = 1
|
||||
LOOP
|
||||
IF rel.table_name = 'users' AND rel.column_name = 'id' THEN
|
||||
CONTINUE;
|
||||
END IF;
|
||||
|
||||
IF rel.not_null THEN
|
||||
EXECUTE format(
|
||||
'DELETE FROM %s.%s WHERE %s IN (SELECT id FROM qa_user_ids)',
|
||||
rel.schema_name,
|
||||
rel.table_name,
|
||||
rel.column_name
|
||||
);
|
||||
ELSE
|
||||
EXECUTE format(
|
||||
'UPDATE %s.%s SET %s = NULL WHERE %s IN (SELECT id FROM qa_user_ids)',
|
||||
rel.schema_name,
|
||||
rel.table_name,
|
||||
rel.column_name,
|
||||
rel.column_name
|
||||
);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$;
|
||||
"""
|
||||
)
|
||||
|
||||
# Delete QA users after dependent project-role and audit rows have been removed.
|
||||
op.execute("DELETE FROM users WHERE id IN (SELECT id FROM qa_user_ids)")
|
||||
op.execute("DROP TABLE qa_user_ids")
|
||||
|
||||
allowed = ", ".join(f"'{role}'" for role in USER_ROLE_VALUES)
|
||||
op.execute(
|
||||
f"""
|
||||
ALTER TABLE users
|
||||
ALTER COLUMN role TYPE text
|
||||
USING role::text
|
||||
"""
|
||||
)
|
||||
op.execute("DROP TYPE user_role")
|
||||
op.execute(f"CREATE TYPE user_role AS ENUM ({allowed})")
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE users
|
||||
ALTER COLUMN role TYPE user_role
|
||||
USING role::user_role
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
allowed = ", ".join(f"'{role}'" for role in (*USER_ROLE_VALUES, "QA"))
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE users
|
||||
ALTER COLUMN role TYPE text
|
||||
USING role::text
|
||||
"""
|
||||
)
|
||||
op.execute("DROP TYPE user_role")
|
||||
op.execute(f"CREATE TYPE user_role AS ENUM ({allowed})")
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE users
|
||||
ALTER COLUMN role TYPE user_role
|
||||
USING role::user_role
|
||||
"""
|
||||
)
|
||||
Reference in New Issue
Block a user