mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): show Organizations in admin dropdown for single-org OSS deployments (#25175)
Fixes https://linear.app/codercom/issue/CODAGT-350 On OSS or no-license single-org deployments, the Organizations admin link was hidden because `canViewOrganizationSettings` was gated on `showOrganizations`, which requires either a multi-org entitlement or >1 org. The page was still reachable via direct URL, but the members view displayed a raw "Template RBAC is a Premium feature. Contact sales!" error from the groups API. Two fixes: 1. Always render the Organizations link inside the `DeploymentDropdown`. The dropdown itself is only shown to users with admin-level permissions, so Organizations is effectively gated on having admin access. 2. Remove `groupsByUserIdQuery.error` from the error chain on the members page. The groups endpoint is gated behind `templateRBACEnabledMW` on enterprise, returning a 403 on OSS. The groups data is already optional, so the page renders fine without it. > Generated by Coder Agents
This commit is contained in:
@@ -22,10 +22,10 @@ const adminSettings = [
|
||||
] as const;
|
||||
|
||||
async function hasAccessToAdminSettings(page: Page, settings: AdminSetting[]) {
|
||||
// Organizations and Audit Logs both require a license to be visible
|
||||
// Audit Logs requires a license to be visible
|
||||
const visibleSettings = license
|
||||
? settings
|
||||
: settings.filter((it) => it !== "Organizations" && it !== "Audit Logs");
|
||||
: settings.filter((it) => it !== "Audit Logs");
|
||||
const adminSettingsButton = page.getByRole("button", {
|
||||
name: "Admin settings",
|
||||
});
|
||||
|
||||
@@ -30,8 +30,8 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
|
||||
if (
|
||||
!canViewAuditLog &&
|
||||
!canViewConnectionLog &&
|
||||
!canViewOrganizations &&
|
||||
!canViewDeployment &&
|
||||
!canViewOrganizations &&
|
||||
!canViewHealth &&
|
||||
!canViewAIBridge
|
||||
) {
|
||||
@@ -63,7 +63,6 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
|
||||
|
||||
const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
|
||||
canViewDeployment,
|
||||
canViewOrganizations,
|
||||
canViewAuditLog,
|
||||
canViewHealth,
|
||||
canViewConnectionLog,
|
||||
@@ -76,11 +75,9 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
|
||||
<Link to="/deployment">Deployment</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canViewOrganizations && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/organizations">Organizations</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/organizations">Organizations</Link>
|
||||
</DropdownMenuItem>
|
||||
{canViewAuditLog && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to={linkToAuditing}>Audit Logs</Link>
|
||||
|
||||
@@ -203,7 +203,6 @@ const ProxySettingsSub: FC<ProxySettingsSubProps> = ({ proxyContextValue }) => {
|
||||
|
||||
const AdminSettingsSub: FC<MobileMenuPermissions> = ({
|
||||
canViewDeployment,
|
||||
canViewOrganizations,
|
||||
canViewAuditLog,
|
||||
canViewConnectionLog,
|
||||
canViewHealth,
|
||||
@@ -235,14 +234,12 @@ const AdminSettingsSub: FC<MobileMenuPermissions> = ({
|
||||
<Link to="/deployment">Deployment</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canViewOrganizations && (
|
||||
<DropdownMenuItem
|
||||
asChild
|
||||
className={cn(itemStyles.default, itemStyles.sub)}
|
||||
>
|
||||
<Link to="/organizations">Organizations</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
asChild
|
||||
className={cn(itemStyles.default, itemStyles.sub)}
|
||||
>
|
||||
<Link to="/organizations">Organizations</Link>
|
||||
</DropdownMenuItem>
|
||||
{canViewAuditLog && (
|
||||
<DropdownMenuItem
|
||||
asChild
|
||||
|
||||
@@ -84,6 +84,21 @@ export const ForOrgAdmin: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const ForSingleOrgOSSAdmin: Story = {
|
||||
args: {
|
||||
canViewAuditLog: false,
|
||||
canViewOrganizations: false,
|
||||
canViewConnectionLog: false,
|
||||
canViewAIBridge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
canvas.getByRole("button", { name: "Admin settings" }),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const ForMember: Story = {
|
||||
args: {
|
||||
user: MockUserMember,
|
||||
|
||||
@@ -102,7 +102,6 @@ const OrganizationMembersPage: FC = () => {
|
||||
error={
|
||||
membersQuery.error ??
|
||||
organizationRolesQuery.error ??
|
||||
groupsByUserIdQuery.error ??
|
||||
addMemberMutation.error ??
|
||||
removeMemberMutation.error ??
|
||||
updateMemberRolesMutation.error
|
||||
|
||||
Reference in New Issue
Block a user