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) <noreply@anthropic.com>
This commit is contained in:
seaislee1209 2026-03-28 22:31:24 +08:00
parent a2a822a889
commit 23ec78e83d

View File

@ -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)