Summary
On the templates tables, the organization filter dropdown is scoped to organization membership, while the table itself is scoped to template permissions. When a user has template permissions in an organization they are not a member of, that organization's templates appear in the table but the organization is never offered in the dropdown, so there is no way to filter by it from the UI.
Observed on v2.37.0, but the code has been this way for a while and is not specific to that release.
Steps to reproduce
- Deploy with multiple organizations and a user who has
updateTemplates in three organizations but is a member of only two.
- Ensure each of the three organizations has at least one template.
- As that user, go to Admin settings -> Coder Agents -> Templates.
- Open the
All organizations dropdown.
Expected: all three organizations are listed.
Actual: only the two organizations the user is a member of are listed, even though templates belonging to the third organization are visible in the table below.
Root cause
The table in site/src/pages/AISettingsPage/TemplatesPage/TemplatesPage.tsx builds its scope from useDashboard().organizations (backed by GET /api/v2/organizations), narrowed by the updateTemplates permission:
const authorizedOrganizationIDs = new Set(
organizations
.filter(
(organization) =>
organizationPermissionsQuery.data?.[organization.id]?.updateTemplates,
)
.map((organization) => organization.id),
);
That page reuses useTemplatesFilter / TemplatesFilter from site/src/pages/TemplatesPage/TemplatesFilter.tsx, whose dropdown options come from a different endpoint:
getOptions: async () => {
const orgs = await API.getMyOrganizations(); // GET /api/v2/users/me/organizations
return orgs.map(orgOption);
},
getMyOrganizations only returns organizations the user is a member of, hence the mismatch. The same filter component is used by the main Templates page, so it is affected too.
Note that getSelectedOption in the same hook already resolves the org unscoped via API.getOrganization(...), so filtering by the missing organization works if the query is set directly in the URL - only the picker hides it.
Suggested fix
Source the dropdown options from API.getOrganizations() instead of API.getMyOrganizations() in TemplatesFilter.tsx, so the picker matches what the table can display.
Possibly related
useOrganizationsFilterMenu in site/src/modules/tableFiltering/options.tsx has an analogous problem: it filters the organization options by audit_log:read permission (correct for the Audit and Connection Log pages), but it is also used by the Workspaces page, where that gate is not appropriate.
Created on behalf of @ericpaulsen
Summary
On the templates tables, the organization filter dropdown is scoped to organization membership, while the table itself is scoped to template permissions. When a user has template permissions in an organization they are not a member of, that organization's templates appear in the table but the organization is never offered in the dropdown, so there is no way to filter by it from the UI.
Observed on v2.37.0, but the code has been this way for a while and is not specific to that release.
Steps to reproduce
updateTemplatesin three organizations but is a member of only two.All organizationsdropdown.Expected: all three organizations are listed.
Actual: only the two organizations the user is a member of are listed, even though templates belonging to the third organization are visible in the table below.
Root cause
The table in
site/src/pages/AISettingsPage/TemplatesPage/TemplatesPage.tsxbuilds its scope fromuseDashboard().organizations(backed byGET /api/v2/organizations), narrowed by theupdateTemplatespermission:That page reuses
useTemplatesFilter/TemplatesFilterfromsite/src/pages/TemplatesPage/TemplatesFilter.tsx, whose dropdown options come from a different endpoint:getMyOrganizationsonly returns organizations the user is a member of, hence the mismatch. The same filter component is used by the main Templates page, so it is affected too.Note that
getSelectedOptionin the same hook already resolves the org unscoped viaAPI.getOrganization(...), so filtering by the missing organization works if the query is set directly in the URL - only the picker hides it.Suggested fix
Source the dropdown options from
API.getOrganizations()instead ofAPI.getMyOrganizations()inTemplatesFilter.tsx, so the picker matches what the table can display.Possibly related
useOrganizationsFilterMenuinsite/src/modules/tableFiltering/options.tsxhas an analogous problem: it filters the organization options byaudit_log:readpermission (correct for the Audit and Connection Log pages), but it is also used by the Workspaces page, where that gate is not appropriate.Created on behalf of @ericpaulsen