Repository navigation
fix(filter): Account dropdown empty for Standard/Read-Only users (missing view:accounts for global filter) #951
Description
Summary
The global topbar filter's Account dropdown is empty for Standard / Read-Only users. Only the seed "All Accounts" option is shown; no accounts can be selected. The Provider chip next to it works correctly. Found during QA as a Standard User.
Steps to reproduce
- Sign in as a non-admin user (a member of the "Standard Users" or "Read-Only Users" group, i.e. the legacy
user/readonlyroles). - Open the global topbar filter and select a Provider — the Provider list shows and is selectable.
- Open the Account dropdown.
Expected
Both Provider and Account dropdowns show selectable lists. The Account list contains the accounts the user is allowed to see (used to scope the views).
Actual
The Account dropdown shows only "All Accounts" (the seed option). No real accounts appear; an account cannot be selected.
Root cause (verified against feat/multicloud-web-frontend)
This is not the allowed_accounts mis-resolution originally hypothesized — all seeded groups carry allowed_accounts = ['*'], so scoping resolves to unrestricted. It is a missing read permission:
- The global filter (
frontend/src/topbar-filters.ts→populateAccountOptions) populates the Account chip by callingapi.listAccounts()→GET /api/accounts. listAccountsininternal/api/handler_accounts.gois gated byrequirePermission(ctx, req, "view", "accounts").- The Standard Users group (migration
000057, mirroringDefaultUserPermissions()) and Read-Only Users group (mirroringDefaultReadOnlyPermissions()) do not include{"action":"view","resource":"accounts"}. Seeinternal/auth/types.goDefaultUserPermissions()/DefaultReadOnlyPermissions(). - So
GET /api/accountsreturns 403 for these users.populateAccountOptions'scatchblock then falls back to[{ value: '', label: 'All Accounts' }]— exactly the observed symptom.
This is a deploy-independent code-level gap, not a regression introduced by #907 / #912. Pre-#912, a user-role user got DefaultUserPermissions() (no view accounts) and hit the same 403. The group-only migration preserved the identical permission set. The behavior is newly visible because the global account filter (#344) assumes every authenticated user can list accounts for the dropdown, which has never been true for Standard / Read-Only users.
Viewers, Plan Authors, and Purchase Approvers groups already carry view accounts, so members of those groups are unaffected.
Fix options (design call needed — pick one)
- Decouple the filter from
view accounts(preferred). The dropdown only needsid / name / provider / external_idscoped byallowed_accounts. Add a lightweight accounts-for-filter source available to any authenticated user, instead of the admin-gradeview accountsendpoint. Keeps the credential/config metadata in theGET /api/accountsresponse (role ARNs, subscription IDs, client emails) gated behindview accounts. - Grant
view accountsto Standard Users + Read-Only Users groups via a new forward migration. Simpler, consistent with the other read groups, but broadens what these users can see (the full account list response carries config metadata:aws_role_arn,azure_subscription_id,gcp_client_email, etc.) and likely unlocks the Settings → Accounts page in the nav.
Option 1 is the elegant, least-privilege fix; option 2 is the one-liner with a scope-expansion cost.
Related defect discovered while investigating (separate issue to file)
Migrations 000057 and 000059 both seed group UUID 00000000-0000-5000-8000-000000000005 (Standard Users vs. Purchaser). On a fresh DB 000057 wins; 000059's INSERT ... ON CONFLICT DO NOTHING is skipped, so the Purchaser group is never created, DefaultPurchaserGroupID resolves to "Standard Users", and 000059's admin-backfill UPDATE adds the Standard Users group to all admins. This is independent of the account-dropdown bug and should be tracked separately.
QA reference
QA sheet row 551.
Activity
cristim commentedon Jun 5, 2026
Fixed in #987 (option 1, least-privilege).
Added a new minimal-disclosure endpoint
GET /api/accounts/listreturning[{id, name, external_id, provider}], gated onview:recommendations(held by both Standard Users and Read-Only Users) instead of the admin-gradeview:accounts, and scoped byallowed_accounts. The global filter Account dropdown (row 551) now reads this endpoint viaapi.listAccountsMinimal(), so it populates for Standard / Read-Only users. The full account-config response (role ARNs, subscription IDs, client emails) stays behindview:accountsonGET /api/accounts.The separate migration-collision defect noted at the bottom of this issue (migrations 000057 and 000059 both seeding the same group UUID) is out of scope for #987 and should stay tracked separately.
fix(accounts): least-privilege accounts list for standard-user filter…