Skip to content

fix(filter): Account dropdown empty for Standard/Read-Only users (missing view:accounts for global filter) #951

Description

@cristim

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

  1. 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).
  2. Open the global topbar filter and select a Provider — the Provider list shows and is selectable.
  3. 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)

  1. 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.
  2. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/mDaysimpact/manyAffects most userspr-createdA PR has been opened for this issue (dedup guard for the auto-PR loop)pr-mergedThe PR for this issue has been mergedpriority/p2Backlog-worthyseverity/mediumModerate harmtriagedItem has been triagedtype/bugDefecturgency/this-sprintWithin the current sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions