From 23ec78e83d037ec6630a21296babc5eedf6c748e Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 28 Mar 2026 22:31:24 +0800 Subject: [PATCH] fix: save/restore policies with correct scope (global vs project-level) - Disable now saves both global and project-level policies with scope info - Restore puts policies back in original scope (global or project) - Project list view now syncs policies from Volcengine in real-time - Fixes: policies incorrectly restored as global after disable/enable Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/apps/monitor/views.py | 56 +++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/backend/apps/monitor/views.py b/backend/apps/monitor/views.py index feeb4c8..c059ef1 100644 --- a/backend/apps/monitor/views.py +++ b/backend/apps/monitor/views.py @@ -569,6 +569,8 @@ def iam_user_disable_view(request, pk): # 2. 移除所有权限策略并保存快照(恢复时加回) saved_policies = [] detach_errors = [] + + # 2a. 全局策略 try: resp = svc.list_attached_user_policies(user.username) policies = resp.get("Result", {}).get("AttachedPolicyMetadata", []) @@ -577,12 +579,31 @@ def iam_user_disable_view(request, pk): ptype = p.get("PolicyType", "") try: svc.detach_user_policy(user.username, pname, ptype) - saved_policies.append({"name": pname, "type": ptype}) + saved_policies.append({"name": pname, "type": ptype, "scope": "global"}) except VolcengineAPIError as detach_err: - detach_errors.append(f"{pname}: {detach_err}") + detach_errors.append(f"{pname}(global): {detach_err}") except VolcengineAPIError: pass + # 2b. 项目级策略 + for proj in user.projects.all(): + try: + resp = svc.client.call('ListAttachedUserPolicies', { + 'UserName': user.username, + 'ProjectName': proj.project_name, + }) + proj_policies = resp.get("Result", {}).get("AttachedPolicyMetadata", []) + for p in proj_policies: + pname = p.get("PolicyName", "") + ptype = p.get("PolicyType", "") + try: + svc.detach_policy_in_project(user.username, pname, proj.project_name, ptype) + saved_policies.append({"name": pname, "type": ptype, "scope": "project", "project": proj.project_name}) + except VolcengineAPIError as detach_err: + detach_errors.append(f"{pname}({proj.project_name}): {detach_err}") + except VolcengineAPIError: + pass + user.status = IAMUser.Status.DISABLED # 在策略快照里记住停用前的火山登录状态 saved_policies.append({"_volc_login_was": user.volc_login_allowed}) @@ -635,12 +656,15 @@ def iam_user_enable_view(request, pk): # 1. 恢复 API 密钥 + 控制台(按停用前状态) svc.enable_user(user.username, restore_login=restore_login) - # 2. 重新附加停用时保存的策略 + # 2. 重新附加停用时保存的策略(按原始位置:全局或项目级) restored_count = 0 restore_errors = [] for p in actual_policies: try: - svc.attach_user_policy(user.username, p["name"], p["type"]) + if p.get("scope") == "project" and p.get("project"): + svc.attach_policy_in_project(user.username, p["name"], p["project"], p["type"]) + else: + svc.attach_user_policy(user.username, p["name"], p["type"]) restored_count += 1 except VolcengineAPIError as restore_err: restore_errors.append(f"{p['name']}: {restore_err}") @@ -762,12 +786,34 @@ def iam_user_detach_policy_view(request, pk): @api_view(['GET']) def iam_user_project_list_view(request, pk): - """查看子账号关联的项目列表""" + """查看子账号关联的项目列表(实时从火山同步项目级策略)""" try: user = IAMUser.objects.get(pk=pk) except IAMUser.DoesNotExist: return Response({'error': 'not_found'}, status=status.HTTP_404_NOT_FOUND) + projects = user.projects.all() + + # 实时从火山查询每个项目的策略,同步到本地 + account, ak, sk = _get_volc_account(user.volc_account_id) + if ak: + svc = IAMService(ak, sk) + for proj in projects: + try: + resp = svc.client.call('ListAttachedUserPolicies', { + 'UserName': user.username, + 'ProjectName': proj.project_name, + }) + volc_policies = [ + p.get('PolicyName', '') + for p in resp.get('Result', {}).get('AttachedPolicyMetadata', []) + ] + if set(volc_policies) != set(proj.attached_policies or []): + proj.attached_policies = volc_policies + proj.save(update_fields=['attached_policies']) + except Exception: + pass + return Response(IAMUserProjectSerializer(projects, many=True).data)