fix: 停用集团任职同时摘掉 IsSuperuser,杜绝超管绕过
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
"""admin_assignment 任职 CRUD(修复停用不生效、重复插入)。"""
|
||||
"""admin_assignment 任职 CRUD(修复停用不生效、重复插入、IsSuperuser 绕过)。"""
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
|
||||
from jituan.constants import DATA_SCOPE_ALL, DATA_SCOPE_SINGLE
|
||||
from jituan.constants import DATA_SCOPE_ALL, DATA_SCOPE_SINGLE, SUPER_ADMIN_PHONES
|
||||
from jituan.models import AdminAssignment
|
||||
|
||||
GROUP_ROLE_CODES = frozenset({
|
||||
'GROUP_OWNER',
|
||||
'GROUP_SUPER_ADMIN',
|
||||
'GROUP_AFTER_SALES',
|
||||
'GROUP_FINANCE',
|
||||
'GROUP_CONFIG',
|
||||
})
|
||||
|
||||
|
||||
def _club_filter(qs, club_id):
|
||||
if club_id is None or club_id == '':
|
||||
@@ -53,7 +61,6 @@ def create_or_reactivate_assignment(
|
||||
if is_primary:
|
||||
_clear_other_primary(yonghuid)
|
||||
|
||||
# MySQL 下 club_id=NULL 唯一约束不生效,可能有多条停用重复行,全部复活改只复活最新一条
|
||||
inactive_qs = _club_filter(
|
||||
AdminAssignment.objects.filter(yonghuid=yonghuid, role_code=role_code).exclude(status=1),
|
||||
club_id,
|
||||
@@ -62,7 +69,6 @@ def create_or_reactivate_assignment(
|
||||
if inactive:
|
||||
if is_primary:
|
||||
_clear_other_primary(yonghuid)
|
||||
# 同键其它停用重复行保持停用,只启用一条
|
||||
inactive_qs.exclude(pk=inactive.pk).update(status=0, UpdateTime=timezone.now())
|
||||
AdminAssignment.objects.filter(pk=inactive.id).update(
|
||||
status=1,
|
||||
@@ -85,10 +91,47 @@ def create_or_reactivate_assignment(
|
||||
return row.id, None
|
||||
|
||||
|
||||
def _strip_system_super_if_needed(yonghuid):
|
||||
"""
|
||||
停用集团权限后:非白名单账号必须摘掉 IsSuperuser。
|
||||
否则 build_admin_club_context 仍把 is_group_admin=True,停用等于没停。
|
||||
"""
|
||||
yonghuid = (yonghuid or '').strip()
|
||||
if not yonghuid:
|
||||
return
|
||||
try:
|
||||
from users.business_models import User
|
||||
except Exception:
|
||||
return
|
||||
user = User.objects.filter(UserUID=yonghuid).first()
|
||||
if not user:
|
||||
return
|
||||
if (user.Phone or '') in SUPER_ADMIN_PHONES:
|
||||
return
|
||||
if user.IsSuperuser:
|
||||
user.IsSuperuser = False
|
||||
user.save(update_fields=['IsSuperuser'])
|
||||
|
||||
|
||||
def deactivate_all_group_assignments_for_user(yonghuid):
|
||||
"""停用该用户全部集团级任职(空俱乐部 / ALL_CLUBS / GROUP_* 角色)。"""
|
||||
yonghuid = (yonghuid or '').strip()
|
||||
if not yonghuid:
|
||||
return 0
|
||||
qs = AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).filter(
|
||||
Q(club_id__isnull=True)
|
||||
| Q(club_id='')
|
||||
| Q(data_scope=DATA_SCOPE_ALL)
|
||||
| Q(role_code__in=list(GROUP_ROLE_CODES))
|
||||
)
|
||||
return qs.update(status=0, UpdateTime=timezone.now())
|
||||
|
||||
|
||||
def deactivate_assignment(assignment_id):
|
||||
"""
|
||||
停用任职。必须连同「同人+同角色+同俱乐部」的全部有效行一起停用:
|
||||
MySQL 对 club_id=NULL 的 unique 不生效,集团任职常会积多条,只停一条等于没停。
|
||||
停用任职。
|
||||
- 同人同角色同俱乐部(含 NULL 重复行)全部停
|
||||
- 若是集团级任职:再停掉该人全部集团任职,并摘掉非白名单 IsSuperuser
|
||||
"""
|
||||
try:
|
||||
pk = int(assignment_id)
|
||||
@@ -105,22 +148,31 @@ def deactivate_assignment(assignment_id):
|
||||
status=1,
|
||||
)
|
||||
qs = _club_filter(qs, row.club_id)
|
||||
updated = qs.update(status=0, UpdateTime=timezone.now())
|
||||
# 目标行若已是停用,仍视为成功(幂等)
|
||||
if updated or int(row.status or 0) != 1:
|
||||
return True, None
|
||||
qs.update(status=0, UpdateTime=timezone.now())
|
||||
|
||||
is_group_level = (
|
||||
not (row.club_id or '').strip()
|
||||
or (row.data_scope or '') == DATA_SCOPE_ALL
|
||||
or (row.role_code or '') in GROUP_ROLE_CODES
|
||||
)
|
||||
if is_group_level:
|
||||
deactivate_all_group_assignments_for_user(row.yonghuid)
|
||||
_strip_system_super_if_needed(row.yonghuid)
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def deactivate_all_assignments_for_user(yonghuid):
|
||||
"""账号禁用时:停用该用户全部有效任职。"""
|
||||
"""账号禁用时:停用该用户全部有效任职,并摘掉非白名单 IsSuperuser。"""
|
||||
yonghuid = (yonghuid or '').strip()
|
||||
if not yonghuid:
|
||||
return 0
|
||||
return AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).update(
|
||||
n = AdminAssignment.objects.filter(yonghuid=yonghuid, status=1).update(
|
||||
status=0,
|
||||
UpdateTime=timezone.now(),
|
||||
)
|
||||
_strip_system_super_if_needed(yonghuid)
|
||||
return n
|
||||
|
||||
|
||||
def dedupe_active_assignments():
|
||||
|
||||
@@ -149,12 +149,15 @@ def build_admin_club_context(user):
|
||||
|
||||
|
||||
def is_system_super_admin(user):
|
||||
"""系统级超管(Django 超管 / admin 账号 / 白名单手机号),非俱乐部 000001 角色。"""
|
||||
return (
|
||||
bool(user.IsSuperuser)
|
||||
or user.UserType == 'admin'
|
||||
or getattr(user, 'Phone', '') in SUPER_ADMIN_PHONES
|
||||
)
|
||||
"""
|
||||
系统级超管。
|
||||
白名单手机号:始终超管。
|
||||
其它账号:仅看 IsSuperuser / UserType=admin;停用集团任职时会清掉 IsSuperuser,
|
||||
避免「任职停了仍是集团上帝」。
|
||||
"""
|
||||
if getattr(user, 'Phone', '') in SUPER_ADMIN_PHONES:
|
||||
return True
|
||||
return bool(user.IsSuperuser) or user.UserType == 'admin'
|
||||
|
||||
|
||||
def is_hardcoded_super_phone(user):
|
||||
|
||||
Reference in New Issue
Block a user