improvements: refactor project templates, filter policies by project type, project type specific roles

This commit is contained in:
Scott Wilson
2025-05-06 11:26:09 -07:00
parent d530604b51
commit 4562f57b54
80 changed files with 1396 additions and 634 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

@@ -72,7 +72,7 @@ const NewProjectForm = ({ onOpenChange, projectType }: NewProjectFormProps) => {
OrgPermissionSubjects.ProjectTemplates
);
const { data: projectTemplates = [] } = useListProjectTemplates({
const { data: projectTemplates = [] } = useListProjectTemplates(projectType, {
enabled: Boolean(canReadProjectTemplates && subscription?.projectTemplates)
});
@@ -0,0 +1,30 @@
import { useState } from "react";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ProjectTemplatesTab } from "./components";
const tabs = [
{ name: "Project Templates", key: "project-templates", component: ProjectTemplatesTab }
];
export const ProjectSettings = () => {
const [selectedTab, setSelectedTab] = useState(tabs[0].key);
return (
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
<TabList>
{tabs.map((tab) => (
<Tab value={tab.key} key={tab.key}>
{tab.name}
</Tab>
))}
</TabList>
{tabs.map(({ key, component: Component }) => (
<TabPanel value={key} key={`tab-panel-${key}`}>
<Component />
</TabPanel>
))}
</Tabs>
);
};
@@ -7,6 +7,7 @@ import { Button, DeleteActionModal } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { usePopUp } from "@app/hooks";
import { TProjectTemplate, useDeleteProjectTemplate } from "@app/hooks/api/projectTemplates";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { ProjectTemplateDetailsModal } from "../../ProjectTemplateDetailsModal";
import { ProjectTemplateEnvironmentsForm } from "./ProjectTemplateEnvironmentsForm";
@@ -24,7 +25,7 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
"editDetails"
] as const);
const { id: templateId, name, description } = projectTemplate;
const { id: templateId, name, description, type } = projectTemplate;
const deleteProjectTemplate = useDeleteProjectTemplate();
@@ -94,10 +95,12 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
</div>
)}
</div>
<ProjectTemplateEnvironmentsForm
isInfisicalTemplate={isInfisicalTemplate}
projectTemplate={projectTemplate}
/>
{type === ProjectType.SecretManager && (
<ProjectTemplateEnvironmentsForm
isInfisicalTemplate={isInfisicalTemplate}
projectTemplate={projectTemplate}
/>
)}
<ProjectTemplateRolesSection
isInfisicalTemplate={isInfisicalTemplate}
projectTemplate={projectTemplate}
@@ -35,6 +35,7 @@ const formSchema = z.object({
slug: slugSchema({ min: 1, max: 32 })
})
.array()
.nullish()
});
type TFormSchema = z.infer<typeof formSchema>;
@@ -67,7 +68,7 @@ export const ProjectTemplateEnvironmentsForm = ({
const onFormSubmit = async (form: TFormSchema) => {
try {
const { environments: updatedEnvs } = await updateProjectTemplate.mutateAsync({
environments: form.environments.map((env, index) => ({
environments: form.environments?.map((env, index) => ({
...env,
position: index + 1
})),
@@ -12,6 +12,7 @@ import {
ModalContent,
TextArea
} from "@app/components/v2";
import { useGetProjectTypeFromRoute } from "@app/hooks";
import {
TProjectTemplate,
useCreateProjectTemplate,
@@ -41,6 +42,7 @@ type FormProps = {
const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
const createProjectTemplate = useCreateProjectTemplate();
const updateProjectTemplate = useUpdateProjectTemplate();
const projectType = useGetProjectTypeFromRoute();
const {
handleSubmit,
@@ -55,9 +57,11 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
});
const onFormSubmit = async (data: FormData) => {
if (!projectType) return;
const mutation = projectTemplate
? updateProjectTemplate.mutateAsync({ templateId: projectTemplate.id, ...data })
: createProjectTemplate.mutateAsync(data);
: createProjectTemplate.mutateAsync({ ...data, type: projectType });
try {
const template = await mutation;
createNotification({
@@ -50,7 +50,7 @@ export const ProjectTemplatesSection = () => {
className="absolute min-h-[10rem] w-full"
>
<div>
<p className="mb-6 text-bunker-300">
<p className="mb-6 font-inter text-bunker-300">
Create and configure templates with predefined roles and environments to streamline
project setup
</p>
@@ -23,7 +23,7 @@ import {
Tr
} from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useGetProjectTypeFromRoute, usePopUp } from "@app/hooks";
import { TProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
import { DeleteProjectTemplateModal } from "./DeleteProjectTemplateModal";
@@ -35,8 +35,10 @@ type Props = {
export const ProjectTemplatesTable = ({ onEdit }: Props) => {
const { subscription } = useSubscription();
const { isPending, data: projectTemplates = [] } = useListProjectTemplates({
enabled: subscription?.projectTemplates
const projectType = useGetProjectTypeFromRoute();
const { isPending, data: projectTemplates = [] } = useListProjectTemplates(projectType, {
enabled: subscription?.projectTemplates && Boolean(projectType)
});
const [search, setSearch] = useState("");
@@ -50,6 +52,8 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
[search, projectTemplates]
);
const isSecretManagerTemplates = projectType === "secret-manager";
return (
<div>
<Input
@@ -64,7 +68,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
<Tr>
<Th>Name</Th>
<Th>Roles</Th>
<Th>Environments</Th>
{isSecretManagerTemplates && <Th>Environments</Th>}
<Th />
</Tr>
</THead>
@@ -77,7 +81,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
/>
)}
{filteredTemplates.map((template) => {
const { id, name, roles, environments, description } = template;
const { id, name, roles, environments = [], description } = template;
return (
<Tr
onClick={() => onEdit(template)}
@@ -116,28 +120,30 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
</Tooltip>
)}
</Td>
<Td className="pl-14">
{environments.length}
{environments.length > 0 && (
<Tooltip
content={
<ul className="ml-2 list-disc">
{environments
.sort((a, b) => (a.position > b.position ? 1 : -1))
.map((env) => (
<li key={env.slug}>{env.name}</li>
))}
</ul>
}
>
<FontAwesomeIcon
size="sm"
className="ml-2 text-mineshaft-400"
icon={faCircleInfo}
/>
</Tooltip>
)}
</Td>
{isSecretManagerTemplates && environments && (
<Td className="pl-14">
{environments.length}
{environments.length > 0 && (
<Tooltip
content={
<ul className="ml-2 list-disc">
{environments
.sort((a, b) => (a.position > b.position ? 1 : -1))
.map((env) => (
<li key={env.slug}>{env.name}</li>
))}
</ul>
}
>
<FontAwesomeIcon
size="sm"
className="ml-2 text-mineshaft-400"
icon={faCircleInfo}
/>
</Tooltip>
)}
</Td>
)}
<Td className="w-5">
{name !== "default" && (
<OrgPermissionCan
@@ -0,0 +1 @@
export * from "./ProjectTemplatesTab";
@@ -0,0 +1 @@
export * from "./ProjectSettings";
+21 -12
View File
@@ -6,21 +6,9 @@ export enum OrgMembershipRole {
NoAccess = "no-access"
}
enum ProjectMemberRole {
Admin = "admin",
Member = "member",
Viewer = "viewer",
NoAccess = "no-access"
}
export const isCustomOrgRole = (slug: string) =>
!Object.values(OrgMembershipRole).includes(slug as OrgMembershipRole);
export const formatProjectRoleName = (name: string) => {
if (name === ProjectMemberRole.Member) return "developer";
return name;
};
export const isCustomProjectRole = (slug: string) =>
!Object.values(ProjectMembershipRole).includes(slug as ProjectMembershipRole);
@@ -28,3 +16,24 @@ export const findOrgMembershipRole = (roles: TOrgRole[], roleIdOrSlug: string) =
isCustomOrgRole(roleIdOrSlug)
? roles.find((r) => r.id === roleIdOrSlug)
: roles.find((r) => r.slug === roleIdOrSlug);
export const formatProjectRoleName = (role: string, customRoleName?: string) => {
switch (role) {
case ProjectMembershipRole.Admin:
return "Admin";
case ProjectMembershipRole.Member:
return "Developer";
case ProjectMembershipRole.Viewer:
return "Viewer";
case ProjectMembershipRole.NoAccess:
return "No Access";
case ProjectMembershipRole.Custom:
return customRoleName ?? role;
case ProjectMembershipRole.SshHostBootstrapper:
return "SSH Host Bootstrapper";
case ProjectMembershipRole.KmsCryptographicOperator:
return "Cryptographic Operator";
default:
return role;
}
};
@@ -6,14 +6,17 @@ import {
TProjectTemplate,
TProjectTemplateResponse
} from "@app/hooks/api/projectTemplates/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
export const projectTemplateKeys = {
all: ["project-template"] as const,
list: () => [...projectTemplateKeys.all, "list"] as const,
list: (projectType?: ProjectType) =>
[...projectTemplateKeys.all, "list", ...(projectType ? [projectType] : [])] as const,
byId: (templateId: string) => [...projectTemplateKeys.all, templateId] as const
};
export const useListProjectTemplates = (
type?: ProjectType,
options?: Omit<
UseQueryOptions<
TProjectTemplate[],
@@ -25,9 +28,11 @@ export const useListProjectTemplates = (
>
) => {
return useQuery({
queryKey: projectTemplateKeys.list(),
queryKey: projectTemplateKeys.list(type),
queryFn: async () => {
const { data } = await apiRequest.get<TListProjectTemplates>("/api/v1/project-templates");
const { data } = await apiRequest.get<TListProjectTemplates>("/api/v1/project-templates", {
params: { type }
});
return data.projectTemplates;
},
@@ -1,11 +1,13 @@
import { TProjectRole } from "@app/hooks/api/roles/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
export type TProjectTemplate = {
id: string;
name: string;
type: ProjectType;
description?: string;
roles: Pick<TProjectRole, "slug" | "name" | "permissions">[];
environments: { name: string; slug: string; position: number }[];
environments?: { name: string; slug: string; position: number }[] | null;
createdAt: string;
updatedAt: string;
};
@@ -14,6 +16,7 @@ export type TListProjectTemplates = { projectTemplates: TProjectTemplate[] };
export type TProjectTemplateResponse = { projectTemplate: TProjectTemplate };
export type TCreateProjectTemplateDTO = {
type: ProjectType;
name: string;
description?: string;
};
+3 -1
View File
@@ -3,7 +3,9 @@ export enum ProjectMembershipRole {
Member = "member",
Custom = "custom",
Viewer = "viewer",
NoAccess = "no-access"
NoAccess = "no-access",
SshHostBootstrapper = "ssh-host-bootstrapper",
KmsCryptographicOperator = "cryptographic-operator"
}
export type TGetProjectRolesDTO = {
+1
View File
@@ -1,4 +1,5 @@
export { useDebounce } from "./useDebounce";
export * from "./useGetProjectTypeFromRoute";
export { usePagination } from "./usePagination";
export { usePersistentState } from "./usePersistentState";
export { usePopUp } from "./usePopUp";
@@ -0,0 +1,13 @@
import { useRouterState } from "@tanstack/react-router";
import { ProjectType } from "@app/hooks/api/workspace/types";
export const useGetProjectTypeFromRoute = () => {
const { location } = useRouterState();
const segment = location.pathname.split("/")[2];
if (!Object.values(ProjectType).includes(segment as ProjectType)) return undefined;
return segment as ProjectType;
};
@@ -11,11 +11,12 @@ import { BreadcrumbContainer, TBreadcrumbFormat } from "@app/components/v2";
import { OrgPermissionSubjects, useOrgPermission, useServerConfig } from "@app/context";
import { OrgPermissionSecretShareAction } from "@app/context/OrgPermissionContext/types";
import { usePopUp } from "@app/hooks";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { InsecureConnectionBanner } from "./components/InsecureConnectionBanner";
import { MinimizedOrgSidebar } from "./components/MinimizedOrgSidebar";
import { SidebarHeader } from "./components/SidebarHeader";
import { DefaultSideBar, SecretSharingSideBar } from "./ProductsSideBar";
import { DefaultSideBar, ProjectOverviewSideBar, SecretSharingSideBar } from "./ProductsSideBar";
export const OrganizationLayout = () => {
const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context });
@@ -45,21 +46,38 @@ export const OrganizationLayout = () => {
] as string[]
).includes(location.pathname);
const isProjectOverviewOrSettingsPage = (
[
linkOptions({ to: "/organization/secret-manager/overview" }).to,
linkOptions({ to: "/organization/secret-manager/settings" }).to,
linkOptions({ to: "/organization/cert-manager/overview" }).to,
linkOptions({ to: "/organization/cert-manager/settings" }).to,
linkOptions({ to: "/organization/kms/overview" }).to,
linkOptions({ to: "/organization/kms/settings" }).to,
linkOptions({ to: "/organization/ssh/overview" }).to,
linkOptions({ to: "/organization/ssh/settings" }).to
] as string[]
).includes(location.pathname);
const shouldShowOrgSidebar =
location.pathname.startsWith("/organization") &&
(!isSecretSharingPage || shouldShowProductsSidebar) &&
!(
[
linkOptions({ to: "/organization/secret-manager/overview" }).to,
linkOptions({ to: "/organization/cert-manager/overview" }).to,
linkOptions({ to: "/organization/ssh/overview" }).to,
linkOptions({ to: "/organization/kms/overview" }).to,
linkOptions({ to: "/organization/secret-scanning" }).to
] as string[]
).includes(location.pathname);
!([linkOptions({ to: "/organization/secret-scanning" }).to] as string[]).includes(
location.pathname
);
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
let SideBarComponent = <DefaultSideBar />;
if (isSecretSharingPage) {
SideBarComponent = <SecretSharingSideBar />;
} else if (isProjectOverviewOrSettingsPage) {
SideBarComponent = (
<ProjectOverviewSideBar type={location.pathname.split("/")[2] as ProjectType} />
);
}
return (
<>
<Banner />
@@ -80,10 +98,12 @@ export const OrganizationLayout = () => {
className="dark w-60 overflow-hidden border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900"
>
<nav className="items-between flex h-full flex-col overflow-y-auto dark:[color-scheme:dark]">
<div className="p-2 pt-3">
<SidebarHeader />
</div>
{isSecretSharingPage ? <SecretSharingSideBar /> : <DefaultSideBar />}
{!isProjectOverviewOrSettingsPage && (
<div className="p-2 pt-3">
<SidebarHeader />
</div>
)}
{SideBarComponent}
</nav>
</motion.div>
)}
@@ -0,0 +1,94 @@
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, useMatchRoute } from "@tanstack/react-router";
import { Menu, MenuGroup, MenuItem } from "@app/components/v2";
import { ProjectType } from "@app/hooks/api/workspace/types";
type TProjectOverviewSideBarProps = {
type: ProjectType;
};
export const ProjectOverviewSideBar = ({ type }: TProjectOverviewSideBarProps) => {
const matchRoute = useMatchRoute();
const isOverviewActive = !!matchRoute({
to: `/organization/${type}/overview`,
fuzzy: false
});
let label: string;
let icon: string;
let link: string;
switch (type) {
case ProjectType.CertificateManager:
label = "Cert Management";
icon = "note";
link = "https://infisical.com/docs/documentation/platform/pki/overview";
break;
case ProjectType.SecretManager:
label = "Secret Management";
icon = "sliding-carousel";
link = "https://infisical.com/docs/documentation/getting-started/introduction";
break;
case ProjectType.KMS:
label = "KMS";
icon = "unlock";
link = "https://infisical.com/docs/documentation/platform/kms/overview";
break;
case ProjectType.SSH:
label = "SSH";
icon = "verified";
link = "https://infisical.com/docs/documentation/platform/ssh/overview";
break;
default:
throw new Error("Unknown project type");
}
return (
<>
<div className="p-2 pt-3">
<a
target="_blank"
rel="noopener noreferrer"
href={link}
className="flex w-full items-center rounded-md border border-mineshaft-600 p-2 pl-3 transition-all duration-150 hover:bg-mineshaft-700"
>
<div className="mr-2 flex h-6 w-6 items-center justify-center rounded-md bg-mineshaft-500 p-4">
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
className="h-3.5 w-3.5 text-mineshaft-300"
/>
</div>
<div className="-mt-1 flex flex-grow flex-col text-white">
<div className="max-w-36 truncate text-ellipsis text-sm font-medium capitalize">
Infisical Docs
</div>
<div className="text-xs leading-[10px] text-mineshaft-400">
{type === ProjectType.SecretManager ? "Get Started" : `${label} Overview`}
</div>
</div>
</a>
</div>
<Menu>
<MenuGroup title="Overview">
<Link to={`/organization/${type}/overview`}>
<MenuItem isSelected={isOverviewActive} icon={icon}>
{label}
</MenuItem>
</Link>
</MenuGroup>
<MenuGroup title="Other">
<Link to={`/organization/${type}/settings`}>
{({ isActive }) => (
<MenuItem isSelected={isActive} icon="toggle-settings">
Settings
</MenuItem>
)}
</Link>
</MenuGroup>
</Menu>
</>
);
};
@@ -1,2 +1,3 @@
export * from "./DefaultSideBar";
export * from "./ProjectOverviewSideBar";
export * from "./SecretSharingSideBar";
@@ -269,7 +269,9 @@ export const MinimizedOrgSidebar = () => {
<MenuIconButton
isSelected={
isActive ||
window.location.pathname.startsWith(`/${ProjectType.SecretManager}`)
window.location.pathname.startsWith(
`/organization/${ProjectType.SecretManager}`
)
}
icon="sliding-carousel"
>
@@ -282,7 +284,9 @@ export const MinimizedOrgSidebar = () => {
<MenuIconButton
isSelected={
isActive ||
window.location.pathname.startsWith(`/${ProjectType.CertificateManager}`)
window.location.pathname.startsWith(
`/organization/${ProjectType.CertificateManager}`
)
}
icon="note"
>
@@ -294,7 +298,8 @@ export const MinimizedOrgSidebar = () => {
{({ isActive }) => (
<MenuIconButton
isSelected={
isActive || window.location.pathname.startsWith(`/${ProjectType.KMS}`)
isActive ||
window.location.pathname.startsWith(`/organization/${ProjectType.KMS}`)
}
icon="unlock"
>
@@ -306,7 +311,8 @@ export const MinimizedOrgSidebar = () => {
{({ isActive }) => (
<MenuIconButton
isSelected={
isActive || window.location.pathname.startsWith(`/${ProjectType.SSH}`)
isActive ||
window.location.pathname.startsWith(`/organization/${ProjectType.SSH}`)
}
icon="verified"
>
@@ -0,0 +1,20 @@
import { Helmet } from "react-helmet";
import { ProjectSettings } from "@app/components/projects/ProjectSettings";
import { PageHeader } from "@app/components/v2";
export const CertManagerSettingsPage = () => {
return (
<>
<Helmet>
<title>Cert Managment Settings</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader title="Cert Management Settings" />
<ProjectSettings />
</div>
</div>
</>
);
};
@@ -0,0 +1,26 @@
import { faHome } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { CertManagerSettingsPage } from "./CertManagerSettingsPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings"
)({
component: CertManagerSettingsPage,
context: () => ({
breadcrumbs: [
{
label: "Products",
icon: () => <FontAwesomeIcon icon={faHome} />
},
{
label: "Cert Management",
link: linkOptions({ to: "/organization/cert-manager/overview" })
},
{
label: "Settings"
}
]
})
});
@@ -6,9 +6,9 @@ import { format } from "date-fns";
import { createNotification } from "@app/components/notifications";
import { IconButton, Tag, Td, Tooltip, Tr } from "@app/components/v2";
import { formatProjectRoleName } from "@app/helpers/roles";
import { useGetUserWorkspaces } from "@app/hooks/api";
import { IdentityMembership } from "@app/hooks/api/identities/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
export enum TabSections {
@@ -26,15 +26,6 @@ type Props = {
) => void;
};
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Admin) return "Admin";
if (role === ProjectMembershipRole.Member) return "Developer";
if (role === ProjectMembershipRole.Viewer) return "Viewer";
if (role === ProjectMembershipRole.NoAccess) return "No Access";
return role;
};
export const IdentityProjectRow = ({
membership: { id, createdAt, identity, project, roles },
handlePopUpOpen
@@ -80,7 +71,7 @@ export const IdentityProjectRow = ({
<Td>
<Tag size="xs">{project.type}</Tag>
</Td>
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
<Td>{`${formatProjectRoleName(roles[0].role, roles[0].customRoleName)}${
roles.length > 1 ? ` (+${roles.length - 1})` : ""
}`}</Td>
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
@@ -0,0 +1,20 @@
import { Helmet } from "react-helmet";
import { ProjectSettings } from "@app/components/projects/ProjectSettings";
import { PageHeader } from "@app/components/v2";
export const KmsSettingsPage = () => {
return (
<>
<Helmet>
<title>KMS Settings</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader title="KMS Settings" />
<ProjectSettings />
</div>
</div>
</>
);
};
@@ -0,0 +1,26 @@
import { faHome } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { KmsSettingsPage } from "./KmsSettingsPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/kms/settings"
)({
component: KmsSettingsPage,
context: () => ({
breadcrumbs: [
{
label: "Products",
icon: () => <FontAwesomeIcon icon={faHome} />
},
{
label: "KMS",
link: linkOptions({ to: "/organization/kms/overview" })
},
{
label: "Settings"
}
]
})
});
@@ -117,12 +117,8 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => {
});
reset();
} catch (err) {
console.error(err);
const error = err as any;
const text =
error?.response?.data?.message ??
`Failed to ${popUp?.role?.data ? "update" : "create"} role`;
} catch {
const text = `Failed to ${popUp?.role?.data ? "update" : "create"} role`;
createNotification({
text,
@@ -27,6 +27,7 @@ export const ProjectListToggle = ({ value, onChange }: Props) => {
value={value}
onValueChange={onChange}
className="absolute left-0 top-0 h-full w-full cursor-pointer opacity-0"
dropdownContainerClassName="mt-10 ml-5"
>
<SelectItem value={ProjectListView.MyProjects}>My Projects</SelectItem>
<SelectItem value={ProjectListView.AllProjects}>All Projects</SelectItem>
@@ -0,0 +1,20 @@
import { Helmet } from "react-helmet";
import { ProjectSettings } from "@app/components/projects/ProjectSettings";
import { PageHeader } from "@app/components/v2";
export const SecretManagerSettingsPage = () => {
return (
<>
<Helmet>
<title>Secret Management Settings</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader title="Secret Management Settings" />
<ProjectSettings />
</div>
</div>
</>
);
};
@@ -0,0 +1,26 @@
import { faHome } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { SecretManagerSettingsPage } from "./SecretManagerSettingsPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings"
)({
component: SecretManagerSettingsPage,
context: () => ({
breadcrumbs: [
{
label: "Products",
icon: () => <FontAwesomeIcon icon={faHome} />
},
{
label: "Secret Management",
link: linkOptions({ to: "/organization/secret-manager/overview" })
},
{
label: "Settings"
}
]
})
});
@@ -2,6 +2,7 @@ import { useState } from "react";
import { useSearch } from "@tanstack/react-router";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { ROUTE_PATHS } from "@app/const/routes";
import { AuditLogStreamsTab } from "../AuditLogStreamTab";
@@ -11,7 +12,6 @@ import { OrgAuthTab } from "../OrgAuthTab";
import { OrgEncryptionTab } from "../OrgEncryptionTab";
import { OrgGeneralTab } from "../OrgGeneralTab";
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab/OrgWorkflowIntegrationTab";
import { ProjectTemplatesTab } from "../ProjectTemplatesTab";
export const OrgTabGroup = () => {
const search = useSearch({
@@ -28,7 +28,34 @@ export const OrgTabGroup = () => {
},
{ name: "Audit Log Streams", key: "tag-audit-log-streams", component: AuditLogStreamsTab },
{ name: "Import", key: "tab-import", component: ImportTab },
{ name: "Project Templates", key: "project-templates", component: ProjectTemplatesTab },
{
name: "Project Templates",
key: "project-templates",
// scott: temporary, remove once users have adjusted
// eslint-disable-next-line react/no-unstable-nested-components
component: () => (
<div>
<NoticeBannerV2
className="mx-auto mt-10 max-w-4xl"
title="Project Templates New Location"
>
<p className="text-sm text-bunker-200">
Project templates have been moved to the Feature Settings page, under the &#34;Project
Templates&#34; tab.
</p>
<p className="mt-2 text-sm text-bunker-200">
Project templates are now product-specific, and can be configured for each project
type.
</p>
<img
src="/images/project-templates/project-templates-new-location.png"
className="mt-4 w-full max-w-4xl rounded"
alt="Project Templates New Location"
/>
</NoticeBannerV2>
</div>
)
},
{ name: "KMIP", key: "kmip", component: KmipTab }
];
@@ -0,0 +1,20 @@
import { Helmet } from "react-helmet";
import { ProjectSettings } from "@app/components/projects/ProjectSettings";
import { PageHeader } from "@app/components/v2";
export const SshSettingsPage = () => {
return (
<>
<Helmet>
<title>SSH Settings</title>
</Helmet>
<div className="flex w-full justify-center bg-bunker-800 text-white">
<div className="w-full max-w-7xl">
<PageHeader title="SSH Settings" />
<ProjectSettings />
</div>
</div>
</>
);
};
@@ -0,0 +1,26 @@
import { faHome } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { SshSettingsPage } from "./SshSettingsPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings"
)({
component: SshSettingsPage,
context: () => ({
breadcrumbs: [
{
label: "Products",
icon: () => <FontAwesomeIcon icon={faHome} />
},
{
label: "SSH",
link: linkOptions({ to: "/organization/ssh/overview" })
},
{
label: "Settings"
}
]
})
});
@@ -5,8 +5,8 @@ import { useNavigate } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications";
import { IconButton, Tag, Td, Tooltip, Tr } from "@app/components/v2";
import { formatProjectRoleName } from "@app/helpers/roles";
import { useGetUserWorkspaces } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { TWorkspaceUser } from "@app/hooks/api/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { OrgAccessControlTabSections } from "@app/types/org";
@@ -19,15 +19,6 @@ type Props = {
) => void;
};
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Admin) return "Admin";
if (role === ProjectMembershipRole.Member) return "Developer";
if (role === ProjectMembershipRole.Viewer) return "Viewer";
if (role === ProjectMembershipRole.NoAccess) return "No Access";
return role;
};
export const UserProjectRow = ({
membership: { id, project, user, roles },
handlePopUpOpen
@@ -73,7 +64,7 @@ export const UserProjectRow = ({
<Td>
<Tag size="xs">{project.type}</Tag>
</Td>
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
<Td>{`${formatProjectRoleName(roles[0].role, roles[0].customRoleName)}${
roles.length > 1 ? ` (+${roles.length - 1})` : ""
}`}</Td>
<Td>
@@ -24,10 +24,10 @@ import {
Tooltip
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { formatProjectRoleName } from "@app/helpers/roles";
import { usePopUp } from "@app/hooks";
import { useGetProjectRoles, useUpdateGroupWorkspaceRole } from "@app/hooks/api";
import { TGroupMembership } from "@app/hooks/api/groups/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectUserMembershipTemporaryMode } from "@app/hooks/api/workspace/types";
import { groupBy } from "@app/lib/fn/array";
@@ -260,12 +260,6 @@ export const GroupRoles = ({ roles = [], disableEdit = false, groupId }: TMember
}
};
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Member) return "Developer";
return role;
};
return (
<div className="flex items-center space-x-2">
{roles
@@ -275,7 +269,7 @@ export const GroupRoles = ({ roles = [], disableEdit = false, groupId }: TMember
return (
<Tag key={id} className="capitalize">
<div className="flex items-center space-x-2">
<div>{formatRoleName(role, customRoleName)}</div>
<div>{formatProjectRoleName(role, customRoleName)}</div>
{isTemporary && (
<div>
<Tooltip content={isExpired ? "Expired Temporary Access" : "Temporary Access"}>
@@ -303,7 +297,7 @@ export const GroupRoles = ({ roles = [], disableEdit = false, groupId }: TMember
return (
<Tag key={id} className="capitalize">
<div className="flex items-center space-x-2">
<div>{formatRoleName(role, customRoleName)}</div>
<div>{formatProjectRoleName(role, customRoleName)}</div>
{isTemporary && (
<div>
<Tooltip
@@ -41,11 +41,11 @@ import {
Tr
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { formatProjectRoleName } from "@app/helpers/roles";
import { withProjectPermission } from "@app/hoc";
import { usePagination, useResetPageHelper } from "@app/hooks";
import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectIdentityOrderBy } from "@app/hooks/api/workspace/types";
import { usePopUp } from "@app/hooks/usePopUp";
@@ -53,12 +53,6 @@ import { IdentityModal } from "./components/IdentityModal";
const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Member) return "Developer";
if (role === ProjectMembershipRole.NoAccess) return "No access";
return role;
};
export const IdentityTab = withProjectPermission(
() => {
const { currentWorkspace } = useWorkspace();
@@ -286,7 +280,7 @@ export const IdentityTab = withProjectPermission(
<Tag key={roleId}>
<div className="flex items-center space-x-2">
<div className="capitalize">
{formatRoleName(role, customRoleName)}
{formatProjectRoleName(role, customRoleName)}
</div>
{isTemporary && (
<div>
@@ -331,7 +325,9 @@ export const IdentityTab = withProjectPermission(
return (
<Tag key={roleId} className="capitalize">
<div className="flex items-center space-x-2">
<div>{formatRoleName(role, customRoleName)}</div>
<div>
{formatProjectRoleName(role, customRoleName)}
</div>
{isTemporary && (
<div>
<Tooltip
@@ -50,19 +50,13 @@ import {
useUser,
useWorkspace
} from "@app/context";
import { formatProjectRoleName } from "@app/helpers/roles";
import { usePagination, useResetPageHelper } from "@app/hooks";
import { useGetProjectRoles, useGetWorkspaceUsers } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Member) return "Developer";
if (role === ProjectMembershipRole.NoAccess) return "No access";
return role;
};
type Props = {
handlePopUpOpen: (
@@ -343,7 +337,7 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => {
<Tag key={id}>
<div className="flex items-center space-x-2">
<div className="capitalize">
{formatRoleName(role, customRoleName)}
{formatProjectRoleName(role, customRoleName)}
</div>
{isTemporary && (
<div>
@@ -386,7 +380,7 @@ export const MembersTable = ({ handlePopUpOpen }: Props) => {
return (
<Tag key={id} className="capitalize">
<div className="flex items-center space-x-2">
<div>{formatRoleName(role, customRoleName)}</div>
<div>{formatProjectRoleName(role, customRoleName)}</div>
{isTemporary && (
<div>
<Tooltip
@@ -24,7 +24,7 @@ import {
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useDeleteProjectRole, useGetProjectRoles } from "@app/hooks/api";
import { TProjectRole } from "@app/hooks/api/roles/types";
import { ProjectMembershipRole, TProjectRole } from "@app/hooks/api/roles/types";
import { RoleModal } from "@app/pages/project/RoleDetailsBySlugPage/components/RoleModal";
export const ProjectRoleList = () => {
@@ -86,7 +86,9 @@ export const ProjectRoleList = () => {
{isRolesLoading && <TableSkeleton columns={4} innerKey="org-roles" />}
{roles?.map((role) => {
const { id, name, slug } = role;
const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug);
const isNonMutatable = Object.values(ProjectMembershipRole).includes(
slug as ProjectMembershipRole
);
return (
<Tr
@@ -50,6 +50,7 @@ import {
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
ProjectTypePermissionSubjects,
rolePermission2Form
} from "@app/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection";
@@ -269,6 +270,12 @@ export const IdentityProjectAdditionalPrivilegeModifySection = ({
</DropdownMenuTrigger>
<DropdownMenuContent className="thin-scrollbar max-h-96" align="end">
{Object.keys(PROJECT_PERMISSION_OBJECT)
.filter(
(sub) =>
ProjectTypePermissionSubjects[currentWorkspace.type][
sub as ProjectPermissionSub
]
)
.sort((a, b) =>
PROJECT_PERMISSION_OBJECT[a as keyof typeof PROJECT_PERMISSION_OBJECT].title
.toLowerCase()
@@ -49,6 +49,7 @@ import {
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
ProjectTypePermissionSubjects,
rolePermission2Form
} from "@app/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils";
import { renderConditionalComponents } from "@app/pages/project/RoleDetailsBySlugPage/components/RolePermissionsSection";
@@ -265,6 +266,12 @@ export const MembershipProjectAdditionalPrivilegeModifySection = ({
</DropdownMenuTrigger>
<DropdownMenuContent className="thin-scrollbar max-h-96" align="end">
{Object.keys(PROJECT_PERMISSION_OBJECT)
.filter(
(subject) =>
ProjectTypePermissionSubjects[currentWorkspace.type][
subject as ProjectPermissionSub
]
)
.sort((a, b) =>
PROJECT_PERMISSION_OBJECT[a as keyof typeof PROJECT_PERMISSION_OBJECT].title
.toLowerCase()
@@ -17,6 +17,7 @@ import {
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { useDeleteProjectRole, useGetProjectRoleBySlug } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { usePopUp } from "@app/hooks/usePopUp";
import { ProjectAccessControlTabs } from "@app/types/project";
@@ -77,7 +78,9 @@ const Page = () => {
}
};
const isCustomRole = !["admin", "member", "viewer", "no-access"].includes(data?.slug ?? "");
const isCustomRole = !Object.values(ProjectMembershipRole).includes(
(data?.slug ?? "") as ProjectMembershipRole
);
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
@@ -25,6 +25,7 @@ import {
TPermissionConditionOperators
} from "@app/context/ProjectPermissionContext/types";
import { TProjectPermission } from "@app/hooks/api/roles/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
const GeneralPolicyActionSchema = z.object({
read: z.boolean().optional(),
@@ -1122,7 +1123,7 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
]
},
[ProjectPermissionSub.SecretApproval]: {
title: "Secret Protect policy",
title: "Secret Approval Policies",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
@@ -1195,3 +1196,87 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
]
}
};
const SharedPermissionSubjects = {
[ProjectPermissionSub.AuditLogs]: true,
[ProjectPermissionSub.Groups]: true,
[ProjectPermissionSub.Member]: true,
[ProjectPermissionSub.Identity]: true,
[ProjectPermissionSub.Project]: true,
[ProjectPermissionSub.Role]: true,
[ProjectPermissionSub.Settings]: true
};
const SecretsManagerPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.SecretFolders]: enabled,
[ProjectPermissionSub.SecretImports]: enabled,
[ProjectPermissionSub.DynamicSecrets]: enabled,
[ProjectPermissionSub.Secrets]: enabled,
[ProjectPermissionSub.SecretApproval]: enabled,
[ProjectPermissionSub.Integrations]: enabled,
[ProjectPermissionSub.SecretSyncs]: enabled,
[ProjectPermissionSub.Kms]: enabled,
[ProjectPermissionSub.Environments]: enabled,
[ProjectPermissionSub.Tags]: enabled,
[ProjectPermissionSub.Webhooks]: enabled,
[ProjectPermissionSub.IpAllowList]: enabled,
[ProjectPermissionSub.SecretRollback]: enabled,
[ProjectPermissionSub.SecretRotation]: enabled,
[ProjectPermissionSub.ServiceTokens]: enabled
});
const KmsPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.Cmek]: enabled,
[ProjectPermissionSub.Kmip]: enabled
});
const CertificateManagerPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.PkiCollections]: enabled,
[ProjectPermissionSub.PkiAlerts]: enabled,
[ProjectPermissionSub.CertificateAuthorities]: enabled,
[ProjectPermissionSub.CertificateTemplates]: enabled,
[ProjectPermissionSub.Certificates]: enabled
});
const SshPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.SshCertificateAuthorities]: enabled,
[ProjectPermissionSub.SshCertificates]: enabled,
[ProjectPermissionSub.SshCertificateTemplates]: enabled,
[ProjectPermissionSub.SshHosts]: enabled,
[ProjectPermissionSub.SshHostGroups]: enabled
});
// scott: this structure ensures we don't forget to add project permissions to their relevant project type
export const ProjectTypePermissionSubjects: Record<
ProjectType,
Record<ProjectPermissionSub, boolean>
> = {
[ProjectType.SecretManager]: {
...SharedPermissionSubjects,
...SecretsManagerPermissionSubjects(true),
...KmsPermissionSubjects(),
...CertificateManagerPermissionSubjects(),
...SshPermissionSubjects()
},
[ProjectType.KMS]: {
...SharedPermissionSubjects,
...KmsPermissionSubjects(true),
...SecretsManagerPermissionSubjects(),
...CertificateManagerPermissionSubjects(),
...SshPermissionSubjects()
},
[ProjectType.CertificateManager]: {
...SharedPermissionSubjects,
...CertificateManagerPermissionSubjects(true),
...KmsPermissionSubjects(),
...SecretsManagerPermissionSubjects(),
...SshPermissionSubjects()
},
[ProjectType.SSH]: {
...SharedPermissionSubjects,
...SshPermissionSubjects(true),
...CertificateManagerPermissionSubjects(),
...KmsPermissionSubjects(),
...SecretsManagerPermissionSubjects()
}
};
@@ -6,6 +6,7 @@ import { IconButton, Tooltip } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useGetProjectRoleBySlug } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
@@ -21,7 +22,9 @@ export const RoleDetailsSection = ({ roleSlug, handlePopUpOpen }: Props) => {
const { currentWorkspace } = useWorkspace();
const { data } = useGetProjectRoleBySlug(currentWorkspace?.id ?? "", roleSlug as string);
const isCustomRole = !["admin", "member", "viewer", "no-access"].includes(data?.slug ?? "");
const isCustomRole = !Object.values(ProjectMembershipRole).includes(
(data?.slug ?? "") as ProjectMembershipRole
);
return data ? (
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
@@ -115,12 +115,8 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => {
});
reset();
} catch (err) {
console.error(err);
const error = err as any;
const text =
error?.response?.data?.message ??
`Failed to ${popUp?.role?.data ? "update" : "create"} role`;
} catch {
const text = `Failed to ${popUp?.role?.data ? "update" : "create"} role`;
createNotification({
text,
@@ -19,6 +19,7 @@ import { ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
import { evaluatePermissionsAbility } from "@app/helpers/permissions";
import { useGetProjectRoleBySlug, useUpdateProjectRole } from "@app/hooks/api";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions";
@@ -31,6 +32,7 @@ import {
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
projectRoleFormSchema,
ProjectTypePermissionSubjects,
rolePermission2Form,
TFormSchema
} from "./ProjectRoleModifySection.utils";
@@ -104,7 +106,9 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
}
};
const isCustomRole = !["admin", "member", "viewer", "no-access"].includes(role?.slug ?? "");
const isCustomRole = !Object.values(ProjectMembershipRole).includes(
(role?.slug ?? "") as ProjectMembershipRole
);
const onNewPolicy = (selectedSubject: ProjectPermissionSub) => {
const rootPolicyValue = form.getValues(`permissions.${selectedSubject}`);
@@ -192,6 +196,12 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
</DropdownMenuTrigger>
<DropdownMenuContent className="thin-scrollbar max-h-96" align="end">
{Object.keys(PROJECT_PERMISSION_OBJECT)
.filter(
(subject) =>
ProjectTypePermissionSubjects[currentWorkspace.type][
subject as ProjectPermissionSub
]
)
.sort((a, b) =>
PROJECT_PERMISSION_OBJECT[
a as keyof typeof PROJECT_PERMISSION_OBJECT
@@ -221,17 +231,19 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
</div>
<div className="py-4">
{!isPending && <PermissionEmptyState />}
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[]).map((subject) => (
<GeneralPermissionPolicies
subject={subject}
actions={PROJECT_PERMISSION_OBJECT[subject].actions}
title={PROJECT_PERMISSION_OBJECT[subject].title}
key={`project-permission-${subject}`}
isDisabled={isDisabled}
>
{renderConditionalComponents(subject, isDisabled)}
</GeneralPermissionPolicies>
))}
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[])
.filter((subject) => ProjectTypePermissionSubjects[currentWorkspace.type][subject])
.map((subject) => (
<GeneralPermissionPolicies
subject={subject}
actions={PROJECT_PERMISSION_OBJECT[subject].actions}
title={PROJECT_PERMISSION_OBJECT[subject].title}
key={`project-permission-${subject}`}
isDisabled={isDisabled}
>
{renderConditionalComponents(subject, isDisabled)}
</GeneralPermissionPolicies>
))}
</div>
</FormProvider>
</form>
+121 -1
View File
@@ -52,14 +52,18 @@ import { Route as sshLayoutImport } from './pages/ssh/layout'
import { Route as secretManagerLayoutImport } from './pages/secret-manager/layout'
import { Route as kmsLayoutImport } from './pages/kms/layout'
import { Route as certManagerLayoutImport } from './pages/cert-manager/layout'
import { Route as organizationSshSettingsPageRouteImport } from './pages/organization/SshSettingsPage/route'
import { Route as organizationSshOverviewPageRouteImport } from './pages/organization/SshOverviewPage/route'
import { Route as organizationSecretSharingSettingsPageRouteImport } from './pages/organization/SecretSharingSettingsPage/route'
import { Route as organizationSecretManagerSettingsPageRouteImport } from './pages/organization/SecretManagerSettingsPage/route'
import { Route as organizationSecretManagerOverviewPageRouteImport } from './pages/organization/SecretManagerOverviewPage/route'
import { Route as organizationRoleByIDPageRouteImport } from './pages/organization/RoleByIDPage/route'
import { Route as organizationUserDetailsByIDPageRouteImport } from './pages/organization/UserDetailsByIDPage/route'
import { Route as organizationKmsSettingsPageRouteImport } from './pages/organization/KmsSettingsPage/route'
import { Route as organizationKmsOverviewPageRouteImport } from './pages/organization/KmsOverviewPage/route'
import { Route as organizationIdentityDetailsByIDPageRouteImport } from './pages/organization/IdentityDetailsByIDPage/route'
import { Route as organizationGroupDetailsByIDPageRouteImport } from './pages/organization/GroupDetailsByIDPage/route'
import { Route as organizationCertManagerSettingsPageRouteImport } from './pages/organization/CertManagerSettingsPage/route'
import { Route as organizationCertManagerOverviewPageRouteImport } from './pages/organization/CertManagerOverviewPage/route'
import { Route as organizationSettingsPageRouteImport } from './pages/organization/SettingsPage/route'
import { Route as organizationSecretSharingPageRouteImport } from './pages/organization/SecretSharingPage/route'
@@ -604,6 +608,14 @@ const certManagerLayoutRoute = certManagerLayoutImport.update({
AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRoute,
} as any)
const organizationSshSettingsPageRouteRoute =
organizationSshSettingsPageRouteImport.update({
id: '/ssh/settings',
path: '/ssh/settings',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationSshOverviewPageRouteRoute =
organizationSshOverviewPageRouteImport.update({
id: '/ssh/overview',
@@ -620,6 +632,14 @@ const organizationSecretSharingSettingsPageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute,
} as any)
const organizationSecretManagerSettingsPageRouteRoute =
organizationSecretManagerSettingsPageRouteImport.update({
id: '/secret-manager/settings',
path: '/secret-manager/settings',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationSecretManagerOverviewPageRouteRoute =
organizationSecretManagerOverviewPageRouteImport.update({
id: '/secret-manager/overview',
@@ -644,6 +664,14 @@ const organizationUserDetailsByIDPageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationKmsSettingsPageRouteRoute =
organizationKmsSettingsPageRouteImport.update({
id: '/kms/settings',
path: '/kms/settings',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationKmsOverviewPageRouteRoute =
organizationKmsOverviewPageRouteImport.update({
id: '/kms/overview',
@@ -668,6 +696,14 @@ const organizationGroupDetailsByIDPageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationCertManagerSettingsPageRouteRoute =
organizationCertManagerSettingsPageRouteImport.update({
id: '/cert-manager/settings',
path: '/cert-manager/settings',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
} as any)
const organizationCertManagerOverviewPageRouteRoute =
organizationCertManagerOverviewPageRouteImport.update({
id: '/cert-manager/overview',
@@ -2072,6 +2108,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof organizationCertManagerOverviewPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings'
path: '/cert-manager/settings'
fullPath: '/organization/cert-manager/settings'
preLoaderRoute: typeof organizationCertManagerSettingsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId'
path: '/groups/$groupId'
@@ -2093,6 +2136,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof organizationKmsOverviewPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/kms/settings': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/kms/settings'
path: '/kms/settings'
fullPath: '/organization/kms/settings'
preLoaderRoute: typeof organizationKmsSettingsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId'
path: '/members/$membershipId'
@@ -2114,6 +2164,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof organizationSecretManagerOverviewPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings'
path: '/secret-manager/settings'
fullPath: '/organization/secret-manager/settings'
preLoaderRoute: typeof organizationSecretManagerSettingsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings'
path: '/settings'
@@ -2128,6 +2185,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof organizationSshOverviewPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings': {
id: '/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings'
path: '/ssh/settings'
fullPath: '/organization/ssh/settings'
preLoaderRoute: typeof organizationSshSettingsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
}
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': {
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
path: ''
@@ -3168,13 +3232,17 @@ interface AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren {
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSettingsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSettingsRouteWithChildren
organizationCertManagerOverviewPageRouteRoute: typeof organizationCertManagerOverviewPageRouteRoute
organizationCertManagerSettingsPageRouteRoute: typeof organizationCertManagerSettingsPageRouteRoute
organizationGroupDetailsByIDPageRouteRoute: typeof organizationGroupDetailsByIDPageRouteRoute
organizationIdentityDetailsByIDPageRouteRoute: typeof organizationIdentityDetailsByIDPageRouteRoute
organizationKmsOverviewPageRouteRoute: typeof organizationKmsOverviewPageRouteRoute
organizationKmsSettingsPageRouteRoute: typeof organizationKmsSettingsPageRouteRoute
organizationUserDetailsByIDPageRouteRoute: typeof organizationUserDetailsByIDPageRouteRoute
organizationRoleByIDPageRouteRoute: typeof organizationRoleByIDPageRouteRoute
organizationSecretManagerOverviewPageRouteRoute: typeof organizationSecretManagerOverviewPageRouteRoute
organizationSecretManagerSettingsPageRouteRoute: typeof organizationSecretManagerSettingsPageRouteRoute
organizationSshOverviewPageRouteRoute: typeof organizationSshOverviewPageRouteRoute
organizationSshSettingsPageRouteRoute: typeof organizationSshSettingsPageRouteRoute
}
const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren: AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren =
@@ -3196,19 +3264,27 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren: Authentica
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSettingsRouteWithChildren,
organizationCertManagerOverviewPageRouteRoute:
organizationCertManagerOverviewPageRouteRoute,
organizationCertManagerSettingsPageRouteRoute:
organizationCertManagerSettingsPageRouteRoute,
organizationGroupDetailsByIDPageRouteRoute:
organizationGroupDetailsByIDPageRouteRoute,
organizationIdentityDetailsByIDPageRouteRoute:
organizationIdentityDetailsByIDPageRouteRoute,
organizationKmsOverviewPageRouteRoute:
organizationKmsOverviewPageRouteRoute,
organizationKmsSettingsPageRouteRoute:
organizationKmsSettingsPageRouteRoute,
organizationUserDetailsByIDPageRouteRoute:
organizationUserDetailsByIDPageRouteRoute,
organizationRoleByIDPageRouteRoute: organizationRoleByIDPageRouteRoute,
organizationSecretManagerOverviewPageRouteRoute:
organizationSecretManagerOverviewPageRouteRoute,
organizationSecretManagerSettingsPageRouteRoute:
organizationSecretManagerSettingsPageRouteRoute,
organizationSshOverviewPageRouteRoute:
organizationSshOverviewPageRouteRoute,
organizationSshSettingsPageRouteRoute:
organizationSshSettingsPageRouteRoute,
}
const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren =
@@ -3893,14 +3969,18 @@ export interface FileRoutesByFullPath {
'/organization/secret-sharing/': typeof organizationSecretSharingPageRouteRoute
'/organization/settings/': typeof organizationSettingsPageRouteRoute
'/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute
'/organization/cert-manager/settings': typeof organizationCertManagerSettingsPageRouteRoute
'/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute
'/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute
'/organization/kms/overview': typeof organizationKmsOverviewPageRouteRoute
'/organization/kms/settings': typeof organizationKmsSettingsPageRouteRoute
'/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute
'/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute
'/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute
'/organization/secret-manager/settings': typeof organizationSecretManagerSettingsPageRouteRoute
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
@@ -4072,14 +4152,18 @@ export interface FileRoutesByTo {
'/organization/secret-sharing': typeof organizationSecretSharingPageRouteRoute
'/organization/settings': typeof organizationSettingsPageRouteRoute
'/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute
'/organization/cert-manager/settings': typeof organizationCertManagerSettingsPageRouteRoute
'/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute
'/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute
'/organization/kms/overview': typeof organizationKmsOverviewPageRouteRoute
'/organization/kms/settings': typeof organizationKmsSettingsPageRouteRoute
'/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute
'/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute
'/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute
'/organization/secret-manager/settings': typeof organizationSecretManagerSettingsPageRouteRoute
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
'/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute
@@ -4264,14 +4348,18 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/': typeof organizationSecretSharingPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/settings/': typeof organizationSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings': typeof organizationCertManagerSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/kms/overview': typeof organizationKmsOverviewPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/kms/settings': typeof organizationKmsSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings': typeof organizationSecretManagerSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': typeof certManagerLayoutRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout': typeof kmsLayoutRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout': typeof secretManagerLayoutRouteWithChildren
@@ -4457,14 +4545,18 @@ export interface FileRouteTypes {
| '/organization/secret-sharing/'
| '/organization/settings/'
| '/organization/cert-manager/overview'
| '/organization/cert-manager/settings'
| '/organization/groups/$groupId'
| '/organization/identities/$identityId'
| '/organization/kms/overview'
| '/organization/kms/settings'
| '/organization/members/$membershipId'
| '/organization/roles/$roleId'
| '/organization/secret-manager/overview'
| '/organization/secret-manager/settings'
| '/organization/secret-sharing/settings'
| '/organization/ssh/overview'
| '/organization/ssh/settings'
| '/cert-manager/$projectId/alerting'
| '/cert-manager/$projectId/certificate-authorities'
| '/cert-manager/$projectId/overview'
@@ -4635,14 +4727,18 @@ export interface FileRouteTypes {
| '/organization/secret-sharing'
| '/organization/settings'
| '/organization/cert-manager/overview'
| '/organization/cert-manager/settings'
| '/organization/groups/$groupId'
| '/organization/identities/$identityId'
| '/organization/kms/overview'
| '/organization/kms/settings'
| '/organization/members/$membershipId'
| '/organization/roles/$roleId'
| '/organization/secret-manager/overview'
| '/organization/secret-manager/settings'
| '/organization/secret-sharing/settings'
| '/organization/ssh/overview'
| '/organization/ssh/settings'
| '/cert-manager/$projectId/alerting'
| '/cert-manager/$projectId/certificate-authorities'
| '/cert-manager/$projectId/overview'
@@ -4825,14 +4921,18 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/'
| '/_authenticate/_inject-org-details/_org-layout/organization/settings/'
| '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview'
| '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings'
| '/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId'
| '/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId'
| '/_authenticate/_inject-org-details/_org-layout/organization/kms/overview'
| '/_authenticate/_inject-org-details/_org-layout/organization/kms/settings'
| '/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId'
| '/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId'
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview'
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings'
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings'
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview'
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings'
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout'
@@ -5209,13 +5309,17 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing",
"/_authenticate/_inject-org-details/_org-layout/organization/settings",
"/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview",
"/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings",
"/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId",
"/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId",
"/_authenticate/_inject-org-details/_org-layout/organization/kms/overview",
"/_authenticate/_inject-org-details/_org-layout/organization/kms/settings",
"/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId",
"/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId",
"/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview",
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview"
"/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings",
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview",
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings"
]
},
"/_authenticate/_inject-org-details/admin/_admin-layout": {
@@ -5328,6 +5432,10 @@ export const routeTree = rootRoute
"filePath": "organization/CertManagerOverviewPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/settings": {
"filePath": "organization/CertManagerSettingsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId": {
"filePath": "organization/GroupDetailsByIDPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
@@ -5340,6 +5448,10 @@ export const routeTree = rootRoute
"filePath": "organization/KmsOverviewPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/kms/settings": {
"filePath": "organization/KmsSettingsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId": {
"filePath": "organization/UserDetailsByIDPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
@@ -5352,6 +5464,10 @@ export const routeTree = rootRoute
"filePath": "organization/SecretManagerOverviewPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/settings": {
"filePath": "organization/SecretManagerSettingsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings": {
"filePath": "organization/SecretSharingSettingsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing"
@@ -5360,6 +5476,10 @@ export const routeTree = rootRoute
"filePath": "organization/SshOverviewPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings": {
"filePath": "organization/SshSettingsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
},
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout": {
"filePath": "cert-manager/layout.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId",
+4
View File
@@ -9,9 +9,13 @@ const adminRoute = route("/admin", [
const organizationRoutes = route("/organization", [
route("/secret-manager/overview", "organization/SecretManagerOverviewPage/route.tsx"),
route("/secret-manager/settings", "organization/SecretManagerSettingsPage/route.tsx"),
route("/cert-manager/overview", "organization/CertManagerOverviewPage/route.tsx"),
route("/cert-manager/settings", "organization/CertManagerSettingsPage/route.tsx"),
route("/ssh/overview", "organization/SshOverviewPage/route.tsx"),
route("/ssh/settings", "organization/SshSettingsPage/route.tsx"),
route("/kms/overview", "organization/KmsOverviewPage/route.tsx"),
route("/kms/settings", "organization/KmsSettingsPage/route.tsx"),
route("/access-management", "organization/AccessManagementPage/route.tsx"),
route("/admin", "organization/AdminPage/route.tsx"),
route("/audit-logs", "organization/AuditLogsPage/route.tsx"),