mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
Opt for bulk save role permissions instead of save on each form change
This commit is contained in:
@@ -66,7 +66,6 @@ export const OrgRoleTable = () => {
|
|||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
// onClick={() => onSelectRole()}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlePopUpOpen("role");
|
handlePopUpOpen("role");
|
||||||
}}
|
}}
|
||||||
|
|||||||
+1
-16
@@ -64,11 +64,8 @@ type Props = {
|
|||||||
formName: keyof Omit<Exclude<TFormSchema["permissions"], undefined>, "workspace">;
|
formName: keyof Omit<Exclude<TFormSchema["permissions"], undefined>, "workspace">;
|
||||||
setValue: UseFormSetValue<TFormSchema>;
|
setValue: UseFormSetValue<TFormSchema>;
|
||||||
control: Control<TFormSchema>;
|
control: Control<TFormSchema>;
|
||||||
handleSubmit: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// permission categories
|
|
||||||
|
|
||||||
enum Permission {
|
enum Permission {
|
||||||
NoAccess = "no-access",
|
NoAccess = "no-access",
|
||||||
ReadOnly = "read-only",
|
ReadOnly = "read-only",
|
||||||
@@ -76,16 +73,7 @@ enum Permission {
|
|||||||
Custom = "custom"
|
Custom = "custom"
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: support for default roles
|
export const RolePermissionRow = ({ isEditable, title, formName, control, setValue }: Props) => {
|
||||||
|
|
||||||
export const RolePermissionRow = ({
|
|
||||||
isEditable,
|
|
||||||
title,
|
|
||||||
formName,
|
|
||||||
handleSubmit,
|
|
||||||
control,
|
|
||||||
setValue
|
|
||||||
}: Props) => {
|
|
||||||
const [isRowExpanded, setIsRowExpanded] = useToggle();
|
const [isRowExpanded, setIsRowExpanded] = useToggle();
|
||||||
const [isCustom, setIsCustom] = useToggle();
|
const [isCustom, setIsCustom] = useToggle();
|
||||||
|
|
||||||
@@ -157,8 +145,6 @@ export const RolePermissionRow = ({
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -211,7 +197,6 @@ export const RolePermissionRow = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
field.onChange(e);
|
field.onChange(e);
|
||||||
handleSubmit();
|
|
||||||
}}
|
}}
|
||||||
id={`permissions.${formName}.${action}`}
|
id={`permissions.${formName}.${action}`}
|
||||||
>
|
>
|
||||||
|
|||||||
+148
-4
@@ -1,18 +1,162 @@
|
|||||||
import { RolePermissionsTable } from "./RolePermissionsTable";
|
import { useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button , Table, TableContainer, TBody, Th, THead, Tr } from "@app/components/v2";
|
||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
import { useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api";
|
||||||
|
import {
|
||||||
|
formRolePermission2API,
|
||||||
|
formSchema,
|
||||||
|
rolePermission2Form,
|
||||||
|
TFormSchema
|
||||||
|
} from "@app/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleModifySection/OrgRoleModifySection.utils";
|
||||||
|
|
||||||
|
import { RolePermissionRow } from "./RolePermissionRow";
|
||||||
|
|
||||||
|
const SIMPLE_PERMISSION_OPTIONS = [
|
||||||
|
{
|
||||||
|
title: "User management",
|
||||||
|
formName: "member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Group management",
|
||||||
|
formName: "groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Machine identity management",
|
||||||
|
formName: "identity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Billing & usage",
|
||||||
|
formName: "billing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Role management",
|
||||||
|
formName: "role"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Incident Contacts",
|
||||||
|
formName: "incident-contact"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Organization profile",
|
||||||
|
formName: "settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Secret Scanning",
|
||||||
|
formName: "secret-scanning"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "SSO",
|
||||||
|
formName: "sso"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "LDAP",
|
||||||
|
formName: "ldap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "SCIM",
|
||||||
|
formName: "scim"
|
||||||
|
}
|
||||||
|
] as const;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
roleId: string;
|
roleId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RolePermissionsSection = ({ roleId }: Props) => {
|
export const RolePermissionsSection = ({ roleId }: Props) => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
|
const { data: role } = useGetOrgRole(orgId, roleId);
|
||||||
|
|
||||||
|
const {
|
||||||
|
setValue,
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { isDirty, isSubmitting },
|
||||||
|
reset
|
||||||
|
} = useForm<TFormSchema>({
|
||||||
|
defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {},
|
||||||
|
resolver: zodResolver(formSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutateAsync: updateRole } = useUpdateOrgRole();
|
||||||
|
|
||||||
|
const onSubmit = async (el: TFormSchema) => {
|
||||||
|
try {
|
||||||
|
await updateRole({
|
||||||
|
orgId,
|
||||||
|
id: roleId,
|
||||||
|
...el,
|
||||||
|
permissions: formRolePermission2API(el.permissions)
|
||||||
|
});
|
||||||
|
createNotification({ type: "success", text: "Successfully updated role" });
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
createNotification({ type: "error", text: "Failed to update role" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isCustomRole = !["admin", "member", "no-access"].includes(role?.slug ?? "");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<form
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
<h3 className="text-lg font-semibold text-mineshaft-100">Permissions</h3>
|
<h3 className="text-lg font-semibold text-mineshaft-100">Permissions</h3>
|
||||||
|
{isCustomRole && (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Button
|
||||||
|
colorSchema="primary"
|
||||||
|
type="submit"
|
||||||
|
isDisabled={isSubmitting || !isDirty}
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="ml-4 text-mineshaft-300"
|
||||||
|
variant="link"
|
||||||
|
isDisabled={isSubmitting || !isDirty}
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
onClick={() => reset()}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<RolePermissionsTable roleId={roleId} />
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th className="w-5" />
|
||||||
|
<Th>Resource</Th>
|
||||||
|
<Th>Permission</Th>
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{SIMPLE_PERMISSION_OPTIONS.map((permission) => {
|
||||||
|
return (
|
||||||
|
<RolePermissionRow
|
||||||
|
title={permission.title}
|
||||||
|
formName={permission.formName}
|
||||||
|
control={control}
|
||||||
|
setValue={setValue}
|
||||||
|
key={`org-role-${roleId}-permission-${permission.formName}`}
|
||||||
|
isEditable={isCustomRole}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
-128
@@ -1,128 +0,0 @@
|
|||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import { Table, TableContainer, TBody, Th, THead, Tr } from "@app/components/v2";
|
|
||||||
import { useOrganization } from "@app/context";
|
|
||||||
import { useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api";
|
|
||||||
import {
|
|
||||||
formRolePermission2API,
|
|
||||||
formSchema,
|
|
||||||
rolePermission2Form,
|
|
||||||
TFormSchema
|
|
||||||
} from "@app/views/Org/MembersPage/components/OrgRoleTabSection/OrgRoleModifySection/OrgRoleModifySection.utils";
|
|
||||||
|
|
||||||
import { RolePermissionRow } from "./RolePermissionRow";
|
|
||||||
|
|
||||||
const SIMPLE_PERMISSION_OPTIONS = [
|
|
||||||
{
|
|
||||||
title: "User management",
|
|
||||||
formName: "member"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Group management",
|
|
||||||
formName: "groups"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Machine identity management",
|
|
||||||
formName: "identity"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Billing & usage",
|
|
||||||
formName: "billing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Role management",
|
|
||||||
formName: "role"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Incident Contacts",
|
|
||||||
formName: "incident-contact"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Organization profile",
|
|
||||||
formName: "settings"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Secret Scanning",
|
|
||||||
formName: "secret-scanning"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "SSO",
|
|
||||||
formName: "sso"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "LDAP",
|
|
||||||
formName: "ldap"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "SCIM",
|
|
||||||
formName: "scim"
|
|
||||||
}
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
roleId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RolePermissionsTable = ({ roleId }: Props) => {
|
|
||||||
const { currentOrg } = useOrganization();
|
|
||||||
const orgId = currentOrg?.id || "";
|
|
||||||
|
|
||||||
const { data: role } = useGetOrgRole(orgId, roleId);
|
|
||||||
|
|
||||||
const { setValue, control, handleSubmit } = useForm<TFormSchema>({
|
|
||||||
defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {},
|
|
||||||
resolver: zodResolver(formSchema)
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: updateRole } = useUpdateOrgRole();
|
|
||||||
|
|
||||||
const onSubmit = async (el: TFormSchema) => {
|
|
||||||
try {
|
|
||||||
await updateRole({
|
|
||||||
orgId,
|
|
||||||
id: roleId,
|
|
||||||
...el,
|
|
||||||
permissions: formRolePermission2API(el.permissions)
|
|
||||||
});
|
|
||||||
createNotification({ type: "success", text: "Successfully updated role" });
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
createNotification({ type: "error", text: "Failed to update role" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isCustomRole = !["admin", "member", "no-access"].includes(role?.slug ?? "");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableContainer>
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<Table>
|
|
||||||
<THead>
|
|
||||||
<Tr>
|
|
||||||
<Th className="w-5" />
|
|
||||||
<Th>Resource</Th>
|
|
||||||
<Th>Permission</Th>
|
|
||||||
</Tr>
|
|
||||||
</THead>
|
|
||||||
<TBody>
|
|
||||||
{SIMPLE_PERMISSION_OPTIONS.map((permission) => {
|
|
||||||
return (
|
|
||||||
<RolePermissionRow
|
|
||||||
title={permission.title}
|
|
||||||
formName={permission.formName}
|
|
||||||
control={control}
|
|
||||||
setValue={setValue}
|
|
||||||
handleSubmit={handleSubmit(onSubmit)}
|
|
||||||
key={`org-role-${roleId}-permission-${permission.formName}`}
|
|
||||||
isEditable={isCustomRole}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TBody>
|
|
||||||
</Table>
|
|
||||||
</form>
|
|
||||||
</TableContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user