From 48c55765c80e0d2f10aa7b051f73cf3e136e3d9f Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Sat, 28 Mar 2026 19:38:39 +0800 Subject: [PATCH] feat: add toggle Volcengine console login button for sub-accounts - Add volc_login_allowed field to IAMUser model - Add toggle-volc-login API endpoint - Add toggle button in IAMUserList dropdown menu - Sync status on user creation and toggle Co-Authored-By: Claude Opus 4.6 (1M context) --- .../0009_iamuser_volc_login_allowed.py | 18 ++++++++ backend/apps/monitor/models.py | 1 + backend/apps/monitor/serializers.py | 2 +- backend/apps/monitor/urls.py | 1 + backend/apps/monitor/views.py | 44 +++++++++++++++++++ frontend/src/views/iam/IAMUserList.vue | 19 ++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 backend/apps/monitor/migrations/0009_iamuser_volc_login_allowed.py diff --git a/backend/apps/monitor/migrations/0009_iamuser_volc_login_allowed.py b/backend/apps/monitor/migrations/0009_iamuser_volc_login_allowed.py new file mode 100644 index 0000000..e00c99b --- /dev/null +++ b/backend/apps/monitor/migrations/0009_iamuser_volc_login_allowed.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.21 on 2026-03-28 11:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('monitor', '0008_iamuser_login_enabled_iamuser_login_password_hash'), + ] + + operations = [ + migrations.AddField( + model_name='iamuser', + name='volc_login_allowed', + field=models.BooleanField(default=False, verbose_name='允许登录火山控制台'), + ), + ] diff --git a/backend/apps/monitor/models.py b/backend/apps/monitor/models.py index dfc54bb..6a1565a 100644 --- a/backend/apps/monitor/models.py +++ b/backend/apps/monitor/models.py @@ -41,6 +41,7 @@ class IAMUser(models.Model): # AirGate 本地登录密码(子账号用来登录 AirGate,与火山控制台无关) login_password_hash = models.CharField('AirGate 登录密码哈希', max_length=256, blank=True) login_enabled = models.BooleanField('允许登录 AirGate', default=False) + volc_login_allowed = models.BooleanField('允许登录火山控制台', default=False) # Access keys (stored as JSON list of AK IDs, not secrets) access_key_ids = models.JSONField('AccessKey ID 列表', default=list, blank=True) diff --git a/backend/apps/monitor/serializers.py b/backend/apps/monitor/serializers.py index 31e37c6..f17bd80 100644 --- a/backend/apps/monitor/serializers.py +++ b/backend/apps/monitor/serializers.py @@ -41,7 +41,7 @@ class IAMUserSerializer(serializers.ModelSerializer): 'alert_thresholds', 'triggered_alerts', 'effective_alert_thresholds', 'projects', 'monitored_project_count', - 'login_enabled', + 'login_enabled', 'volc_login_allowed', 'remark', 'created_at', 'updated_at', ] read_only_fields = ['user_id', 'access_key_ids', 'status', diff --git a/backend/apps/monitor/urls.py b/backend/apps/monitor/urls.py index 61ebba9..9eeb425 100644 --- a/backend/apps/monitor/urls.py +++ b/backend/apps/monitor/urls.py @@ -18,6 +18,7 @@ urlpatterns = [ path('iam-users//', views.iam_user_detail_view), path('iam-users//update/', views.iam_user_update_view), path('iam-users//edit-profile/', views.iam_user_edit_profile_view), + path('iam-users//toggle-volc-login/', views.iam_user_toggle_volc_login_view), path('iam-users//set-login/', views.iam_user_set_login_view), path('iam-users//disable/', views.iam_user_disable_view), path('iam-users//enable/', views.iam_user_enable_view), diff --git a/backend/apps/monitor/views.py b/backend/apps/monitor/views.py index 66da080..22a8d09 100644 --- a/backend/apps/monitor/views.py +++ b/backend/apps/monitor/views.py @@ -252,6 +252,7 @@ def iam_user_create_view(request): try: svc.create_login_profile(d['username'], password) result_info['login_enabled'] = True + result_info['volc_login_allowed'] = True except VolcengineAPIError as e: if 'InvalidPassword' in str(e): # Rollback: delete the user we just created @@ -292,6 +293,7 @@ def iam_user_create_view(request): phone=d.get('phone', ''), status=IAMUser.Status.ACTIVE, access_key_ids=[result_info.get('access_key_id', '')] if result_info.get('access_key_id') else [], + volc_login_allowed=result_info.get('volc_login_allowed', False), ) # 6. Auto-add project if specified @@ -379,6 +381,48 @@ def iam_user_update_view(request, pk): return Response(IAMUserSerializer(user).data) +@api_view(['POST']) +def iam_user_toggle_volc_login_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 = user.volc_account + ak = decrypt(account.access_key_enc) + sk = decrypt(account.secret_key_enc) + iam = IAMService(ak, sk) + + # Check current status + try: + resp = iam.get_login_profile(user.username) + current = resp.get('Result', {}).get('LoginProfile', {}).get('LoginAllowed', False) + except VolcengineAPIError as e: + if 'LoginProfileNotExist' in str(e): + return Response({'message': '该子账号未设置火山控制台密码,无法切换登录状态'}, + status=status.HTTP_400_BAD_REQUEST) + raise + + new_status = not current + try: + iam.update_login_allowed(user.username, new_status) + except VolcengineAPIError as e: + return Response({'message': f'操作失败: {e}'}, status=status.HTTP_400_BAD_REQUEST) + + user.volc_login_allowed = new_status + user.save(update_fields=['volc_login_allowed']) + + action = '开启' if new_status else '关闭' + AlertRecord.objects.create( + iam_user=user, alert_type='manual', + title=f'{action}火山控制台登录 {user.username}', + content=f'操作人: {request.user.username}', + ) + return Response({'message': f'已{action} {user.username} 的火山控制台登录', + 'volc_login_allowed': new_status}) + + @api_view(['POST']) def iam_user_edit_profile_view(request, pk): """编辑子账号信息(显示名、手机号、邮箱),同步到火山""" diff --git a/frontend/src/views/iam/IAMUserList.vue b/frontend/src/views/iam/IAMUserList.vue index 19095c7..383bc6f 100644 --- a/frontend/src/views/iam/IAMUserList.vue +++ b/frontend/src/views/iam/IAMUserList.vue @@ -79,6 +79,9 @@ 项目管理 监控配置 编辑信息 + + {{ row.volc_login_allowed ? '🔓 关闭火山登录' : '🔒 开启火山登录' }} + 权限策略 划拨记录 登录密码 @@ -475,6 +478,22 @@ async function handleEnable(row) { } } +// Toggle Volcengine console login +async function toggleVolcLogin(row) { + const action = row.volc_login_allowed ? '关闭' : '开启' + await ElMessageBox.confirm( + `确定${action} "${row.username}" 的火山引擎控制台登录?`, + `${action}火山登录`, { type: 'warning' } + ) + try { + const { data } = await api.post(`/api/v1/iam-users/${row.id}/toggle-volc-login/`) + ElMessage.success(data.message) + await loadUsers() + } catch (e) { + ElMessage.error(e.response?.data?.message || '操作失败') + } +} + // Edit Profile const editProfileVisible = ref(false) const editProfileUser = ref(null)