77 lines
3.8 KiB
Python
77 lines
3.8 KiB
Python
"""用实际授权 INSERT 验证迁移:停用/隐藏入口不得派生维护权限。"""
|
|
from pathlib import Path
|
|
import re
|
|
import sqlite3
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
|
|
MIGRATION = Path(__file__).resolve().parents[2] / 'docs/sql/migrations/2026-10-26-manual-dispatch-calendar.sql'
|
|
|
|
|
|
def database(prefix, *, existing_target=True):
|
|
db = sqlite3.connect(':memory:')
|
|
db.create_function('UUID', 0, lambda: str(uuid4()))
|
|
db.create_function('NOW', 0, lambda: '2026-10-26 12:00:00')
|
|
common = "tenant_no TEXT DEFAULT 't1', order_num INT, remark TEXT, create_by TEXT, create_dept_id TEXT, create_time TEXT, update_time TEXT, del_flag INT DEFAULT 0"
|
|
db.execute(f"CREATE TABLE sys_menu (id TEXT PRIMARY KEY, menu_name TEXT, parent_id TEXT DEFAULT '0', menu_type TEXT, path TEXT, component TEXT, perms TEXT, is_frame INT, visible TEXT DEFAULT '1', status TEXT DEFAULT '1', {common})")
|
|
db.execute(f"CREATE TABLE sys_role (id TEXT PRIMARY KEY, status TEXT DEFAULT '1', {common})")
|
|
db.execute(f"CREATE TABLE sys_role_menu (id TEXT PRIMARY KEY, role_id TEXT, menu_id TEXT, {common})")
|
|
def menu(record_id, parent_id, path=None, perms=None):
|
|
db.execute('INSERT INTO sys_menu (id,parent_id,path,perms) VALUES (?,?,?,?)', (record_id, parent_id, path, perms))
|
|
is_web = prefix == 'pl'
|
|
menu('root', '0', '/web' if is_web else '/mini')
|
|
menu('parent', 'root', '/pl' if is_web else '/mini/pl')
|
|
if is_web:
|
|
menu('folder', 'parent', 'calendar-module')
|
|
menu('leaf', 'folder' if is_web else 'parent', perms=prefix + ':dispatch-calendar:list')
|
|
menu('source', 'parent', perms='pl:plan:list' if is_web else 'mini:pl:plan:manage')
|
|
if existing_target:
|
|
menu('target', 'leaf', perms=prefix + ':dispatch-calendar:manage')
|
|
db.execute("INSERT INTO sys_role (id) VALUES ('r1')")
|
|
db.execute("INSERT INTO sys_role_menu (id,role_id,menu_id) VALUES ('calendar-grant','r1','leaf')")
|
|
db.execute("INSERT INTO sys_role_menu (id,role_id,menu_id) VALUES ('source-grant','r1','source')")
|
|
return db
|
|
|
|
|
|
def migrate(db):
|
|
for sql in re.findall(r'INSERT INTO sys_(?:menu|role_menu)\s*\(.*?;', MIGRATION.read_text(), re.S):
|
|
db.execute(sql)
|
|
|
|
|
|
def grants(db):
|
|
return db.execute("SELECT COUNT(*) FROM sys_role_menu rm JOIN sys_menu m ON m.id=rm.menu_id WHERE m.perms LIKE '%dispatch-calendar:manage' AND rm.del_flag=0").fetchone()[0]
|
|
|
|
|
|
@pytest.mark.parametrize('prefix,part', [(prefix, part) for prefix in ['pl', 'mini:pl']
|
|
for part in ['source', 'leaf', 'parent', 'root', 'target']]
|
|
+ [('pl', 'folder')])
|
|
@pytest.mark.parametrize('field', ['status', 'visible'])
|
|
def test_inactive_menu_cannot_grant_manage(prefix, part, field):
|
|
with database(prefix) as db:
|
|
db.execute(f'UPDATE sys_menu SET {field}=? WHERE id=?', ('0', part))
|
|
migrate(db)
|
|
assert grants(db) == 0
|
|
|
|
|
|
@pytest.mark.parametrize('prefix', ['pl', 'mini:pl'])
|
|
def test_active_permissions_are_granted_once_and_revocation_is_preserved(prefix):
|
|
with database(prefix, existing_target=False) as db:
|
|
migrate(db)
|
|
migrate(db)
|
|
assert grants(db) == 1
|
|
db.execute("UPDATE sys_role_menu SET del_flag=1 WHERE menu_id IN (SELECT id FROM sys_menu WHERE perms LIKE '%dispatch-calendar:manage')")
|
|
migrate(db)
|
|
assert grants(db) == 0
|
|
assert db.execute("SELECT COUNT(*) FROM sys_role_menu WHERE del_flag=1").fetchone()[0] == 1
|
|
|
|
|
|
@pytest.mark.parametrize('prefix', ['pl', 'mini:pl'])
|
|
@pytest.mark.parametrize('part', ['leaf', 'parent', 'root'])
|
|
def test_inactive_calendar_ancestors_do_not_create_manage_menu(prefix, part):
|
|
with database(prefix, existing_target=False) as db:
|
|
db.execute('UPDATE sys_menu SET status=? WHERE id=?', ('0', part))
|
|
migrate(db)
|
|
assert db.execute("SELECT COUNT(*) FROM sys_menu WHERE perms LIKE '%dispatch-calendar:manage'").fetchone()[0] == 0
|