156 lines
6.4 KiB
Python
156 lines
6.4 KiB
Python
"""gvsdsdk.auth.admin_service — 管理员身份统一判定服务
|
||
|
||
提供管理员身份判定的统一入口,基于两个层级的管理员权限码:
|
||
|
||
``admin:super`` —— 跨租户管理员(如 GERP 测试员),关闭租户隔离 + 公司隔离
|
||
``admin:tenant`` —— 租户内管理员(如 集团超级管理员),仅关闭公司隔离,保留租户隔离
|
||
|
||
判定顺序(适用于所有判定函数):
|
||
1. 用户未认证 → False
|
||
2. 有当前角色 → 仅依据该角色的权限码判定(角色超级管理员模式)
|
||
超级用户切换到普通角色时也应用隔离,不再因 IsSuperuser/IsStaff 直接放行
|
||
3. 无当前角色 → IsSuperuser 或 IsStaff 作为系统维护回退(仅授予 admin:tenant 级别,
|
||
不授予 admin:super,防止超级用户在未切换角色时获得跨租户越权能力)
|
||
4. 否则 → False
|
||
|
||
提供的函数:
|
||
``IsAdmin`` —— 是否为管理员(admin:super 或 admin:tenant),用于权限校验
|
||
``IsSuperAdmin`` —— 是否为跨租户管理员(仅 admin:super),用于租户隔离判定
|
||
``IsTenantAdmin``—— 是否为租户内管理员(仅 admin:tenant),用于租户内管理员判定
|
||
|
||
用法:
|
||
from gvsdsdk.auth.admin_service import IsAdmin, IsSuperAdmin
|
||
|
||
if IsAdmin(request):
|
||
... # 权限校验放行
|
||
|
||
if IsSuperAdmin(request):
|
||
... # 关闭租户隔离
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging as _logging
|
||
|
||
_logger = _logging.getLogger(__name__)
|
||
|
||
|
||
def _IsAuthenticated(user) -> bool:
|
||
"""兼容 gvsdsdk User(IsAuthenticated 属性)与 Django User(is_authenticated 属性)"""
|
||
if user is None:
|
||
return False
|
||
for attr in ('IsAuthenticated', 'is_authenticated'):
|
||
val = getattr(user, attr, None)
|
||
if val:
|
||
return bool(val)
|
||
return False
|
||
|
||
|
||
def _HasPerm(request=None, user=None, role_uuid=None, perm_code=None) -> bool:
|
||
"""判断当前角色是否拥有指定权限码
|
||
|
||
内部函数,供 IsAdmin / IsSuperAdmin / IsTenantAdmin 复用。
|
||
判定顺序:
|
||
1. 用户未认证 → False
|
||
2. 有当前角色 → 仅依据该角色的指定权限码判定(角色超级管理员模式)
|
||
3. 无当前角色 → IsSuperuser 或 IsStaff 作为系统维护回退(仅授予 admin:tenant 级别,
|
||
perm_code='admin:super' 时直接返回 False,防止跨租户越权)
|
||
4. 否则 → False
|
||
"""
|
||
if perm_code is None:
|
||
return False
|
||
|
||
# 1. 解析 user
|
||
if request is not None and user is None:
|
||
user = getattr(request, 'user', None)
|
||
if not _IsAuthenticated(user):
|
||
return False
|
||
|
||
# 2. 解析 role_uuid(优先从参数,其次从 request.current_role)
|
||
if role_uuid is None and request is not None:
|
||
current_role = getattr(request, 'current_role', None)
|
||
if current_role:
|
||
role_uuid = getattr(current_role, 'RoleUUID', None)
|
||
|
||
# 3. 有当前角色时,仅依据该角色的指定权限码判定(角色超级管理员模式)
|
||
if role_uuid:
|
||
try:
|
||
from gvsdsdk.models import RolePermission, Permission
|
||
perm_uuids = Permission.objects.filter(
|
||
PermCode=perm_code,
|
||
PermStatus=1,
|
||
).values_list('PermUUID', flat=True)
|
||
return RolePermission.objects.filter(
|
||
RoleUUID=role_uuid,
|
||
PermUUID__in=perm_uuids,
|
||
).exists()
|
||
except Exception:
|
||
_logger.warning('查询权限 %s 失败', perm_code, exc_info=True)
|
||
return False
|
||
|
||
# 4. 无当前角色时,IsSuperuser/IsStaff 作为系统维护回退(无角色无法做权限判定)
|
||
# 安全策略:无角色时仅授予 admin:tenant 级别权限(不授予 admin:super),
|
||
# 防止超级用户在未切换角色时获得跨租户越权能力。
|
||
# superuser 无角色 → IsAdmin=True / IsTenantAdmin=True / IsSuperAdmin=False
|
||
if perm_code == 'admin:super':
|
||
return False
|
||
return bool(getattr(user, 'IsSuperuser', False) or getattr(user, 'IsStaff', False))
|
||
|
||
|
||
def IsAdmin(request=None, user=None, role_uuid=None) -> bool:
|
||
"""判断用户是否为管理员(admin:super 或 admin:tenant)
|
||
|
||
用于权限校验:两种层级的管理员都应放行。
|
||
跨租户管理员(admin:super)和租户内管理员(admin:tenant)都是管理员。
|
||
|
||
参数:
|
||
request: 请求对象。优先从 ``request.user`` 取用户、``request.current_role`` 取角色。
|
||
user: 用户对象。``request`` 为空时使用此参数。
|
||
role_uuid: 角色 UUID(bytes 或 UUIDObj)。``request`` 为空时使用此参数;
|
||
若 ``request`` 非空则优先使用 ``request.current_role.RoleUUID``。
|
||
|
||
返回:
|
||
bool
|
||
"""
|
||
if _HasPerm(request, user, role_uuid, 'admin:super'):
|
||
return True
|
||
return _HasPerm(request, user, role_uuid, 'admin:tenant')
|
||
|
||
|
||
def IsSuperAdmin(request=None, user=None, role_uuid=None) -> bool:
|
||
"""判断用户是否为跨租户管理员(仅 admin:super)
|
||
|
||
用于隔离控制:仅 admin:super 关闭租户隔离,可跨租户查看数据。
|
||
"""
|
||
return _HasPerm(request, user, role_uuid, 'admin:super')
|
||
|
||
|
||
def IsTenantAdmin(request=None, user=None, role_uuid=None) -> bool:
|
||
"""判断用户是否为租户内管理员(仅 admin:tenant)
|
||
|
||
用于租户内管理员判定:仅关闭公司隔离,保留租户隔离。
|
||
"""
|
||
return _HasPerm(request, user, role_uuid, 'admin:tenant')
|
||
|
||
|
||
def IsCompanyAdmin(request=None, user=None, role_uuid=None) -> bool:
|
||
"""判断用户是否为公司管理员(admin:company / admin:tenant / admin:super)
|
||
|
||
公司管理员权限键 ``admin:company`` 由管理员在角色权限分配时手动勾选,
|
||
绑定到具体公司(RolePermission.CompanyUUID 维度)后,仅在该公司内生效。
|
||
|
||
继承规则(公司管理员能力):
|
||
- admin:super —— 跨租户管理员,天然具备
|
||
- admin:tenant —— 租户内管理员,自动继承(可管理租户下所有公司)
|
||
- admin:company—— 手动授予的角色,需按公司维度绑定
|
||
|
||
返回:
|
||
bool
|
||
"""
|
||
if _HasPerm(request, user, role_uuid, 'admin:company'):
|
||
return True
|
||
# 租户管理员 / 跨租户管理员自动继承公司管理员权限
|
||
if IsAdmin(request=request, user=user, role_uuid=role_uuid):
|
||
return True
|
||
return False
|