library-picturebook-activity/docs/migration/rbac-audit.sql

138 lines
4.3 KiB
MySQL
Raw Normal View History

2026-04-01 19:30:33 +08:00
-- =========================================================
-- RBAC/菜单/权限 数据审计(只读)
-- 目标:快速定位“已授权但菜单为空 / 权限缺失 / 绑定缺失”等问题
-- 说明:不做任何写操作,可在生产环境安全执行
-- =========================================================
-- 0) 基础:租户列表
SELECT id, code, name, tenant_type, is_super, valid_state, deleted
FROM t_sys_tenant
WHERE deleted = 0
ORDER BY id;
-- 1) 角色存在但未做菜单授权role_menu 为空)
-- 重点tenant_admin / school_admin / teacher / student / judge 等关键角色
SELECT
r.tenant_id,
t.code AS tenant_code,
r.id AS role_id,
r.code AS role_code,
r.name AS role_name
FROM t_auth_role r
JOIN t_sys_tenant t ON t.id = r.tenant_id AND t.deleted = 0
LEFT JOIN t_auth_role_menu rm ON rm.role_id = r.id
WHERE r.deleted = 0
AND r.valid_state = 1
GROUP BY r.tenant_id, t.code, r.id, r.code, r.name
HAVING COUNT(rm.id) = 0
ORDER BY r.tenant_id, r.code;
-- 2) 菜单表中声明的 permission 在某些租户的 permission 表缺失
-- 由于 t_auth_menu 无 tenant_idpermission code 应在每个租户的 t_auth_permission 中都存在(除 super_admin 这种全局码也同理)
SELECT
t.id AS tenant_id,
t.code AS tenant_code,
m.permission AS menu_permission_code
FROM t_sys_tenant t
JOIN (
SELECT DISTINCT permission
FROM t_auth_menu
WHERE deleted = 0
AND valid_state = 1
AND permission IS NOT NULL
AND permission <> ''
) m ON 1 = 1
LEFT JOIN t_auth_permission p
ON p.tenant_id = t.id
AND p.code = m.permission
AND p.deleted = 0
AND p.valid_state = 1
WHERE t.deleted = 0
AND t.valid_state = 1
AND p.id IS NULL
ORDER BY t.id, m.permission;
-- 3) 关键前端权限码在各租户 permission 表缺失
-- 来自 docs/migration/permission-inventory.md前端提取
SELECT
t.id AS tenant_id,
t.code AS tenant_code,
req.code AS required_code
FROM t_sys_tenant t
JOIN (
SELECT 'activity:read' AS code UNION ALL
SELECT 'config:create' UNION ALL
SELECT 'config:delete' UNION ALL
SELECT 'config:update' UNION ALL
SELECT 'contest:create' UNION ALL
SELECT 'contest:delete' UNION ALL
SELECT 'contest:publish' UNION ALL
SELECT 'contest:read' UNION ALL
SELECT 'contest:update' UNION ALL
SELECT 'dict:create' UNION ALL
SELECT 'dict:delete' UNION ALL
SELECT 'dict:update' UNION ALL
SELECT 'judge:create' UNION ALL
SELECT 'judge:delete' UNION ALL
SELECT 'judge:read' UNION ALL
SELECT 'judge:update' UNION ALL
SELECT 'log:delete' UNION ALL
SELECT 'menu:create' UNION ALL
SELECT 'menu:delete' UNION ALL
SELECT 'menu:read' UNION ALL
SELECT 'menu:update' UNION ALL
SELECT 'notice:create' UNION ALL
SELECT 'notice:delete' UNION ALL
SELECT 'notice:update' UNION ALL
SELECT 'permission:read' UNION ALL
SELECT 'registration:read' UNION ALL
SELECT 'review:score' UNION ALL
SELECT 'role:create' UNION ALL
SELECT 'role:delete' UNION ALL
SELECT 'role:read' UNION ALL
SELECT 'role:update' UNION ALL
SELECT 'tenant:create' UNION ALL
SELECT 'tenant:delete' UNION ALL
SELECT 'tenant:update' UNION ALL
SELECT 'work:read'
) req ON 1 = 1
LEFT JOIN t_auth_permission p
ON p.tenant_id = t.id
AND p.code = req.code
AND p.deleted = 0
AND p.valid_state = 1
WHERE t.deleted = 0
AND t.valid_state = 1
AND p.id IS NULL
ORDER BY t.id, req.code;
-- 4) 角色拥有菜单,但缺少对应 permission可能导致“菜单看得到但点进去 403 / 按钮缺失”)
-- 规则:若菜单 permission 非空,则角色至少要拥有该 permissionrole_permission
SELECT
r.tenant_id,
t.code AS tenant_code,
r.id AS role_id,
r.code AS role_code,
m.id AS menu_id,
m.name AS menu_name,
m.permission AS menu_permission_code
FROM t_auth_role r
JOIN t_sys_tenant t ON t.id = r.tenant_id AND t.deleted = 0
JOIN t_auth_role_menu rm ON rm.role_id = r.id
JOIN t_auth_menu m ON m.id = rm.menu_id AND m.deleted = 0 AND m.valid_state = 1
LEFT JOIN t_auth_permission p
ON p.tenant_id = r.tenant_id
AND p.code = m.permission
AND p.deleted = 0
AND p.valid_state = 1
LEFT JOIN t_auth_role_permission rp
ON rp.role_id = r.id
AND rp.permission_id = p.id
WHERE r.deleted = 0
AND r.valid_state = 1
AND m.permission IS NOT NULL
AND m.permission <> ''
AND (p.id IS NULL OR rp.id IS NULL)
ORDER BY r.tenant_id, r.code, m.id;