mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore(site): remove template ACL XService (#10332)
This commit is contained in:
+1
-1
@@ -947,7 +947,7 @@ export const getTemplateACL = async (
|
||||
export const updateTemplateACL = async (
|
||||
templateId: string,
|
||||
data: TypesGen.UpdateTemplateACL,
|
||||
): Promise<TypesGen.TemplateACL> => {
|
||||
): Promise<{ message: string }> => {
|
||||
const response = await axios.patch(
|
||||
`/api/v2/templates/${templateId}/acl`,
|
||||
data,
|
||||
|
||||
@@ -6,8 +6,13 @@ import {
|
||||
type TemplateVersion,
|
||||
CreateTemplateRequest,
|
||||
ProvisionerJob,
|
||||
TemplateRole,
|
||||
} from "api/typesGenerated";
|
||||
import { type QueryClient, type QueryOptions } from "react-query";
|
||||
import {
|
||||
MutationOptions,
|
||||
type QueryClient,
|
||||
type QueryOptions,
|
||||
} from "react-query";
|
||||
import { delay } from "utils/delay";
|
||||
|
||||
export const templateByNameKey = (orgId: string, name: string) => [
|
||||
@@ -36,6 +41,53 @@ export const templates = (orgId: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const templateACL = (templateId: string) => {
|
||||
return {
|
||||
queryKey: ["templateAcl", templateId],
|
||||
queryFn: () => API.getTemplateACL(templateId),
|
||||
};
|
||||
};
|
||||
|
||||
export const setUserRole = (
|
||||
queryClient: QueryClient,
|
||||
): MutationOptions<
|
||||
Awaited<ReturnType<typeof API.updateTemplateACL>>,
|
||||
unknown,
|
||||
{ templateId: string; userId: string; role: TemplateRole }
|
||||
> => {
|
||||
return {
|
||||
mutationFn: ({ templateId, userId, role }) =>
|
||||
API.updateTemplateACL(templateId, {
|
||||
user_perms: {
|
||||
[userId]: role,
|
||||
},
|
||||
}),
|
||||
onSuccess: async (_res, { templateId }) => {
|
||||
await queryClient.invalidateQueries(["templateAcl", templateId]);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const setGroupRole = (
|
||||
queryClient: QueryClient,
|
||||
): MutationOptions<
|
||||
Awaited<ReturnType<typeof API.updateTemplateACL>>,
|
||||
unknown,
|
||||
{ templateId: string; groupId: string; role: TemplateRole }
|
||||
> => {
|
||||
return {
|
||||
mutationFn: ({ templateId, groupId, role }) =>
|
||||
API.updateTemplateACL(templateId, {
|
||||
group_perms: {
|
||||
[groupId]: role,
|
||||
},
|
||||
}),
|
||||
onSuccess: async (_res, { templateId }) => {
|
||||
await queryClient.invalidateQueries(["templateAcl", templateId]);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const templateExamples = (orgId: string) => {
|
||||
return {
|
||||
queryKey: [...getTemplatesQueryKey(orgId), "examples"],
|
||||
|
||||
+68
-23
@@ -1,7 +1,6 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import Link from "@mui/material/Link";
|
||||
import ArrowRightAltOutlined from "@mui/icons-material/ArrowRightAltOutlined";
|
||||
import { useMachine } from "@xstate/react";
|
||||
import { Paywall } from "components/Paywall/Paywall";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { useFeatureVisibility } from "hooks/useFeatureVisibility";
|
||||
@@ -9,10 +8,12 @@ import { useOrganizationId } from "hooks/useOrganizationId";
|
||||
import { FC } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { pageTitle } from "utils/page";
|
||||
import { templateACLMachine } from "xServices/template/templateACLXService";
|
||||
import { useTemplateSettings } from "../TemplateSettingsLayout";
|
||||
import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView";
|
||||
import { docs } from "utils/docs";
|
||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||
import { setGroupRole, setUserRole, templateACL } from "api/queries/templates";
|
||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||
|
||||
export const TemplatePermissionsPage: FC<
|
||||
React.PropsWithChildren<unknown>
|
||||
@@ -20,10 +21,16 @@ export const TemplatePermissionsPage: FC<
|
||||
const organizationId = useOrganizationId();
|
||||
const { template, permissions } = useTemplateSettings();
|
||||
const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility();
|
||||
const [state, send] = useMachine(templateACLMachine, {
|
||||
context: { templateId: template.id },
|
||||
});
|
||||
const { templateACL, userToBeUpdated, groupToBeUpdated } = state.context;
|
||||
const templateACLQuery = useQuery(templateACL(template.id));
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const addUserMutation = useMutation(setUserRole(queryClient));
|
||||
const updateUserMutation = useMutation(setUserRole(queryClient));
|
||||
const removeUserMutation = useMutation(setUserRole(queryClient));
|
||||
|
||||
const addGroupMutation = useMutation(setGroupRole(queryClient));
|
||||
const updateGroupMutation = useMutation(setGroupRole(queryClient));
|
||||
const removeGroupMutation = useMutation(setGroupRole(queryClient));
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -58,29 +65,67 @@ export const TemplatePermissionsPage: FC<
|
||||
<TemplatePermissionsPageView
|
||||
organizationId={organizationId}
|
||||
templateID={template.id}
|
||||
templateACL={templateACL}
|
||||
templateACL={templateACLQuery.data}
|
||||
canUpdatePermissions={Boolean(permissions?.canUpdateTemplate)}
|
||||
onAddUser={(user, role, reset) => {
|
||||
send("ADD_USER", { user, role, onDone: reset });
|
||||
onAddUser={async (user, role, reset) => {
|
||||
await addUserMutation.mutateAsync({
|
||||
templateId: template.id,
|
||||
userId: user.id,
|
||||
role,
|
||||
});
|
||||
reset();
|
||||
}}
|
||||
isAddingUser={state.matches("addingUser")}
|
||||
onUpdateUser={(user, role) => {
|
||||
send("UPDATE_USER_ROLE", { user, role });
|
||||
isAddingUser={addUserMutation.isLoading}
|
||||
onUpdateUser={async (user, role) => {
|
||||
await updateUserMutation.mutateAsync({
|
||||
templateId: template.id,
|
||||
userId: user.id,
|
||||
role,
|
||||
});
|
||||
displaySuccess("User role updated successfully!");
|
||||
}}
|
||||
updatingUser={userToBeUpdated}
|
||||
onRemoveUser={(user) => {
|
||||
send("REMOVE_USER", { user });
|
||||
updatingUserId={
|
||||
updateUserMutation.isLoading
|
||||
? updateUserMutation.variables?.userId
|
||||
: undefined
|
||||
}
|
||||
onRemoveUser={async (user) => {
|
||||
await removeUserMutation.mutateAsync({
|
||||
templateId: template.id,
|
||||
userId: user.id,
|
||||
role: "",
|
||||
});
|
||||
displaySuccess("User removed successfully!");
|
||||
}}
|
||||
onAddGroup={(group, role, reset) => {
|
||||
send("ADD_GROUP", { group, role, onDone: reset });
|
||||
onAddGroup={async (group, role, reset) => {
|
||||
await addGroupMutation.mutateAsync({
|
||||
templateId: template.id,
|
||||
groupId: group.id,
|
||||
role,
|
||||
});
|
||||
reset();
|
||||
}}
|
||||
isAddingGroup={state.matches("addingGroup")}
|
||||
onUpdateGroup={(group, role) => {
|
||||
send("UPDATE_GROUP_ROLE", { group, role });
|
||||
isAddingGroup={addGroupMutation.isLoading}
|
||||
onUpdateGroup={async (group, role) => {
|
||||
await updateGroupMutation.mutateAsync({
|
||||
templateId: template.id,
|
||||
groupId: group.id,
|
||||
role,
|
||||
});
|
||||
displaySuccess("Group role updated successfully!");
|
||||
}}
|
||||
updatingGroup={groupToBeUpdated}
|
||||
onRemoveGroup={(group) => {
|
||||
send("REMOVE_GROUP", { group });
|
||||
updatingGroupId={
|
||||
updateGroupMutation.isLoading
|
||||
? updateGroupMutation.variables?.groupId
|
||||
: undefined
|
||||
}
|
||||
onRemoveGroup={async (group) => {
|
||||
await removeGroupMutation.mutateAsync({
|
||||
groupId: group.id,
|
||||
templateId: template.id,
|
||||
role: "",
|
||||
});
|
||||
displaySuccess("Group removed successfully!");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
+6
-10
@@ -161,7 +161,7 @@ export interface TemplatePermissionsPageViewProps {
|
||||
) => void;
|
||||
isAddingUser: boolean;
|
||||
onUpdateUser: (user: TemplateUser, role: TemplateRole) => void;
|
||||
updatingUser: TemplateUser | undefined;
|
||||
updatingUserId: TemplateUser["id"] | undefined;
|
||||
onRemoveUser: (user: TemplateUser) => void;
|
||||
// Group
|
||||
onAddGroup: (
|
||||
@@ -171,7 +171,7 @@ export interface TemplatePermissionsPageViewProps {
|
||||
) => void;
|
||||
isAddingGroup: boolean;
|
||||
onUpdateGroup: (group: TemplateGroup, role: TemplateRole) => void;
|
||||
updatingGroup: TemplateGroup | undefined;
|
||||
updatingGroupId?: TemplateGroup["id"] | undefined;
|
||||
onRemoveGroup: (group: Group) => void;
|
||||
}
|
||||
|
||||
@@ -185,13 +185,13 @@ export const TemplatePermissionsPageView: FC<
|
||||
// User
|
||||
onAddUser,
|
||||
isAddingUser,
|
||||
updatingUser,
|
||||
updatingUserId,
|
||||
onUpdateUser,
|
||||
onRemoveUser,
|
||||
// Group
|
||||
onAddGroup,
|
||||
isAddingGroup,
|
||||
updatingGroup,
|
||||
updatingGroupId,
|
||||
onUpdateGroup,
|
||||
onRemoveGroup,
|
||||
}) => {
|
||||
@@ -265,9 +265,7 @@ export const TemplatePermissionsPageView: FC<
|
||||
<Cond condition={canUpdatePermissions}>
|
||||
<RoleSelect
|
||||
value={group.role}
|
||||
disabled={
|
||||
updatingGroup && updatingGroup.id === group.id
|
||||
}
|
||||
disabled={updatingGroupId === group.id}
|
||||
onChange={(event) => {
|
||||
onUpdateGroup(
|
||||
group,
|
||||
@@ -313,9 +311,7 @@ export const TemplatePermissionsPageView: FC<
|
||||
<Cond condition={canUpdatePermissions}>
|
||||
<RoleSelect
|
||||
value={user.role}
|
||||
disabled={
|
||||
updatingUser && updatingUser.id === user.id
|
||||
}
|
||||
disabled={updatingUserId === user.id}
|
||||
onChange={(event) => {
|
||||
onUpdateUser(
|
||||
user,
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
import { getTemplateACL, updateTemplateACL } from "api/api";
|
||||
import {
|
||||
TemplateACL,
|
||||
TemplateGroup,
|
||||
TemplateRole,
|
||||
TemplateUser,
|
||||
} from "api/typesGenerated";
|
||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||
import { assign, createMachine } from "xstate";
|
||||
|
||||
export const templateACLMachine = createMachine(
|
||||
{
|
||||
schema: {
|
||||
context: {} as {
|
||||
templateId: string;
|
||||
templateACL?: TemplateACL;
|
||||
// User
|
||||
userToBeAdded?: TemplateUser;
|
||||
userToBeUpdated?: TemplateUser;
|
||||
addUserCallback?: () => void;
|
||||
// Group
|
||||
groupToBeAdded?: TemplateGroup;
|
||||
groupToBeUpdated?: TemplateGroup;
|
||||
addGroupCallback?: () => void;
|
||||
},
|
||||
services: {} as {
|
||||
loadTemplateACL: {
|
||||
data: TemplateACL;
|
||||
};
|
||||
// User
|
||||
addUser: {
|
||||
data: unknown;
|
||||
};
|
||||
updateUser: {
|
||||
data: unknown;
|
||||
};
|
||||
// Group
|
||||
addGroup: {
|
||||
data: unknown;
|
||||
};
|
||||
updateGroup: {
|
||||
data: unknown;
|
||||
};
|
||||
},
|
||||
events: {} as // User
|
||||
| {
|
||||
type: "ADD_USER";
|
||||
user: TemplateUser;
|
||||
role: TemplateRole;
|
||||
onDone: () => void;
|
||||
}
|
||||
| {
|
||||
type: "UPDATE_USER_ROLE";
|
||||
user: TemplateUser;
|
||||
role: TemplateRole;
|
||||
}
|
||||
| {
|
||||
type: "REMOVE_USER";
|
||||
user: TemplateUser;
|
||||
}
|
||||
// Group
|
||||
| {
|
||||
type: "ADD_GROUP";
|
||||
group: TemplateGroup;
|
||||
role: TemplateRole;
|
||||
onDone: () => void;
|
||||
}
|
||||
| {
|
||||
type: "UPDATE_GROUP_ROLE";
|
||||
group: TemplateGroup;
|
||||
role: TemplateRole;
|
||||
}
|
||||
| {
|
||||
type: "REMOVE_GROUP";
|
||||
group: TemplateGroup;
|
||||
},
|
||||
},
|
||||
tsTypes: {} as import("./templateACLXService.typegen").Typegen0,
|
||||
id: "templateACL",
|
||||
initial: "loading",
|
||||
states: {
|
||||
loading: {
|
||||
invoke: {
|
||||
src: "loadTemplateACL",
|
||||
onDone: {
|
||||
actions: ["assignTemplateACL"],
|
||||
target: "idle",
|
||||
},
|
||||
},
|
||||
},
|
||||
idle: {
|
||||
on: {
|
||||
// User
|
||||
ADD_USER: { target: "addingUser", actions: ["assignUserToBeAdded"] },
|
||||
UPDATE_USER_ROLE: {
|
||||
target: "updatingUser",
|
||||
actions: ["assignUserToBeUpdated"],
|
||||
},
|
||||
REMOVE_USER: {
|
||||
target: "removingUser",
|
||||
actions: ["removeUserFromTemplateACL"],
|
||||
},
|
||||
// Group
|
||||
ADD_GROUP: {
|
||||
target: "addingGroup",
|
||||
actions: ["assignGroupToBeAdded"],
|
||||
},
|
||||
UPDATE_GROUP_ROLE: {
|
||||
target: "updatingGroup",
|
||||
actions: ["assignGroupToBeUpdated"],
|
||||
},
|
||||
REMOVE_GROUP: {
|
||||
target: "removingGroup",
|
||||
actions: ["removeGroupFromTemplateACL"],
|
||||
},
|
||||
},
|
||||
},
|
||||
// User
|
||||
addingUser: {
|
||||
invoke: {
|
||||
src: "addUser",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: ["addUserToTemplateACL", "runAddUserCallback"],
|
||||
},
|
||||
},
|
||||
},
|
||||
updatingUser: {
|
||||
invoke: {
|
||||
src: "updateUser",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: [
|
||||
"updateUserOnTemplateACL",
|
||||
"clearUserToBeUpdated",
|
||||
"displayUpdateUserSuccessMessage",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
removingUser: {
|
||||
invoke: {
|
||||
src: "removeUser",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: ["displayRemoveUserSuccessMessage"],
|
||||
},
|
||||
},
|
||||
},
|
||||
// Group
|
||||
addingGroup: {
|
||||
invoke: {
|
||||
src: "addGroup",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: ["addGroupToTemplateACL", "runAddGroupCallback"],
|
||||
},
|
||||
},
|
||||
},
|
||||
updatingGroup: {
|
||||
invoke: {
|
||||
src: "updateGroup",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: [
|
||||
"updateGroupOnTemplateACL",
|
||||
"clearGroupToBeUpdated",
|
||||
"displayUpdateGroupSuccessMessage",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
removingGroup: {
|
||||
invoke: {
|
||||
src: "removeGroup",
|
||||
onDone: {
|
||||
target: "idle",
|
||||
actions: ["displayRemoveGroupSuccessMessage"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
services: {
|
||||
loadTemplateACL: ({ templateId }) => getTemplateACL(templateId),
|
||||
// User
|
||||
addUser: ({ templateId }, { user, role }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
user_perms: {
|
||||
[user.id]: role,
|
||||
},
|
||||
}),
|
||||
updateUser: ({ templateId }, { user, role }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
user_perms: {
|
||||
[user.id]: role,
|
||||
},
|
||||
}),
|
||||
removeUser: ({ templateId }, { user }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
user_perms: {
|
||||
[user.id]: "",
|
||||
},
|
||||
}),
|
||||
// Group
|
||||
addGroup: ({ templateId }, { group, role }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
group_perms: {
|
||||
[group.id]: role,
|
||||
},
|
||||
}),
|
||||
updateGroup: ({ templateId }, { group, role }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
group_perms: {
|
||||
[group.id]: role,
|
||||
},
|
||||
}),
|
||||
removeGroup: ({ templateId }, { group }) =>
|
||||
updateTemplateACL(templateId, {
|
||||
group_perms: {
|
||||
[group.id]: "",
|
||||
},
|
||||
}),
|
||||
},
|
||||
actions: {
|
||||
assignTemplateACL: assign({
|
||||
templateACL: (_, { data }) => data,
|
||||
}),
|
||||
// User
|
||||
assignUserToBeAdded: assign({
|
||||
userToBeAdded: (_, { user, role }) => ({ ...user, role }),
|
||||
addUserCallback: (_, { onDone }) => onDone,
|
||||
}),
|
||||
addUserToTemplateACL: assign({
|
||||
templateACL: ({ templateACL, userToBeAdded }) => {
|
||||
if (!userToBeAdded) {
|
||||
throw new Error("No user to be added");
|
||||
}
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
users: [...templateACL.users, userToBeAdded],
|
||||
};
|
||||
},
|
||||
}),
|
||||
runAddUserCallback: ({ addUserCallback }) => {
|
||||
if (addUserCallback) {
|
||||
addUserCallback();
|
||||
}
|
||||
},
|
||||
assignUserToBeUpdated: assign({
|
||||
userToBeUpdated: (_, { user, role }) => ({ ...user, role }),
|
||||
}),
|
||||
updateUserOnTemplateACL: assign({
|
||||
templateACL: ({ templateACL, userToBeUpdated }) => {
|
||||
if (!userToBeUpdated) {
|
||||
throw new Error("No user to be added");
|
||||
}
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
users: templateACL.users.map((oldTemplateUser) => {
|
||||
return oldTemplateUser.id === userToBeUpdated.id
|
||||
? userToBeUpdated
|
||||
: oldTemplateUser;
|
||||
}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
clearUserToBeUpdated: assign({
|
||||
userToBeUpdated: (_) => undefined,
|
||||
}),
|
||||
displayUpdateUserSuccessMessage: () => {
|
||||
displaySuccess("User role update successfully!");
|
||||
},
|
||||
removeUserFromTemplateACL: assign({
|
||||
templateACL: ({ templateACL }, { user }) => {
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
users: templateACL.users.filter((oldTemplateUser) => {
|
||||
return oldTemplateUser.id !== user.id;
|
||||
}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
displayRemoveUserSuccessMessage: () => {
|
||||
displaySuccess("User removed successfully!");
|
||||
},
|
||||
// Group
|
||||
assignGroupToBeAdded: assign({
|
||||
groupToBeAdded: (_, { group, role }) => ({ ...group, role }),
|
||||
addGroupCallback: (_, { onDone }) => onDone,
|
||||
}),
|
||||
addGroupToTemplateACL: assign({
|
||||
templateACL: ({ templateACL, groupToBeAdded }) => {
|
||||
if (!groupToBeAdded) {
|
||||
throw new Error("No group to be added");
|
||||
}
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
group: [...templateACL.group, groupToBeAdded],
|
||||
};
|
||||
},
|
||||
}),
|
||||
runAddGroupCallback: ({ addGroupCallback }) => {
|
||||
if (addGroupCallback) {
|
||||
addGroupCallback();
|
||||
}
|
||||
},
|
||||
assignGroupToBeUpdated: assign({
|
||||
groupToBeUpdated: (_, { group, role }) => ({ ...group, role }),
|
||||
}),
|
||||
updateGroupOnTemplateACL: assign({
|
||||
templateACL: ({ templateACL, groupToBeUpdated }) => {
|
||||
if (!groupToBeUpdated) {
|
||||
throw new Error("No group to be added");
|
||||
}
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
group: templateACL.group.map((oldTemplateGroup) => {
|
||||
return oldTemplateGroup.id === groupToBeUpdated.id
|
||||
? groupToBeUpdated
|
||||
: oldTemplateGroup;
|
||||
}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
clearGroupToBeUpdated: assign({
|
||||
groupToBeUpdated: (_) => undefined,
|
||||
}),
|
||||
displayUpdateGroupSuccessMessage: () => {
|
||||
displaySuccess("Group role update successfully!");
|
||||
},
|
||||
removeGroupFromTemplateACL: assign({
|
||||
templateACL: ({ templateACL }, { group }) => {
|
||||
if (!templateACL) {
|
||||
throw new Error("Template ACL is not loaded yet");
|
||||
}
|
||||
return {
|
||||
...templateACL,
|
||||
group: templateACL.group.filter((oldTemplateGroup) => {
|
||||
return oldTemplateGroup.id !== group.id;
|
||||
}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
displayRemoveGroupSuccessMessage: () => {
|
||||
displaySuccess("Group removed successfully!");
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user