后端: - User.is_observer BooleanField (0016 migration, default=False) - AdminAuditLog 加 user_observer_toggle 操作类型 - UserSerializer fields 含 is_observer (/auth/me 透出) - IsSuperAdminOrObserver permission 类:超管 + (is_team_admin && is_observer) - 3 个 assets endpoint (overview/team_members/user_videos) 权限从 IsSuperAdmin 改为 IsSuperAdminOrObserver - admin_user_observer_toggle_view (PATCH /admin/users/<id>/observer): 仅超管,只允许打在团管上,拒超管自己 + 拒成员 - admin_users_list_view 返回 is_team_owner/is_observer 字段(前端 row-level 判断用) 前端: - User/AdminUser/TeamMember type 加 is_observer - adminApi.toggleUserObserver - ProtectedRoute 新 requireAdminOrObserver prop + requireAdmin 智能 fallback(团管被拒回 /team/dashboard) - App.tsx /admin 父路由 requireAdminOrObserver,子路由除 assets 外仍 requireAdmin (race 防御) - RoleAwareAdminIndexRedirect:观察者团管入 /admin 跳 /admin/assets,超管跳 /admin/dashboard - AdminLayout sidebar 角色过滤:观察者只见「内容资产」+ 「返回首页」改「返回团队管理」+ logo「观察者」字样 - TeamAdminLayout 观察者团管加「全局资产」入口跳 /admin/assets - AdminAssetsPage 4 处 ¥ 条件渲染 (hideMoney = role !== 'super_admin') - UsersPage 行加「设为观察者/取消观察者」按钮(仅 is_team_admin && team_id) + 观察者 badge - toast 提示「需该用户重新登录后生效」(JWT 不缓存 is_observer claim) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
from rest_framework.permissions import BasePermission
|
||
|
||
|
||
class IsSuperAdmin(BasePermission):
|
||
"""超级管理员:is_staff=True 且 team=NULL"""
|
||
def has_permission(self, request, view):
|
||
return (
|
||
request.user
|
||
and request.user.is_authenticated
|
||
and request.user.is_staff
|
||
and request.user.team is None
|
||
)
|
||
|
||
|
||
class IsTeamAdmin(BasePermission):
|
||
"""团队管理员:is_team_admin=True 且 team≠NULL"""
|
||
def has_permission(self, request, view):
|
||
return (
|
||
request.user
|
||
and request.user.is_authenticated
|
||
and request.user.is_team_admin
|
||
and request.user.team is not None
|
||
)
|
||
|
||
|
||
class IsTeamAdminOrSuperAdmin(BasePermission):
|
||
"""团队管理员或超级管理员"""
|
||
def has_permission(self, request, view):
|
||
if not (request.user and request.user.is_authenticated):
|
||
return False
|
||
if request.user.is_staff and request.user.team is None:
|
||
return True
|
||
if request.user.is_team_admin and request.user.team is not None:
|
||
return True
|
||
return False
|
||
|
||
|
||
class IsTeamMember(BasePermission):
|
||
"""团队成员(含团管):team≠NULL"""
|
||
def has_permission(self, request, view):
|
||
return (
|
||
request.user
|
||
and request.user.is_authenticated
|
||
and request.user.team is not None
|
||
)
|
||
|
||
|
||
class IsSuperAdminOrObserver(BasePermission):
|
||
"""超级管理员,或被标记为观察者的团队管理员(可查看全局内容资产)。"""
|
||
def has_permission(self, request, view):
|
||
u = request.user
|
||
if not (u and u.is_authenticated):
|
||
return False
|
||
# 超管
|
||
if u.is_staff and u.team is None:
|
||
return True
|
||
# 观察者团管
|
||
if u.is_team_admin and u.team is not None and getattr(u, 'is_observer', False):
|
||
return True
|
||
return False
|