feat: made review changes

This commit is contained in:
=
2025-07-23 14:31:53 +05:30
parent 7c87feb546
commit f38b8eac2b
17 changed files with 527 additions and 390 deletions
@@ -208,6 +208,7 @@ const kickOutSecretManagerProject = async (knex: Knex, oldProjectId: string) =>
if (secret) { if (secret) {
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretManager); const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretManager);
await knex(TableName.IntegrationAuth).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.IntegrationAuth).where("projectId", oldProjectId).update("projectId", newProjectId);
await knex(TableName.Environment).where("projectId", oldProjectId).update("projectId", newProjectId);
await knex(TableName.SecretBlindIndex).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.SecretBlindIndex).where("projectId", oldProjectId).update("projectId", newProjectId);
await knex(TableName.SecretSync).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.SecretSync).where("projectId", oldProjectId).update("projectId", newProjectId);
await knex(TableName.SecretTag).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.SecretTag).where("projectId", oldProjectId).update("projectId", newProjectId);
@@ -229,7 +230,7 @@ const kickOutCertManagerProject = async (knex: Knex, oldProjectId: string) => {
}; };
const kickOutSecretScanningProject = async (knex: Knex, oldProjectId: string) => { const kickOutSecretScanningProject = async (knex: Knex, oldProjectId: string) => {
const cas = await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).returning("id").first(); const cas = await knex(TableName.SecretScanningDataSource).where("projectId", oldProjectId).returning("id").first();
if (cas) { if (cas) {
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretScanning); const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretScanning);
await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).update("projectId", newProjectId);
@@ -244,6 +245,7 @@ const kickOutKmsProject = async (knex: Knex, oldProjectId: string) => {
.andWhere("isReserved", false) .andWhere("isReserved", false)
.returning("id") .returning("id")
.first(); .first();
if (kmsKeys) { if (kmsKeys) {
const newProjectId = await newProject(knex, oldProjectId, ProjectType.KMS); const newProjectId = await newProject(knex, oldProjectId, ProjectType.KMS);
await knex(TableName.KmsKey) await knex(TableName.KmsKey)
@@ -255,7 +257,7 @@ const kickOutKmsProject = async (knex: Knex, oldProjectId: string) => {
}; };
const kickOutSshProject = async (knex: Knex, oldProjectId: string) => { const kickOutSshProject = async (knex: Knex, oldProjectId: string) => {
const hosts = await knex(TableName.ProjectSshConfig).where("projectId", oldProjectId).returning("id").first(); const hosts = await knex(TableName.SshCertificateAuthority).where("projectId", oldProjectId).returning("id").first();
if (hosts) { if (hosts) {
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SSH); const newProjectId = await newProject(knex, oldProjectId, ProjectType.SSH);
await knex(TableName.SshHost).where("projectId", oldProjectId).update("projectId", newProjectId); await knex(TableName.SshHost).where("projectId", oldProjectId).update("projectId", newProjectId);
@@ -270,6 +272,9 @@ const BATCH_SIZE = 1000;
export async function up(knex: Knex): Promise<void> { export async function up(knex: Knex): Promise<void> {
const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type"); const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type");
if (hasTemplateTypeColumn) { if (hasTemplateTypeColumn) {
await knex(TableName.ProjectTemplates).whereNull("type").update({
type: ProjectType.SecretManager
});
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => { await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
t.string("type").notNullable().defaultTo(ProjectType.SecretManager).alter(); t.string("type").notNullable().defaultTo(ProjectType.SecretManager).alter();
}); });
@@ -52,7 +52,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
}, },
pkiEst: false, pkiEst: false,
enforceMfa: false, enforceMfa: false,
projectTemplates: false, projectTemplates: true,
kmip: false, kmip: false,
gateway: false, gateway: false,
sshHostGroups: false, sshHostGroups: false,
@@ -137,7 +137,7 @@ export const pkiAlertServiceFactory = ({
projectId: alert.projectId, projectId: alert.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
actionProjectType: ActionProjectType.Any actionProjectType: ActionProjectType.CertificateManager
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiAlerts); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiAlerts);
@@ -67,7 +67,7 @@ export const serviceTokenServiceFactory = ({
projectId, projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
actionProjectType: ActionProjectType.Any actionProjectType: ActionProjectType.SecretManager
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
@@ -158,7 +158,7 @@ export const serviceTokenServiceFactory = ({
projectId, projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId, actorOrgId,
actionProjectType: ActionProjectType.Any actionProjectType: ActionProjectType.SecretManager
}); });
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
@@ -33,7 +33,7 @@ import {
useSubscription, useSubscription,
useUser useUser
} from "@app/context"; } from "@app/context";
import { getProjectHomePage } from "@app/helpers/project"; import { getProjectHomePage, getProjectLottieIcon } from "@app/helpers/project";
import { useCreateWorkspace, useGetExternalKmsList, useGetUserWorkspaces } from "@app/hooks/api"; import { useCreateWorkspace, useGetExternalKmsList, useGetUserWorkspaces } from "@app/hooks/api";
import { INTERNAL_KMS_KEY_ID } from "@app/hooks/api/kms/types"; import { INTERNAL_KMS_KEY_ID } from "@app/hooks/api/kms/types";
import { InfisicalProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates"; import { InfisicalProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
@@ -63,28 +63,23 @@ type NewProjectFormProps = Pick<NewProjectModalProps, "onOpenChange">;
const PROJECT_TYPE_MENU_ITEMS = [ const PROJECT_TYPE_MENU_ITEMS = [
{ {
label: "Secret Manager", label: "Secret Manager",
value: ProjectType.SecretManager, value: ProjectType.SecretManager
icon: "vault"
}, },
{ {
label: "Certificate Manager", label: "Certificate Manager",
value: ProjectType.CertificateManager, value: ProjectType.CertificateManager
icon: "note"
}, },
{ {
label: "KMS", label: "KMS",
value: ProjectType.KMS, value: ProjectType.KMS
icon: "unlock"
}, },
{ {
label: "SSH", label: "SSH",
value: ProjectType.SSH, value: ProjectType.SSH
icon: "terminal"
}, },
{ {
label: "Secret Scanning", label: "Secret Scanning",
value: ProjectType.SecretScanning, value: ProjectType.SecretScanning
icon: "secret-scan"
} }
]; ];
@@ -102,18 +97,11 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
OrgPermissionSubjects.ProjectTemplates OrgPermissionSubjects.ProjectTemplates
); );
const { data: projectTemplates = [] } = useListProjectTemplates({
enabled: Boolean(canReadProjectTemplates && subscription?.projectTemplates)
});
const { data: externalKmsList } = useGetExternalKmsList(currentOrg.id, {
enabled: permission.can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms)
});
const { const {
control, control,
handleSubmit, handleSubmit,
reset, reset,
watch,
formState: { isSubmitting, errors } formState: { isSubmitting, errors }
} = useForm<TAddProjectFormData>({ } = useForm<TAddProjectFormData>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
@@ -123,6 +111,16 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
} }
}); });
const selectedProjectType = watch("type");
const { data: projectTemplates = [] } = useListProjectTemplates({
enabled: Boolean(canReadProjectTemplates && subscription?.projectTemplates),
select: (template) => template.filter((el) => el.type === selectedProjectType)
});
const { data: externalKmsList } = useGetExternalKmsList(currentOrg.id, {
enabled: permission.can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms)
});
useEffect(() => { useEffect(() => {
if (Object.keys(errors).length > 0) { if (Object.keys(errors).length > 0) {
console.log("Current form errors:", errors); console.log("Current form errors:", errors);
@@ -212,7 +210,7 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
} }
}} }}
> >
<Lottie icon={el.icon} className="h-8 w-8" /> <Lottie icon={getProjectLottieIcon(el.value)} className="h-8 w-8" />
<div className="text-center text-xs">{el.label}</div> <div className="text-center text-xs">{el.label}</div>
</div> </div>
))} ))}
+1 -1
View File
@@ -87,7 +87,7 @@ export const getProjectHomePage = (type: ProjectType) => {
export const getProjectTitle = (type: ProjectType) => { export const getProjectTitle = (type: ProjectType) => {
const titleConvert = { const titleConvert = {
[ProjectType.SecretManager]: "Secret Management", [ProjectType.SecretManager]: "Secrets Management",
[ProjectType.KMS]: "Key Management", [ProjectType.KMS]: "Key Management",
[ProjectType.CertificateManager]: "Cert Management", [ProjectType.CertificateManager]: "Cert Management",
[ProjectType.SSH]: "SSH", [ProjectType.SSH]: "SSH",
@@ -1,9 +1,12 @@
import { TProjectRole } from "@app/hooks/api/roles/types"; import { TProjectRole } from "@app/hooks/api/roles/types";
import { ProjectType } from "../workspace/types";
export type TProjectTemplate = { export type TProjectTemplate = {
id: string; id: string;
name: string; name: string;
description?: string; description?: string;
type: ProjectType;
roles: Pick<TProjectRole, "slug" | "name" | "permissions">[]; roles: Pick<TProjectRole, "slug" | "name" | "permissions">[];
environments?: { name: string; slug: string; position: number }[] | null; environments?: { name: string; slug: string; position: number }[] | null;
createdAt: string; createdAt: string;
@@ -16,6 +19,7 @@ export type TProjectTemplateResponse = { projectTemplate: TProjectTemplate };
export type TCreateProjectTemplateDTO = { export type TCreateProjectTemplateDTO = {
name: string; name: string;
description?: string; description?: string;
type?: ProjectType;
}; };
export type TUpdateProjectTemplateDTO = Partial< export type TUpdateProjectTemplateDTO = Partial<
@@ -91,7 +91,7 @@ export const SecretScanningLayout = () => {
)} )}
</Link> </Link>
<Link <Link
to="/projects/secret-management/$projectId/settings" to="/projects/secret-scanning/$projectId/settings"
params={{ params={{
projectId: currentWorkspace.id projectId: currentWorkspace.id
}} }}
@@ -5,6 +5,7 @@ import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2"; import { Button, DeleteActionModal } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { getProjectTitle } from "@app/helpers/project";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { TProjectTemplate, useDeleteProjectTemplate } from "@app/hooks/api/projectTemplates"; import { TProjectTemplate, useDeleteProjectTemplate } from "@app/hooks/api/projectTemplates";
@@ -24,7 +25,7 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
"editDetails" "editDetails"
] as const); ] as const);
const { id: templateId, name, description } = projectTemplate; const { id: templateId, name, description, type } = projectTemplate;
const deleteProjectTemplate = useDeleteProjectTemplate(); const deleteProjectTemplate = useDeleteProjectTemplate();
@@ -53,7 +54,10 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
<div className="mb-4 flex items-start justify-between border-b border-bunker-400 pb-4"> <div className="mb-4 flex items-start justify-between border-b border-bunker-400 pb-4">
<div className="flex-col"> <div className="flex-col">
<h3 className="text-xl font-semibold">{name}</h3> <h3 className="text-xl font-semibold">{name}</h3>
<h2 className="text-sm text-mineshaft-400">{description || "Project Template"}</h2> <h2 className="text-sm text-mineshaft-400">
{`${getProjectTitle(type)} - `}
{description || "Project Template"}
</h2>
</div> </div>
{!isInfisicalTemplate && ( {!isInfisicalTemplate && (
<div className="flex gap-2"> <div className="flex gap-2">
@@ -1,5 +1,6 @@
import { useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { twMerge } from "tailwind-merge";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -7,21 +8,25 @@ import {
Button, Button,
FormControl, FormControl,
Input, Input,
Lottie,
Modal, Modal,
ModalClose, ModalClose,
ModalContent, ModalContent,
TextArea TextArea
} from "@app/components/v2"; } from "@app/components/v2";
import { getProjectLottieIcon } from "@app/helpers/project";
import { import {
TProjectTemplate, TProjectTemplate,
useCreateProjectTemplate, useCreateProjectTemplate,
useUpdateProjectTemplate useUpdateProjectTemplate
} from "@app/hooks/api/projectTemplates"; } from "@app/hooks/api/projectTemplates";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { slugSchema } from "@app/lib/schemas"; import { slugSchema } from "@app/lib/schemas";
const formSchema = z.object({ const formSchema = z.object({
name: slugSchema({ min: 1, max: 64, field: "Name" }), name: slugSchema({ min: 1, max: 64, field: "Name" }),
description: z.string().max(500).optional() description: z.string().max(500).optional(),
type: z.nativeEnum(ProjectType).optional()
}); });
export type FormData = z.infer<typeof formSchema>; export type FormData = z.infer<typeof formSchema>;
@@ -38,6 +43,29 @@ type FormProps = {
onComplete: (template: TProjectTemplate) => void; onComplete: (template: TProjectTemplate) => void;
}; };
const PROJECT_TYPE_MENU_ITEMS = [
{
label: "Secrets Manager",
value: ProjectType.SecretManager
},
{
label: "Certificates Manager",
value: ProjectType.CertificateManager
},
{
label: "KMS",
value: ProjectType.KMS
},
{
label: "SSH",
value: ProjectType.SSH
},
{
label: "Secret Scanning",
value: ProjectType.SecretScanning
}
];
const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => { const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
const createProjectTemplate = useCreateProjectTemplate(); const createProjectTemplate = useCreateProjectTemplate();
const updateProjectTemplate = useUpdateProjectTemplate(); const updateProjectTemplate = useUpdateProjectTemplate();
@@ -45,12 +73,14 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
const { const {
handleSubmit, handleSubmit,
register, register,
control,
formState: { isSubmitting, errors } formState: { isSubmitting, errors }
} = useForm<FormData>({ } = useForm<FormData>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
name: projectTemplate?.name, name: projectTemplate?.name,
description: projectTemplate?.description description: projectTemplate?.description,
type: ProjectType.SecretManager
} }
}); });
@@ -90,6 +120,42 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
> >
<Input autoFocus placeholder="my-project-template" {...register("name")} /> <Input autoFocus placeholder="my-project-template" {...register("name")} />
</FormControl> </FormControl>
<Controller
control={control}
name="type"
defaultValue={ProjectType.SecretManager}
render={({ field, fieldState: { error } }) => (
<FormControl
label="Project Type"
isError={Boolean(error)}
errorText={error?.message}
className="flex-1"
>
<div className="mt-2 grid grid-cols-5 gap-4">
{PROJECT_TYPE_MENU_ITEMS.map((el) => (
<div
key={el.value}
className={twMerge(
"flex cursor-pointer flex-col items-center gap-2 rounded border border-mineshaft-600 p-4 opacity-75 transition-all hover:border-primary-400 hover:bg-mineshaft-600",
field.value === el.value && "border-primary-400 bg-mineshaft-600 opacity-100"
)}
onClick={() => field.onChange(el.value)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter") {
field.onChange(el.value);
}
}}
>
<Lottie icon={getProjectLottieIcon(el.value)} className="h-8 w-8" />
<div className="text-center text-xs">{el.label}</div>
</div>
))}
</div>
</FormControl>
)}
/>
<FormControl <FormControl
label="Description (optional)" label="Description (optional)"
errorText={errors.description?.message} errorText={errors.description?.message}
@@ -23,6 +23,7 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context"; import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
import { getProjectTitle } from "@app/helpers/project";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { TProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates"; import { TProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
@@ -66,8 +67,8 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
<THead> <THead>
<Tr> <Tr>
<Th>Name</Th> <Th>Name</Th>
<Th>Type</Th>
<Th>Roles</Th> <Th>Roles</Th>
<Th>Environments</Th>
<Th /> <Th />
</Tr> </Tr>
</THead> </THead>
@@ -80,7 +81,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
/> />
)} )}
{filteredTemplates.map((template) => { {filteredTemplates.map((template) => {
const { id, name, roles, environments = [], description } = template; const { id, name, roles, environments = [], description, type } = template;
return ( return (
<Tr <Tr
onClick={() => onEdit(template)} onClick={() => onEdit(template)}
@@ -99,6 +100,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
</Tooltip> </Tooltip>
)} )}
</Td> </Td>
<Td>{getProjectTitle(type)}</Td>
<Td className="pl-8"> <Td className="pl-8">
{roles.length} {roles.length}
{roles.length > 0 && ( {roles.length > 0 && (
@@ -5,6 +5,7 @@ import { useNavigate, useSearch } from "@tanstack/react-router";
import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { useWorkspace } from "@app/context"; import { useWorkspace } from "@app/context";
import { getProjectBaseURL } from "@app/helpers/project"; import { getProjectBaseURL } from "@app/helpers/project";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProjectAccessControlTabs } from "@app/types/project"; import { ProjectAccessControlTabs } from "@app/types/project";
import { import {
@@ -33,6 +34,8 @@ const Page = () => {
}); });
}; };
const isSecretManager = currentWorkspace.type === ProjectType.SecretManager;
return ( return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white"> <div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto mb-6 w-full max-w-7xl"> <div className="mx-auto mb-6 w-full max-w-7xl">
@@ -49,7 +52,9 @@ const Page = () => {
<p>Machine Identities</p> <p>Machine Identities</p>
</div> </div>
</Tab> </Tab>
<Tab value={ProjectAccessControlTabs.ServiceTokens}>Service Tokens</Tab> {isSecretManager && (
<Tab value={ProjectAccessControlTabs.ServiceTokens}>Service Tokens</Tab>
)}
<Tab value={ProjectAccessControlTabs.Roles}>Project Roles</Tab> <Tab value={ProjectAccessControlTabs.Roles}>Project Roles</Tab>
</TabList> </TabList>
<TabPanel value={ProjectAccessControlTabs.Member}> <TabPanel value={ProjectAccessControlTabs.Member}>
@@ -61,9 +66,11 @@ const Page = () => {
<TabPanel value={ProjectAccessControlTabs.Identities}> <TabPanel value={ProjectAccessControlTabs.Identities}>
<IdentityTab /> <IdentityTab />
</TabPanel> </TabPanel>
<TabPanel value={ProjectAccessControlTabs.ServiceTokens}> {isSecretManager && (
<ServiceTokenTab /> <TabPanel value={ProjectAccessControlTabs.ServiceTokens}>
</TabPanel> <ServiceTokenTab />
</TabPanel>
)}
<TabPanel value={ProjectAccessControlTabs.Roles}> <TabPanel value={ProjectAccessControlTabs.Roles}>
<ProjectRoleListTab /> <ProjectRoleListTab />
</TabPanel> </TabPanel>
@@ -8,6 +8,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
IconButton IconButton
} from "@app/components/v2"; } from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal"; import { PolicySelectionModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicySelectionModal";
import { PolicyTemplateModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicyTemplateModal"; import { PolicyTemplateModal } from "@app/pages/project/RoleDetailsBySlugPage/components/PolicyTemplateModal";
@@ -17,6 +18,7 @@ type Props = {
}; };
export const AddPoliciesButton = ({ isDisabled }: Props) => { export const AddPoliciesButton = ({ isDisabled }: Props) => {
const { currentWorkspace } = useWorkspace();
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([ const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
"addPolicy", "addPolicy",
"addPolicyOptions", "addPolicyOptions",
@@ -66,10 +68,12 @@ export const AddPoliciesButton = ({ isDisabled }: Props) => {
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<PolicySelectionModal <PolicySelectionModal
type={currentWorkspace.type}
isOpen={popUp.addPolicy.isOpen} isOpen={popUp.addPolicy.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("addPolicy", isOpen)}
/> />
<PolicyTemplateModal <PolicyTemplateModal
type={currentWorkspace.type}
isOpen={popUp.applyTemplate.isOpen} isOpen={popUp.applyTemplate.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("applyTemplate", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("applyTemplate", isOpen)}
/> />
@@ -19,26 +19,31 @@ import {
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionSub } from "@app/context"; import { ProjectPermissionSub } from "@app/context";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { import {
EXCLUDED_PERMISSION_SUBS, EXCLUDED_PERMISSION_SUBS,
isConditionalSubjects, isConditionalSubjects,
PROJECT_PERMISSION_OBJECT, PROJECT_PERMISSION_OBJECT,
ProjectTypePermissionSubjects,
TFormSchema TFormSchema
} from "./ProjectRoleModifySection.utils"; } from "./ProjectRoleModifySection.utils";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
onOpenChange: (isOpen: boolean) => void; onOpenChange: (isOpen: boolean) => void;
type: ProjectType;
}; };
type ContentProps = { type ContentProps = {
onClose: () => void; onClose: () => void;
type: ProjectType;
}; };
type TForm = { permissions: Record<ProjectPermissionSub, boolean> }; type TForm = { permissions: Record<ProjectPermissionSub, boolean> };
const Content = ({ onClose }: ContentProps) => { const Content = ({ onClose, type: projectType }: ContentProps) => {
const rootForm = useFormContext<TFormSchema>(); const rootForm = useFormContext<TFormSchema>();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const { const {
@@ -56,7 +61,12 @@ const Content = ({ onClose }: ContentProps) => {
}); });
const filteredPolicies = Object.entries(PROJECT_PERMISSION_OBJECT) const filteredPolicies = Object.entries(PROJECT_PERMISSION_OBJECT)
.filter(([, { title }]) => (search ? title.toLowerCase().includes(search.toLowerCase()) : true)) .filter(
([subject, { title }]) =>
ProjectTypePermissionSubjects[projectType ?? ProjectType.SecretManager][
subject as ProjectPermissionSub
] && (search ? title.toLowerCase().includes(search.toLowerCase()) : true)
)
.filter(([subject]) => !EXCLUDED_PERMISSION_SUBS.includes(subject as ProjectPermissionSub)) .filter(([subject]) => !EXCLUDED_PERMISSION_SUBS.includes(subject as ProjectPermissionSub))
.sort((a, b) => a[1].title.localeCompare(b[1].title)) .sort((a, b) => a[1].title.localeCompare(b[1].title))
.map(([subject]) => subject); .map(([subject]) => subject);
@@ -191,7 +201,7 @@ const Content = ({ onClose }: ContentProps) => {
); );
}; };
export const PolicySelectionModal = ({ isOpen, onOpenChange }: Props) => { export const PolicySelectionModal = ({ isOpen, onOpenChange, type }: Props) => {
return ( return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}> <Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent <ModalContent
@@ -199,7 +209,7 @@ export const PolicySelectionModal = ({ isOpen, onOpenChange }: Props) => {
subTitle="Select one or more policies to add to this role." subTitle="Select one or more policies to add to this role."
className="max-w-3xl" className="max-w-3xl"
> >
<Content onClose={() => onOpenChange(false)} /> <Content onClose={() => onOpenChange(false)} type={type} />
</ModalContent> </ModalContent>
</Modal> </Modal>
); );
@@ -13,6 +13,7 @@ import {
ModalContent ModalContent
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionSub } from "@app/context"; import { ProjectPermissionSub } from "@app/context";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { import {
PROJECT_PERMISSION_OBJECT, PROJECT_PERMISSION_OBJECT,
@@ -24,19 +25,23 @@ import {
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
onOpenChange: (isOpen: boolean) => void; onOpenChange: (isOpen: boolean) => void;
type: ProjectType;
}; };
type ContentProps = { type ContentProps = {
onClose: () => void; onClose: () => void;
type: ProjectType;
}; };
const Content = ({ onClose }: ContentProps) => { const Content = ({ onClose, type: projectType }: ContentProps) => {
const rootForm = useFormContext<TFormSchema>(); const rootForm = useFormContext<TFormSchema>();
const [selectedTemplate, setSelectedTemplate] = useState<RoleTemplate>(); const [selectedTemplate, setSelectedTemplate] = useState<RoleTemplate>();
const [conflictingSubjects, setConflictingSubjects] = useState<ProjectPermissionSub[]>([]); const [conflictingSubjects, setConflictingSubjects] = useState<ProjectPermissionSub[]>([]);
const [showConflictingSubjects, setShowConflictingSubjects] = useState(false); const [showConflictingSubjects, setShowConflictingSubjects] = useState(false);
const templates = RoleTemplates[projectType ?? ProjectType.SecretManager];
const onSubmit = (skipConflicting = false) => { const onSubmit = (skipConflicting = false) => {
if (!selectedTemplate) { if (!selectedTemplate) {
createNotification({ type: "error", text: "Please select a template" }); createNotification({ type: "error", text: "Please select a template" });
@@ -121,12 +126,12 @@ const Content = ({ onClose }: ContentProps) => {
type="single" type="single"
value={selectedTemplate?.id} value={selectedTemplate?.id}
onValueChange={(value) => onValueChange={(value) =>
setSelectedTemplate(RoleTemplates.find((template) => template.id === value)) setSelectedTemplate(templates.find((template) => template.id === value))
} }
collapsible collapsible
className="w-full border-collapse" className="w-full border-collapse"
> >
{RoleTemplates.map(({ name, description, permissions, id }) => ( {templates.map(({ name, description, permissions, id }) => (
<AccordionItem <AccordionItem
key={id} key={id}
value={id} value={id}
@@ -182,7 +187,7 @@ const Content = ({ onClose }: ContentProps) => {
); );
}; };
export const PolicyTemplateModal = ({ isOpen, onOpenChange }: Props) => { export const PolicyTemplateModal = ({ isOpen, onOpenChange, type }: Props) => {
return ( return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}> <Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent <ModalContent
@@ -190,7 +195,7 @@ export const PolicyTemplateModal = ({ isOpen, onOpenChange }: Props) => {
subTitle="Select a template with prepopulated policies to get started. You can always add more policies later." subTitle="Select a template with prepopulated policies to get started. You can always add more policies later."
className="max-w-3xl" className="max-w-3xl"
> >
<Content onClose={() => onOpenChange(false)} /> <Content onClose={() => onOpenChange(false)} type={type} />
</ModalContent> </ModalContent>
</Modal> </Modal>
); );
@@ -1645,41 +1645,378 @@ export type RoleTemplate = {
permissions: { subject: ProjectPermissionSub; actions: string[] }[]; permissions: { subject: ProjectPermissionSub; actions: string[] }[];
}; };
export const RoleTemplates = [ const projectManagerTemplate = (
{ additionalPermissions: RoleTemplate["permissions"] = []
id: "project-manager", ): RoleTemplate => ({
name: "Project Management Policies", id: "project-manager",
description: "Grants access to manage project members and settings", name: "Project Management Policies",
permissions: [ description: "Grants access to manage project members and settings",
permissions: [
{
subject: ProjectPermissionSub.AuditLogs,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.Groups,
actions: Object.values(ProjectPermissionGroupActions)
},
{
subject: ProjectPermissionSub.Member,
actions: Object.values(ProjectPermissionMemberActions)
},
{
subject: ProjectPermissionSub.Identity,
actions: Object.values(ProjectPermissionIdentityActions)
},
{
subject: ProjectPermissionSub.Project,
actions: [ProjectPermissionActions.Edit, ProjectPermissionActions.Delete]
},
{ subject: ProjectPermissionSub.Role, actions: Object.values(ProjectPermissionActions) },
{
subject: ProjectPermissionSub.Settings,
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Edit]
},
...additionalPermissions
]
});
export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
[ProjectType.SSH]: [
{
id: "ssh-viewer",
name: "SSH Viewing Policies",
description: "Grants read access to SSH certificates and hosts",
permissions: [
{
subject: ProjectPermissionSub.SshCertificateAuthorities,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshCertificates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshCertificateTemplates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshHosts,
actions: [ProjectPermissionSshHostActions.Read]
},
{
subject: ProjectPermissionSub.SshHostGroups,
actions: [ProjectPermissionActions.Read]
}
]
},
{
id: "ssh-cert-editor",
name: "SSH Certificate Editing Policies",
description: "Grants read and edit access to SSH certificates",
permissions: [
{
subject: ProjectPermissionSub.SshCertificateAuthorities,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SshCertificates,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SshCertificateTemplates,
actions: Object.values(ProjectPermissionActions)
}
]
},
{
id: "ssh-host-editor",
name: "SSH Host Editing Policies",
description: "Grants read and edit access to SSH hosts",
permissions: [
{
subject: ProjectPermissionSub.SshHosts,
actions: Object.values(ProjectPermissionSshHostActions)
},
{
subject: ProjectPermissionSub.SshHostGroups,
actions: Object.values(ProjectPermissionActions)
}
]
},
projectManagerTemplate()
],
[ProjectType.KMS]: [
{
id: "kms-viewer",
name: "KMS Viewing Policies",
description: "Grants read access to KMS keys and KMIP clients",
permissions: [
{
subject: ProjectPermissionSub.Cmek,
actions: [ProjectPermissionCmekActions.Read]
},
{
subject: ProjectPermissionSub.Kmip,
actions: [ProjectPermissionKmipActions.ReadClients]
}
]
},
{
id: "key-editor",
name: "KMS Key Editing Policies",
description: "Grants read and edit access to KMS keys",
permissions: [
{
subject: ProjectPermissionSub.Cmek,
actions: Object.values(ProjectPermissionCmekActions)
}
]
},
{
id: "kmip-editor",
name: "KMIP Client Editing Policies",
description: "Grants read and edit access to KMIP clients",
permissions: [
{
subject: ProjectPermissionSub.Kmip,
actions: Object.values(ProjectPermissionKmipActions)
}
]
},
projectManagerTemplate()
],
[ProjectType.CertificateManager]: [
{
id: "cert-viewer",
name: "Certificate Viewing Policies",
description: "Grants read access to certificates and related resources",
permissions: [
{
subject: ProjectPermissionSub.PkiCollections,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.PkiAlerts,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.CertificateAuthorities,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.CertificateTemplates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.Certificates,
actions: [
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.ReadPrivateKey
]
}
]
},
{
id: "cert-editor",
name: "Certificate Editing Policies",
description: "Grants read and edit access to certificates and related resources",
permissions: [
{
subject: ProjectPermissionSub.PkiCollections,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.PkiAlerts,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.CertificateAuthorities,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.CertificateTemplates,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.Certificates,
actions: Object.values(ProjectPermissionCertificateActions)
}
]
},
projectManagerTemplate()
],
[ProjectType.SecretScanning]: [
{
id: "scanning-viewer",
name: "Secret Scanning Viewing Policies",
description: "Grants read access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: [
ProjectPermissionSecretScanningDataSourceActions.Read,
ProjectPermissionSecretScanningDataSourceActions.ReadResources,
ProjectPermissionSecretScanningDataSourceActions.ReadScans
]
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: [ProjectPermissionSecretScanningFindingActions.Read]
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
{
id: "scanning-editor",
name: "Secret Scanning Editing Policies",
description: "Grants read and edit access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: Object.values(ProjectPermissionSecretScanningDataSourceActions)
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: Object.values(ProjectPermissionSecretScanningFindingActions)
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
projectManagerTemplate([
{ {
subject: ProjectPermissionSub.AuditLogs, subject: ProjectPermissionSub.SecretScanningConfigs,
actions: Object.values(ProjectPermissionActions) actions: Object.values(ProjectPermissionSecretScanningConfigActions)
}, }
{ ])
subject: ProjectPermissionSub.Groups, ],
actions: Object.values(ProjectPermissionGroupActions) [ProjectType.SecretManager]: [
}, {
{ id: "secret-viewer",
subject: ProjectPermissionSub.Member, name: "Secret Viewing Policies",
actions: Object.values(ProjectPermissionMemberActions) description: "Grants read access to secrets and related resources",
}, permissions: [
{ {
subject: ProjectPermissionSub.Identity, subject: ProjectPermissionSub.SecretRollback,
actions: Object.values(ProjectPermissionIdentityActions) actions: [ProjectPermissionActions.Read]
}, },
{ {
subject: ProjectPermissionSub.Project, subject: ProjectPermissionSub.SecretImports,
actions: [ProjectPermissionActions.Edit, ProjectPermissionActions.Delete] actions: [ProjectPermissionActions.Read]
}, },
{ subject: ProjectPermissionSub.Role, actions: Object.values(ProjectPermissionActions) }, {
{ subject: ProjectPermissionSub.Secrets,
subject: ProjectPermissionSub.Settings, actions: [
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Edit] ProjectPermissionSecretActions.DescribeSecret,
}, ProjectPermissionSecretActions.ReadValue
]
},
{
subject: ProjectPermissionSub.DynamicSecrets,
actions: [ProjectPermissionDynamicSecretActions.ReadRootCredential]
},
{
subject: ProjectPermissionSub.Environments,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.Tags,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SecretRotation,
actions: [ProjectPermissionSecretRotationActions.Read]
},
{
subject: ProjectPermissionSub.Integrations,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SecretSyncs,
actions: [ProjectPermissionSecretSyncActions.Read]
},
{
subject: ProjectPermissionSub.Commits,
actions: [ProjectPermissionCommitsActions.Read]
}
]
},
{
id: "secret-editor",
name: "Secret Editing Policies",
description: "Grants read and edit access to secrets and related resources",
permissions: [
{
subject: ProjectPermissionSub.Environments,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.DynamicSecrets,
actions: Object.values(ProjectPermissionDynamicSecretActions)
},
{
subject: ProjectPermissionSub.Secrets,
actions: [
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Delete
]
},
{
subject: ProjectPermissionSub.SecretRollback,
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Create]
},
{
subject: ProjectPermissionSub.Tags,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretImports,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretRotation,
actions: Object.values(ProjectPermissionSecretRotationActions)
},
{
subject: ProjectPermissionSub.SecretFolders,
actions: [
ProjectPermissionActions.Create,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Delete
]
},
{
subject: ProjectPermissionSub.Integrations,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretSyncs,
actions: Object.values(ProjectPermissionSecretSyncActions)
},
{
subject: ProjectPermissionSub.Commits,
actions: Object.values(ProjectPermissionCommitsActions)
}
]
},
projectManagerTemplate([
{ {
subject: ProjectPermissionSub.IpAllowList, subject: ProjectPermissionSub.IpAllowList,
actions: Object.values(ProjectPermissionActions) actions: Object.values(ProjectPermissionActions)
}, },
{
subject: ProjectPermissionSub.Kms,
actions: [ProjectPermissionActions.Edit]
},
{ {
subject: ProjectPermissionSub.SecretApproval, subject: ProjectPermissionSub.SecretApproval,
actions: Object.values(ProjectPermissionActions) actions: Object.values(ProjectPermissionActions)
@@ -1692,314 +2029,6 @@ export const RoleTemplates = [
subject: ProjectPermissionSub.Webhooks, subject: ProjectPermissionSub.Webhooks,
actions: Object.values(ProjectPermissionActions) actions: Object.values(ProjectPermissionActions)
} }
] ])
}, ]
{ };
id: "ssh-viewer",
name: "SSH Viewing Policies",
description: "Grants read access to SSH certificates and hosts",
permissions: [
{
subject: ProjectPermissionSub.SshCertificateAuthorities,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshCertificates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshCertificateTemplates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SshHosts,
actions: [ProjectPermissionSshHostActions.Read]
},
{
subject: ProjectPermissionSub.SshHostGroups,
actions: [ProjectPermissionActions.Read]
}
]
},
{
id: "ssh-cert-editor",
name: "SSH Certificate Editing Policies",
description: "Grants read and edit access to SSH certificates",
permissions: [
{
subject: ProjectPermissionSub.SshCertificateAuthorities,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SshCertificates,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SshCertificateTemplates,
actions: Object.values(ProjectPermissionActions)
}
]
},
{
id: "ssh-host-editor",
name: "SSH Host Editing Policies",
description: "Grants read and edit access to SSH hosts",
permissions: [
{
subject: ProjectPermissionSub.SshHosts,
actions: Object.values(ProjectPermissionSshHostActions)
},
{
subject: ProjectPermissionSub.SshHostGroups,
actions: Object.values(ProjectPermissionActions)
}
]
},
{
id: "kms-viewer",
name: "KMS Viewing Policies",
description: "Grants read access to KMS keys and KMIP clients",
permissions: [
{
subject: ProjectPermissionSub.Cmek,
actions: [ProjectPermissionCmekActions.Read]
},
{
subject: ProjectPermissionSub.Kmip,
actions: [ProjectPermissionKmipActions.ReadClients]
}
]
},
{
id: "key-editor",
name: "KMS Key Editing Policies",
description: "Grants read and edit access to KMS keys",
permissions: [
{
subject: ProjectPermissionSub.Cmek,
actions: Object.values(ProjectPermissionCmekActions)
}
]
},
{
id: "kmip-editor",
name: "KMIP Client Editing Policies",
description: "Grants read and edit access to KMIP clients",
permissions: [
{
subject: ProjectPermissionSub.Kmip,
actions: Object.values(ProjectPermissionKmipActions)
}
]
},
{
id: "cert-viewer",
name: "Certificate Viewing Policies",
description: "Grants read access to certificates and related resources",
permissions: [
{
subject: ProjectPermissionSub.PkiCollections,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.PkiAlerts,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.CertificateAuthorities,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.CertificateTemplates,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.Certificates,
actions: [
ProjectPermissionCertificateActions.Read,
ProjectPermissionCertificateActions.ReadPrivateKey
]
}
]
},
{
id: "cert-editor",
name: "Certificate Editing Policies",
description: "Grants read and edit access to certificates and related resources",
permissions: [
{
subject: ProjectPermissionSub.PkiCollections,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.PkiAlerts,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.CertificateAuthorities,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.CertificateTemplates,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.Certificates,
actions: Object.values(ProjectPermissionCertificateActions)
}
]
},
{
id: "scanning-viewer",
name: "Secret Scanning Viewing Policies",
description: "Grants read access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: [
ProjectPermissionSecretScanningDataSourceActions.Read,
ProjectPermissionSecretScanningDataSourceActions.ReadResources,
ProjectPermissionSecretScanningDataSourceActions.ReadScans
]
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: [ProjectPermissionSecretScanningFindingActions.Read]
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
{
id: "scanning-editor",
name: "Secret Scanning Editing Policies",
description: "Grants read and edit access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: Object.values(ProjectPermissionSecretScanningDataSourceActions)
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: Object.values(ProjectPermissionSecretScanningFindingActions)
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
{
id: "secret-viewer",
name: "Secret Viewing Policies",
description: "Grants read access to secrets and related resources",
permissions: [
{
subject: ProjectPermissionSub.SecretRollback,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SecretImports,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.Secrets,
actions: [
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue
]
},
{
subject: ProjectPermissionSub.DynamicSecrets,
actions: [ProjectPermissionDynamicSecretActions.ReadRootCredential]
},
{
subject: ProjectPermissionSub.Environments,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.Tags,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SecretRotation,
actions: [ProjectPermissionSecretRotationActions.Read]
},
{
subject: ProjectPermissionSub.Integrations,
actions: [ProjectPermissionActions.Read]
},
{
subject: ProjectPermissionSub.SecretSyncs,
actions: [ProjectPermissionSecretSyncActions.Read]
},
{
subject: ProjectPermissionSub.Commits,
actions: [ProjectPermissionCommitsActions.Read]
}
]
},
{
id: "secret-editor",
name: "Secret Editing Policies",
description: "Grants read and edit access to secrets and related resources",
permissions: [
{
subject: ProjectPermissionSub.Environments,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.DynamicSecrets,
actions: Object.values(ProjectPermissionDynamicSecretActions)
},
{
subject: ProjectPermissionSub.Secrets,
actions: [
ProjectPermissionSecretActions.DescribeSecret,
ProjectPermissionSecretActions.ReadValue,
ProjectPermissionSecretActions.Edit,
ProjectPermissionSecretActions.Create,
ProjectPermissionSecretActions.Delete
]
},
{
subject: ProjectPermissionSub.SecretRollback,
actions: [ProjectPermissionActions.Read, ProjectPermissionActions.Create]
},
{
subject: ProjectPermissionSub.Tags,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretImports,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretRotation,
actions: Object.values(ProjectPermissionSecretRotationActions)
},
{
subject: ProjectPermissionSub.SecretFolders,
actions: [
ProjectPermissionActions.Create,
ProjectPermissionActions.Edit,
ProjectPermissionActions.Delete
]
},
{
subject: ProjectPermissionSub.Integrations,
actions: Object.values(ProjectPermissionActions)
},
{
subject: ProjectPermissionSub.SecretSyncs,
actions: Object.values(ProjectPermissionSecretSyncActions)
},
{
subject: ProjectPermissionSub.Commits,
actions: Object.values(ProjectPermissionCommitsActions)
}
]
}
];
@@ -14,6 +14,7 @@ import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
import { evaluatePermissionsAbility } from "@app/helpers/permissions"; import { evaluatePermissionsAbility } from "@app/helpers/permissions";
import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api"; import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { AddPoliciesButton } from "./AddPoliciesButton"; import { AddPoliciesButton } from "./AddPoliciesButton";
import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions"; import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions";
@@ -121,6 +122,8 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
(role?.slug ?? "") as ProjectMembershipRole (role?.slug ?? "") as ProjectMembershipRole
); );
const isSecretManagerProject = currentWorkspace.type === ProjectType.SecretManager;
const permissions = form.watch("permissions"); const permissions = form.watch("permissions");
const formattedPermissions = useMemo( const formattedPermissions = useMemo(
@@ -206,7 +209,7 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
</div> </div>
</FormProvider> </FormProvider>
</form> </form>
{showAccessTree && ( {isSecretManagerProject && showAccessTree && (
<AccessTree <AccessTree
permissions={formattedPermissions} permissions={formattedPermissions}
subject={showAccessTree} subject={showAccessTree}