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 / readonly roles).
- 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 calling api.listAccounts() → GET /api/accounts.
listAccounts in internal/api/handler_accounts.go is gated by requirePermission(ctx, req, "view", "accounts").
- The Standard Users group (migration
000057, mirroring DefaultUserPermissions()) and Read-Only Users group (mirroring DefaultReadOnlyPermissions()) do not include {"action":"view","resource":"accounts"}. See internal/auth/types.go DefaultUserPermissions() / DefaultReadOnlyPermissions().
- So
GET /api/accounts returns 403 for these users. populateAccountOptions's catch block 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 needs id / name / provider / external_id scoped by allowed_accounts. Add a lightweight accounts-for-filter source available to any authenticated user, instead of the admin-grade view accounts endpoint. Keeps the credential/config metadata in the GET /api/accounts response (role ARNs, subscription IDs, client emails) gated behind view accounts.
- Grant
view accounts to 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.
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
user/readonlyroles).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_accountsmis-resolution originally hypothesized — all seeded groups carryallowed_accounts = ['*'], so scoping resolves to unrestricted. It is a missing read permission: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").000057, mirroringDefaultUserPermissions()) and Read-Only Users group (mirroringDefaultReadOnlyPermissions()) do not include{"action":"view","resource":"accounts"}. Seeinternal/auth/types.goDefaultUserPermissions()/DefaultReadOnlyPermissions().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 gotDefaultUserPermissions()(noview 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, andPurchase Approversgroups already carryview accounts, so members of those groups are unaffected.Fix options (design call needed — pick one)
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.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
000057and000059both seed group UUID00000000-0000-5000-8000-000000000005(Standard Users vs. Purchaser). On a fresh DB000057wins;000059'sINSERT ... ON CONFLICT DO NOTHINGis skipped, so the Purchaser group is never created,DefaultPurchaserGroupIDresolves to "Standard Users", and000059'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.