Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: fix loading states and permissions checks in organization settings#16465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
aslilac merged 25 commits intomainfromlilac/fix-a-bunch-of-orgs-stuff
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
25 commits
Select commitHold shift + click to select a range
f3828b4
oh man what I have gotten myself into
aslilacFeb 5, 2025
cd61327
fix even more-er permissions
aslilacFeb 5, 2025
baccd65
Merge branch 'main' into lilac/fix-a-bunch-of-orgs-stuff
aslilacFeb 7, 2025
0a59089
oh yeah
aslilacFeb 7, 2025
379bacd
oh yeah
aslilacFeb 7, 2025
45b2ba6
🔥
aslilacFeb 7, 2025
b841aac
yes
aslilacFeb 8, 2025
d7990cf
🧹
aslilacFeb 8, 2025
acf5cc7
:)
aslilacFeb 8, 2025
6db6aa4
Merge branch 'main' into lilac/fix-a-bunch-of-orgs-stuff
aslilacFeb 11, 2025
c8db4f7
yay polish time
aslilacFeb 11, 2025
1bb6137
💀
aslilacFeb 11, 2025
44b84d1
Merge branch 'main' into lilac/fix-a-bunch-of-orgs-stuff
aslilacFeb 18, 2025
5cd198b
🧹
aslilacFeb 18, 2025
0cb6c4e
of course
aslilacFeb 18, 2025
b4227b2
fix some stories
aslilacFeb 18, 2025
5f6b248
a few more permissions fixes
aslilacFeb 18, 2025
81fbdf4
mmmmm bep
aslilacFeb 18, 2025
41ff221
:|
aslilacFeb 18, 2025
58a0876
testin'
aslilacFeb 18, 2025
71b3170
ok lets do this separately
aslilacFeb 18, 2025
37d87d3
add missing query to terminal page story
aslilacFeb 19, 2025
660cbda
🧹
aslilacFeb 19, 2025
b6024b5
Merge branch 'main' into lilac/fix-a-bunch-of-orgs-stuff
aslilacFeb 19, 2025
f0f3859
story fixes
aslilacFeb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletionscoderd/rbac/roles.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -297,18 +297,17 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
Identifier: RoleAuditor(),
DisplayName: "Auditor",
Site: Permissions(map[string][]policy.Action{
// Should be able to read all template details, even in orgs they
// are not in.
ResourceTemplate.Type: {policy.ActionRead, policy.ActionViewInsights},
ResourceAuditLog.Type: {policy.ActionRead},
ResourceUser.Type: {policy.ActionRead},
ResourceGroup.Type: {policy.ActionRead},
ResourceGroupMember.Type: {policy.ActionRead},
ResourceAuditLog.Type: {policy.ActionRead},
// Allow auditors to see the resources that audit logs reflect.
ResourceTemplate.Type: {policy.ActionRead, policy.ActionViewInsights},
ResourceUser.Type: {policy.ActionRead},
ResourceGroup.Type: {policy.ActionRead},
ResourceGroupMember.Type: {policy.ActionRead},
ResourceOrganization.Type: {policy.ActionRead},
ResourceOrganizationMember.Type: {policy.ActionRead},
// Allow auditors to query deployment stats and insights.
ResourceDeploymentStats.Type: {policy.ActionRead},
ResourceDeploymentConfig.Type: {policy.ActionRead},
// Org roles are not really used yet, so grant the perm at the site level.
ResourceOrganizationMember.Type: {policy.ActionRead},
}),
Org: map[string][]Permission{},
User: []Permission{},
Expand All@@ -325,11 +324,10 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
// CRUD to provisioner daemons for now.
ResourceProvisionerDaemon.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
// Needs to read all organizations since
ResourceOrganization.Type: {policy.ActionRead},
ResourceUser.Type: {policy.ActionRead},
ResourceGroup.Type: {policy.ActionRead},
ResourceGroupMember.Type: {policy.ActionRead},
// Org roles are not really used yet, so grant the perm at the site level.
ResourceUser.Type: {policy.ActionRead},
ResourceGroup.Type: {policy.ActionRead},
ResourceGroupMember.Type: {policy.ActionRead},
ResourceOrganization.Type: {policy.ActionRead},
ResourceOrganizationMember.Type: {policy.ActionRead},
}),
Org: map[string][]Permission{},
Expand All@@ -348,10 +346,11 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete,
policy.ActionUpdatePersonal, policy.ActionReadPersonal,
},
ResourceGroup.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
ResourceGroupMember.Type: {policy.ActionRead},
ResourceOrganization.Type: {policy.ActionRead},
// Full perms to manage org members
ResourceOrganizationMember.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
ResourceGroup.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
ResourceGroupMember.Type: {policy.ActionRead},
// Manage org membership based on OIDC claims
ResourceIdpsyncSettings.Type: {policy.ActionRead, policy.ActionUpdate},
}),
Expand Down
5 changes: 3 additions & 2 deletionscoderd/rbac/roles_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -117,6 +117,7 @@ func TestRolePermissions(t *testing.T) {
owner := authSubject{Name: "owner", Actor: rbac.Subject{ID: adminID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.RoleOwner()}}}
templateAdmin := authSubject{Name: "template-admin", Actor: rbac.Subject{ID: templateAdminID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.RoleTemplateAdmin()}}}
userAdmin := authSubject{Name: "user-admin", Actor: rbac.Subject{ID: userAdminID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.RoleUserAdmin()}}}
auditor := authSubject{Name: "auditor", Actor: rbac.Subject{ID: auditorID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.RoleAuditor()}}}

orgAdmin := authSubject{Name: "org_admin", Actor: rbac.Subject{ID: adminID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.ScopedRoleOrgMember(orgID), rbac.ScopedRoleOrgAdmin(orgID)}}}
orgAuditor := authSubject{Name: "org_auditor", Actor: rbac.Subject{ID: auditorID.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember(), rbac.ScopedRoleOrgMember(orgID), rbac.ScopedRoleOrgAuditor(orgID)}}}
Expand DownExpand Up@@ -286,8 +287,8 @@ func TestRolePermissions(t *testing.T) {
Actions: []policy.Action{policy.ActionRead},
Resource: rbac.ResourceOrganization.WithID(orgID).InOrg(orgID),
AuthorizeMap: map[bool][]hasAuthSubjects{
true: {owner, orgAdmin, orgMemberMe, templateAdmin, orgTemplateAdmin, orgAuditor, orgUserAdmin},
false: {setOtherOrg, memberMe, userAdmin},
true: {owner, orgAdmin, orgMemberMe, templateAdmin, orgTemplateAdmin,auditor,orgAuditor, userAdmin, orgUserAdmin},
false: {setOtherOrg, memberMe},
},
},
{
Expand Down
127 changes: 28 additions & 99 deletionssite/src/api/queries/organizations.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
import { API } from "api/api";
import type {
AuthorizationResponse,
CreateOrganizationRequest,
GroupSyncSettings,
RoleSyncSettings,
UpdateOrganizationRequest,
} from "api/typesGenerated";
import {
type AnyOrganizationPermissions,
type OrganizationPermissionName,
type OrganizationPermissions,
anyOrganizationPermissionChecks,
organizationPermissionChecks,
} from "modules/management/organizationPermissions";
import type { QueryClient } from "react-query";
import { meKey } from "./users";

Expand DownExpand Up@@ -197,53 +203,6 @@ export const patchRoleSyncSettings = (
};
};

/**
* Fetch permissions for a single organization.
*
* If the ID is undefined, return a disabled query.
*/
export const organizationPermissions = (organizationId: string | undefined) => {
if (!organizationId) {
return { enabled: false };
}
return {
queryKey: ["organization", organizationId, "permissions"],
queryFn: () =>
// Only request what we use on individual org settings, members, and group
// pages, which at the moment is whether you can edit the members on the
// members page, create roles on the roles page, and create groups on the
// groups page. The edit organization check for the settings page is
// covered by the multi-org query at the moment, and the edit group check
// on the group page is done on the group itself, not the org, so neither
// show up here.
API.checkAuthorization({
checks: {
editMembers: {
object: {
resource_type: "organization_member",
organization_id: organizationId,
},
action: "update",
},
createGroup: {
object: {
resource_type: "group",
organization_id: organizationId,
},
action: "create",
},
assignOrgRole: {
object: {
resource_type: "assign_org_role",
organization_id: organizationId,
},
action: "create",
},
},
}),
};
};

export const provisionerJobQueryKey = (orgId: string) => [
"organization",
orgId,
Expand DownExpand Up@@ -276,58 +235,13 @@ export const organizationsPermissions = (
// per sub-link (settings, groups, roles, and members pages) that tells us
// whether to show that page, since we only show them if you can edit (and
// not, at the moment if you can only view).
const checks = (organizationId: string) => ({
editMembers: {
object: {
resource_type: "organization_member",
organization_id: organizationId,
},
action: "update",
},
editGroups: {
object: {
resource_type: "group",
organization_id: organizationId,
},
action: "update",
},
editOrganization: {
object: {
resource_type: "organization",
organization_id: organizationId,
},
action: "update",
},
assignOrgRole: {
object: {
resource_type: "assign_org_role",
organization_id: organizationId,
},
action: "create",
},
viewProvisioners: {
object: {
resource_type: "provisioner_daemon",
organization_id: organizationId,
},
action: "read",
},
viewIdpSyncSettings: {
object: {
resource_type: "idpsync_settings",
organization_id: organizationId,
},
action: "read",
},
});

// The endpoint takes a flat array, so to avoid collisions prepend each
// check with the org ID (the key can be anything we want).
const prefixedChecks = organizationIds.flatMap((orgId) =>
Object.entries(checks(orgId)).map(([key, val]) => [
`${orgId}.${key}`,
val,
]),
Object.entries(organizationPermissionChecks(orgId)).map(
([key, val]) => [`${orgId}.${key}`, val],
),
);

const response = await API.checkAuthorization({
Expand All@@ -343,15 +257,30 @@ export const organizationsPermissions = (
if (!acc[orgId]) {
acc[orgId] = {};
}
acc[orgId][perm] = value;
acc[orgId][perm as OrganizationPermissionName] = value;
return acc;
},
{} as Record<string,AuthorizationResponse>,
);
{} as Record<string,Partial<OrganizationPermissions>>,
) as Record<string, OrganizationPermissions>;
},
};
};

export const anyOrganizationPermissionsKey = [
"authorization",
"anyOrganization",
];

export const anyOrganizationPermissions = () => {
return {
queryKey: anyOrganizationPermissionsKey,
queryFn: () =>
API.checkAuthorization({
checks: anyOrganizationPermissionChecks,
}) as Promise<AnyOrganizationPermissions>,
};
};

export const getOrganizationIdpSyncClaimFieldValuesKey = (
organization: string,
field: string,
Expand Down
8 changes: 0 additions & 8 deletionssite/src/contexts/auth/permissions.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,6 @@ export const checks = {
readWorkspaceProxies: "readWorkspaceProxies",
editWorkspaceProxies: "editWorkspaceProxies",
createOrganization: "createOrganization",
editAnyOrganization: "editAnyOrganization",
viewAnyGroup: "viewAnyGroup",
createGroup: "createGroup",
viewAllLicenses: "viewAllLicenses",
Expand DownExpand Up@@ -122,13 +121,6 @@ export const permissionsToCheck = {
},
action: "create",
},
[checks.editAnyOrganization]: {
object: {
resource_type: "organization",
any_org: true,
},
action: "update",
},
[checks.viewAnyGroup]: {
object: {
resource_type: "group",
Expand Down
22 changes: 18 additions & 4 deletionssite/src/modules/dashboard/DashboardProvider.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
import { appearance } from "api/queries/appearance";
import { entitlements } from "api/queries/entitlements";
import { experiments } from "api/queries/experiments";
import { organizations } from "api/queries/organizations";
import {
anyOrganizationPermissions,
organizations,
} from "api/queries/organizations";
import type {
AppearanceConfig,
Entitlements,
Expand All@@ -11,6 +14,7 @@ import type {
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { canViewAnyOrganization } from "modules/management/organizationPermissions";
import { type FC, type PropsWithChildren, createContext } from "react";
import { useQuery } from "react-query";
import { selectFeatureVisibility } from "./entitlements";
Expand All@@ -21,6 +25,7 @@ export interface DashboardValue {
appearance: AppearanceConfig;
organizations: readonly Organization[];
showOrganizations: boolean;
canViewOrganizationSettings: boolean;
}

export const DashboardContext = createContext<DashboardValue | undefined>(
Expand All@@ -33,12 +38,16 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
const experimentsQuery = useQuery(experiments(metadata.experiments));
const appearanceQuery = useQuery(appearance(metadata.appearance));
const organizationsQuery = useQuery(organizations());
const anyOrganizationPermissionsQuery = useQuery(
anyOrganizationPermissions(),
);

const error =
entitlementsQuery.error ||
appearanceQuery.error ||
experimentsQuery.error ||
organizationsQuery.error;
organizationsQuery.error ||
anyOrganizationPermissionsQuery.error;

if (error) {
return <ErrorAlert error={error} />;
Expand All@@ -48,7 +57,8 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
!entitlementsQuery.data ||
!appearanceQuery.data ||
!experimentsQuery.data ||
!organizationsQuery.data;
!organizationsQuery.data ||
!anyOrganizationPermissionsQuery.data;

if (isLoading) {
return <Loader fullscreen />;
Expand All@@ -58,6 +68,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
const organizationsEnabled = selectFeatureVisibility(
entitlementsQuery.data,
).multiple_organizations;
const showOrganizations = hasMultipleOrganizations || organizationsEnabled;

return (
<DashboardContext.Provider
Expand All@@ -66,7 +77,10 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
experiments: experimentsQuery.data,
appearance: appearanceQuery.data,
organizations: organizationsQuery.data,
showOrganizations: hasMultipleOrganizations || organizationsEnabled,
showOrganizations,
canViewOrganizationSettings:
showOrganizations &&
canViewAnyOrganization(anyOrganizationPermissionsQuery.data),
}}
>
{children}
Expand Down
9 changes: 4 additions & 5 deletionssite/src/modules/dashboard/Navbar/Navbar.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,14 +12,13 @@ export const Navbar: FC = () => {
const { metadata } = useEmbeddedMetadata();
const buildInfoQuery = useQuery(buildInfo(metadata["build-info"]));

const { appearance,showOrganizations } = useDashboard();
const { appearance,canViewOrganizationSettings } = useDashboard();
const { user: me, permissions, signOut } = useAuthenticated();
const featureVisibility = useFeatureVisibility();
const canViewAuditLog =
featureVisibility.audit_log && Boolean(permissions.viewAnyAuditLog);
const canViewDeployment = Boolean(permissions.viewDeploymentValues);
const canViewOrganizations =
Boolean(permissions.editAnyOrganization) && showOrganizations;
featureVisibility.audit_log && permissions.viewAnyAuditLog;
const canViewDeployment = permissions.viewDeploymentValues;
const canViewOrganizations = canViewOrganizationSettings;
const proxyContextValue = useProxy();
const canViewHealth = canViewDeployment;

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@ import { Stack } from "components/Stack/Stack";
import { usePopover } from "components/deprecated/Popover/Popover";
import type { FC } from "react";
import { Link } from "react-router-dom";

export const Language = {
accountLabel: "Account",
signOutLabel: "Sign Out",
Expand DownExpand Up@@ -129,7 +130,7 @@ export const UserDropdownContent: FC<UserDropdownContentProps> = ({
</a>
</Tooltip>

{Boolean(buildInfo?.deployment_id) && (
{buildInfo?.deployment_id && (
<div
css={css`
font-size: 12px;
Expand All@@ -145,11 +146,11 @@ export const UserDropdownContent: FC<UserDropdownContentProps> = ({
text-overflow: ellipsis;
`}
>
{buildInfo?.deployment_id}
{buildInfo.deployment_id}
</div>
</Tooltip>
<CopyButton
text={buildInfo!.deployment_id}
text={buildInfo.deployment_id}
buttonStyles={css`
width: 16px;
height: 16px;
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp