- 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) <noreply@anthropic.com>
- Global policy view: filter out project-scoped policies, only show Global
- Project list view: filter out global policies, only show Project-scoped
- Fixes: same policy appearing in both global and project views
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
Deny policy (AirGate_Deny_{username}) was removed during disable
but not recreated on restore. Now _update_deny_policy is called
during restore to rebuild project isolation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Save volc_login_allowed state before disable
- Restore to original state (not always open)
- e.g. login=off before disable -> still off after restore
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Disable: sync volc_login_allowed=False
- Enable: sync volc_login_allowed from actual LoginProfile state
- Sync: check AK status to detect AirGate-disabled accounts
(all AKs inactive = disabled, even if user Status=active)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Account status now comes from Volcengine User.Status field (active/disabled)
- Console login status synced to volc_login_allowed separately
- Fixes: closing Volcengine login no longer marks account as disabled after sync
- Handles ghost LoginProfile (CreateDate=1970) correctly during sync
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Skip LoginProfile operations when user has no console password
- Only send non-empty fields to Volcengine UpdateUser API
- Fixes enable_user crash for users created without password
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix: detach policy before deleting (avoids deletion error on referenced policy)
- Fix: fail explicitly if project list can't be fetched (prevent no-op Deny)
- Add _refresh_all_deny_policies helper for batch refresh after new project creation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add upsert_deny_policy / remove_deny_policy to IAMService
- Auto-update Deny policy when adding/removing projects
- Auto-create Deny policy on sub-account creation
- Deny policy lists all non-authorized projects explicitly
- Verified: cross-project ListAssetGroups and ListApiKeys are blocked
- Updated research report with cross-project API findings (2026-03-28)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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) <noreply@anthropic.com>
- Detect InvalidPassword error and return user-friendly message
- Rollback user creation if password policy fails
- Add password requirements hint in create form
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add edit profile (display name, phone, email) with Volcengine sync
- Add IAMService.update_user for Volcengine UpdateUser API
- Add edit-profile API endpoint and URL
- Add Edit Profile dialog in IAMUserList frontend
- Verify admin change password, sub-account change password, set login password all working
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Admin: set sub-account AirGate login password via dropdown menu
- Admin: toggle sub-account login enabled/disabled
- Sub-account: change own password (sidebar "修改密码")
- Sub-account: auto-redirect to login page after password change
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Makes auth role logic consistent between admin (role: 'admin')
and sub-account (role: 'iam_user') logins.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- IAMUser model: login_password_hash + login_enabled fields
- Custom JWT auth for sub-accounts (role: iam_user)
- Login/me/my-keys/reveal endpoints for sub-accounts
- Admin can set login password via set-login endpoint
- Sub-accounts can only see their own API Keys
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New ArkApiKey model (encrypted storage, bound to user+project)
- Admin enters API Key from Volcengine console into AirGate
- Sub-accounts can only view their own keys
- Reveal endpoint decrypts key on demand with audit log
- Updated research report: documented Ark API limitation (CreateApiKey
doesn't return plaintext) and manual entry solution
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New VolcengineClient.call_json() for POST+JSON signing (Ark API)
- ArkService for API Key CRUD operations
- Backend views: list/create/toggle/delete ark keys per project
- Frontend: ArkKeysView with project selector, key table, create dialog
- Created key value shown once with copy button
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "授权" button on each linked project row
- New dialog to select/deselect policies per project
- Backend does incremental diff: only attach new, detach removed
- Handle PolicyAttachConflict gracefully
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Global policies conflict with project-level attach - treat as success
since the user already has the permission globally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change password: current user can change their own password
- Admin management: superuser can create/toggle/reset-password for admins
- Operation log: view all system operations with type filter
- All operations are recorded to AlertRecord for audit trail
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Disable now removes all policies (saved to DB) + Enable restores them
- Project add: policies are now user-selected (checkbox), not auto-attached
- Fix serializer allow_blank for optional fields (email/phone/password)
- Better error reporting for policy detach/attach failures
- Handle duplicate user creation with clear error message
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Project-level authorization:
- Adding a project to a sub-account now auto-calls AttachPolicyInProject
to grant default policies (ArkFullAccess, TOSFullAccess) in that project scope
- Removing a project auto-calls DetachPolicyInProject to revoke those policies
- Each project records which policies were attached (attached_policies field)
so removal knows exactly what to revoke
Configuration:
- GlobalConfig.default_project_policies: configurable list of policies to
auto-attach (editable in Settings page, defaults to ArkFullAccess + TOSFullAccess)
IAM Service:
- Added attach_policy_in_project() and detach_policy_in_project() methods
using standard AttachUserPolicy/DetachUserPolicy with ProjectName parameter
Frontend:
- Projects dialog now shows "已授权策略" column with policy tags
- Settings page has "项目默认授权策略" config field
Alert logging:
- Project add/remove operations are logged with attached/detached policy details
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data model:
- Add IAMUserProject model (sub-account → N projects, each with monitoring toggle)
- Remove old single project_name from IAMUser model
- Update SpendingRecord with per-project granularity
Backend:
- Project CRUD views: list/add/update-toggle/delete/toggle-all
- Create user view auto-adds first project if specified
- Scheduler aggregates spending across all enabled projects per user
- Per-project spending recorded in SpendingRecord + IAMUserProject.current_spending
- Alert details include per-project spending breakdown
Frontend:
- New "项目管理" dialog: add projects from Volcengine dropdown, toggle monitoring per project, remove projects, batch toggle all
- "项目" column in user table showing monitored/total count (clickable)
- BillingView: expandable rows showing per-project spending breakdown
- Create dialog: optional initial project selection
- Removed old single-project select from config dialog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quota allocation system:
- Replace monthly budget with one-time quota allocation (prepaid model)
- Support both adding (+) and deducting (-) quota with underflow protection
- Stepped alerts at configurable percentages (e.g., 50%/80%/90%)
- Auto-disable when quota exhausted (100%), alert state resets on new allocation
- Quota allocation history with operator audit trail
IAM management:
- Create new IAM sub-accounts directly from AirGate (auto-generates API keys)
- SecretKey shown once in dialog with copy-to-clipboard
- Attach/detach IAM policies via UI (ArkFullAccess, TOSFullAccess, etc.)
- Sync existing users from Volcengine
- Project list pulled from Volcengine API for dropdown selection
Security & auth:
- API Key authentication for external systems (AirDrama integration)
- SECRET_KEY enforced in production (raises error if missing with DEBUG=False)
- APIKeyUser with proper pk/is_staff attributes for DRF compatibility
Infrastructure:
- Docker + docker-compose for backend and frontend
- Nginx reverse proxy for frontend with /api/ forwarding
- Entrypoint with auto-migrate and default admin creation
- SQLite data persisted via Docker volume at /app/data/
Bug fixes from audit:
- Fix frontend referencing non-existent fields (current_month_spending, effective_budget, budget_usage_percent)
- Fix scheduler using naive datetime.now() → timezone.now()
- Fix scheduler reading interval from settings instead of GlobalConfig DB
- Fix docker-compose SQLite volume mounting as directory
- Fix CORS origin with explicit port 80
- Remove dead config (VOLC_ACCESS_KEY/SK, MONITOR_INTERVAL from settings)
- Remove unused imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend (Django 4.2 + DRF):
- Admin auth with SimpleJWT
- Volcengine API client with HMAC-SHA256 signing
- IAM user management (create/sync/import/disable/enable)
- Billing query with pagination
- Feishu webhook notifications (async)
- APScheduler for periodic spending checks
- AES-256 encrypted credential storage
- API key auth for external system integration
Frontend (Vue 3 + Element Plus):
- Login page
- Dashboard with stats overview
- IAM user list with per-user threshold config
- Billing view with spending progress bars
- Alert history with type filtering
- Settings page for global config and Volcengine account management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>