From 94cb71e82ab7166196e58e0413b3677de17fa3da Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 18:23:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E6=96=B0=E5=A2=9E=20IsCompanyAdm?= =?UTF-8?q?inUser=20=E6=9D=83=E9=99=90=E7=B1=BB=E5=B9=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=BE=85=E5=AE=A1=E5=88=97=E8=A1=A8=E8=B7=A8=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - permissions.py 新增 IsCompanyAdminUser(admin:company / admin:tenant / admin:super) - ListPendingApplications 移除被静默忽略的 TenantUUID 形参,改为显式 AllowedCompanyUUIDs - 过滤在 SQL 侧用子查询完成,规避 UUIDObj 与 bytes 混比恒为 False 的坑 --- auth/managers.py | 26 +++++++++++++++++++++++--- auth/permissions.py | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/auth/managers.py b/auth/managers.py index 73a5399..a3aef10 100644 --- a/auth/managers.py +++ b/auth/managers.py @@ -3257,12 +3257,32 @@ class AppManager: return True @staticmethod - def ListPendingApplications(TenantUUID): + def ListPendingApplications(AllowedCompanyUUIDs=None): """待审批的个人授权申请(ApplyStatus=1,按申请时间倒序) - 说明:UserApp 表无租户列,租户隔离由上层承担(保持既有行为)。 + 数据范围:UserApp 表无租户/公司列,作用域按「申请人所属公司」反查 + (UserRole.CompanyUUID)。由调用方按管理员级别显式传参: + - ``AllowedCompanyUUIDs=None`` → 不限制(平台管理员 admin:super) + - ``AllowedCompanyUUIDs=[]`` → 无任何可管辖公司,返回空 + - ``AllowedCompanyUUIDs=[...]`` → 仅申请人的公司命中该集合的申请 + + 注意:此前签名是 ``(TenantUUID)`` 但**传入值被完全忽略**,导致任何租户 + 管理员都能看到全平台待审申请(跨租户泄漏)。现改为显式范围参数,不再 + 接受会被静默忽略的租户参数。 """ - return list(UserApp.objects.filter(ApplyStatus=1).order_by('-ApplyTime')) + qs = UserApp.objects.filter(ApplyStatus=1).order_by('-ApplyTime') + if AllowedCompanyUUIDs is not None: + allowed = [c for c in AllowedCompanyUUIDs if c] + if not allowed: + return [] + # 用子查询在 SQL 侧完成「申请人属于可管辖公司」过滤,避免 Python 侧 + # UUIDObj/bytes 混比(两者 hash 不同,曾因此导致判定恒为 False) + qs = qs.filter( + UserUUID__in=UserRole.objects.filter( + CompanyUUID__in=allowed, + ).values('UserUUID') + ) + return list(qs) # ==================== 应用配置(AppConfig 键值表) ==================== diff --git a/auth/permissions.py b/auth/permissions.py index 25daf9d..893df19 100644 --- a/auth/permissions.py +++ b/auth/permissions.py @@ -53,6 +53,28 @@ class IsAdminUser(BasePermission): return IsAdmin(request) +class IsCompanyAdminUser(BasePermission): + """仅允许公司管理员及以上访问(admin:company / admin:tenant / admin:super) + + 三级管理员体系(由窄到宽): + - ``admin:company``:公司管理员,按 RolePermission.CompanyUUID 绑定到具体公司后生效 + - ``admin:tenant`` :租户内管理员,自动继承公司管理员能力 + - ``admin:super`` :跨租户管理员,天然具备 + + 判定委托给 ``gvsdsdk.auth.admin_service.IsCompanyAdmin``。本类只负责"能不能进", + 数据范围收窄(本公司/本租户/全部)由视图按 IsSuperAdmin/IsTenantAdmin 另行计算。 + """ + + def has_permission(self, request, view): + user = request.user + if not user or not user.is_authenticated: + return False + if not isinstance(user, _GetUserModel()): + return False + from gvsdsdk.auth.admin_service import IsCompanyAdmin + return IsCompanyAdmin(request) + + class IsCompanyMember(BasePermission): """验证用户当前角色属于目标数据所在公司