Separate cross-team selection from attribution maintenance and apply the same scope to options, history filters and writes. Add an opt-in tenant permission without granting existing roles. Protect authorization mutation paths against self-grants, validate tenant targets and links, and include menu ancestors atomically. Cover role union, revocation, mobile writes and administration boundaries with regression tests.
36 lines
1.5 KiB
Python
36 lines
1.5 KiB
Python
"""Validate authorization targets before any role/menu relationship is changed."""
|
|
from app.models.models import SysMenu
|
|
|
|
|
|
def validate_tenant_references(db, model, ids, tenant_no):
|
|
"""Reject deleted, foreign or missing targets without writing partial grants."""
|
|
ids = list(dict.fromkeys(ids or []))
|
|
if not ids:
|
|
return
|
|
found = {row.id for row in db.query(model.id).filter(
|
|
model.id.in_(ids), model.tenant_no == tenant_no, model.del_flag == 0)}
|
|
if found != set(ids):
|
|
raise ValueError('关联记录不存在或不属于当前租户')
|
|
|
|
|
|
def menu_ids_with_ancestors(db, menu_ids, tenant_no):
|
|
"""Include only each selected node's same-tenant ancestors, up to its own root."""
|
|
menus = {row.id: row for row in db.query(SysMenu).filter(
|
|
SysMenu.tenant_no == tenant_no, SysMenu.del_flag == 0)}
|
|
result, included = [], set()
|
|
for selected in menu_ids:
|
|
current, path = selected, set()
|
|
while current and current != '0':
|
|
if current in path:
|
|
raise ValueError('菜单存在循环父级,不能授权')
|
|
if current not in menus:
|
|
raise ValueError('菜单或祖先不存在或不属于当前租户')
|
|
path.add(current)
|
|
if current not in included:
|
|
result.append(current)
|
|
included.add(current)
|
|
current = menus[current].parent_id
|
|
if not path:
|
|
raise ValueError('菜单不存在或不属于当前租户')
|
|
return result
|