From 4b8181b96a11c7d39d8ab78fd01b79018aad3a93 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 28 Mar 2026 22:45:04 +0800 Subject: [PATCH] feat: unified policy overview page for sub-accounts - New page: /iam-users/:id/policies shows all policies in one view - Separated into global policies and per-project policies sections - Each section has inline add/remove with disabled duplicates - Backend: new policies/overview/ endpoint returns global + project policies - Replaces old popup-based policy management Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/apps/monitor/urls.py | 1 + backend/apps/monitor/views.py | 75 ++++++++ frontend/src/router/index.js | 1 + frontend/src/views/iam/IAMUserList.vue | 2 +- frontend/src/views/iam/UserPoliciesView.vue | 198 ++++++++++++++++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 frontend/src/views/iam/UserPoliciesView.vue diff --git a/backend/apps/monitor/urls.py b/backend/apps/monitor/urls.py index 9eeb425..41019d5 100644 --- a/backend/apps/monitor/urls.py +++ b/backend/apps/monitor/urls.py @@ -23,6 +23,7 @@ urlpatterns = [ path('iam-users//disable/', views.iam_user_disable_view), path('iam-users//enable/', views.iam_user_enable_view), path('iam-users//policies/', views.iam_user_policies_view), + path('iam-users//policies/overview/', views.iam_user_policies_overview_view), path('iam-users//policies/attach/', views.iam_user_attach_policy_view), path('iam-users//policies/detach/', views.iam_user_detach_policy_view), # IAM user projects (multi-project) diff --git a/backend/apps/monitor/views.py b/backend/apps/monitor/views.py index 2de7aee..4062d74 100644 --- a/backend/apps/monitor/views.py +++ b/backend/apps/monitor/views.py @@ -695,6 +695,81 @@ def iam_user_enable_view(request, pk): @api_view(['GET']) +@api_view(['GET']) +def iam_user_policies_overview_view(request, pk): + """查看子账号的完整权限总览(全局 + 各项目)""" + try: + user = IAMUser.objects.get(pk=pk) + except IAMUser.DoesNotExist: + return Response({'error': 'not_found'}, status=status.HTTP_404_NOT_FOUND) + + account, ak, sk = _get_volc_account(user.volc_account_id) + if not ak: + return Response({'error': 'no_credentials'}, status=status.HTTP_400_BAD_REQUEST) + + svc = IAMService(ak, sk) + + try: + # Get all policies + resp = svc.list_attached_user_policies(user.username) + all_policies = resp.get("Result", {}).get("AttachedPolicyMetadata", []) + + # Separate global vs project + global_policies = [] + for p in all_policies: + scopes = p.get('PolicyScope', []) + is_global = not scopes or any(s.get('PolicyScopeType') == 'Global' for s in scopes) + if is_global: + global_policies.append({ + 'name': p.get('PolicyName', ''), + 'type': p.get('PolicyType', ''), + 'description': p.get('Description', ''), + }) + + # Get project-level policies for each associated project + project_policies = [] + for proj in user.projects.all(): + try: + resp2 = svc.client.call('ListAttachedUserPolicies', { + 'UserName': user.username, + 'ProjectName': proj.project_name, + }) + proj_items = [] + for p in resp2.get('Result', {}).get('AttachedPolicyMetadata', []): + scopes = p.get('PolicyScope', []) + for s in scopes: + if s.get('PolicyScopeType') == 'Project' and s.get('ProjectName') == proj.project_name: + proj_items.append({ + 'name': p.get('PolicyName', ''), + 'type': p.get('PolicyType', ''), + 'description': p.get('Description', ''), + }) + break + project_policies.append({ + 'project_name': proj.project_name, + 'display_name': proj.display_name, + 'project_id': proj.id, + 'policies': proj_items, + }) + except Exception: + project_policies.append({ + 'project_name': proj.project_name, + 'display_name': proj.display_name, + 'project_id': proj.id, + 'policies': [], + }) + + return Response({ + 'username': user.username, + 'display_name': user.display_name, + 'global_policies': global_policies, + 'project_policies': project_policies, + }) + except VolcengineAPIError as e: + return Response({'error': 'api_error', 'message': str(e)}, + status=status.HTTP_502_BAD_GATEWAY) + + def iam_user_policies_view(request, pk): """查看子账号的权限策略""" try: diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 89b96b2..2c83cfd 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -21,6 +21,7 @@ const routes = [ // Admin routes { path: 'dashboard', name: 'Dashboard', component: () => import('../views/dashboard/DashboardView.vue') }, { path: 'iam-users', name: 'IAMUsers', component: () => import('../views/iam/IAMUserList.vue') }, + { path: 'iam-users/:id/policies', name: 'UserPolicies', component: () => import('../views/iam/UserPoliciesView.vue'), props: true }, { path: 'billing', name: 'Billing', component: () => import('../views/billing/BillingView.vue') }, { path: 'alerts', name: 'Alerts', component: () => import('../views/alerts/AlertList.vue') }, { path: 'ark-keys', name: 'ArkKeys', component: () => import('../views/ark/ArkKeysView.vue') }, diff --git a/frontend/src/views/iam/IAMUserList.vue b/frontend/src/views/iam/IAMUserList.vue index 070e43e..937b444 100644 --- a/frontend/src/views/iam/IAMUserList.vue +++ b/frontend/src/views/iam/IAMUserList.vue @@ -82,7 +82,7 @@ {{ row.volc_login_allowed ? '🔓 关闭火山登录' : '🔒 开启火山登录' }} - 全局权限策略 + 权限管理 划拨记录 登录密码 +
+ + +
+ + + + + 全局策略对所有项目生效。一般只放 Deny 策略(项目隔离),业务权限请加到项目级。 + + + + + + + + + + + + + + + + + + 以下权限仅在 {{ proj.project_name }} 项目范围内生效。 + + + + + + + + + + + + + + +
+
+ + + + +