Move ssh back to project level

This commit is contained in:
Tuan Dang
2024-12-09 21:36:42 -08:00
parent 42249726d4
commit 7cf297344b
61 changed files with 837 additions and 635 deletions
@@ -23,10 +23,7 @@ export enum OrgPermissionSubjects {
Kms = "kms",
AdminConsole = "organization-admin-console",
AuditLogs = "audit-logs",
ProjectTemplates = "project-templates",
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificateTemplates = "ssh-certificate-templates",
SshCertificates = "ssh-certificates"
ProjectTemplates = "project-templates"
}
export enum OrgPermissionAdminConsoleAction {
@@ -50,9 +47,6 @@ export type OrgPermissionSet =
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateAuthorities]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificates]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateTemplates];
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates];
export type TOrgPermission = MongoAbility<OrgPermissionSet>;
@@ -81,6 +81,9 @@ export enum ProjectPermissionSub {
CertificateAuthorities = "certificate-authorities",
Certificates = "certificates",
CertificateTemplates = "certificate-templates",
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificateTemplates = "ssh-certificate-templates",
SshCertificates = "ssh-certificates",
PkiAlerts = "pki-alerts",
PkiCollections = "pki-collections",
Kms = "kms",
@@ -155,6 +158,9 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.Certificates]
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
+2 -4
View File
@@ -19,8 +19,6 @@ export {
useGetOrgPmtMethods,
useGetOrgTaxIds,
useGetOrgTrialUrl,
useListOrgSshCas,
useListOrgSshCertificates,
useListOrgSshCertificateTemplates,
useUpdateOrg,
useUpdateOrgBillingDetails} from "./queries";
useUpdateOrgBillingDetails
} from "./queries";
@@ -4,8 +4,6 @@ import { apiRequest } from "@app/config/request";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { TGroupOrgMembership } from "../groups/types";
import { TSshCertificate,TSshCertificateAuthority } from "../ssh-ca/types";
import { TSshCertificateTemplate } from "../sshCertificateTemplates/types";
import { IntegrationAuth } from "../types";
import {
BillingDetails,
@@ -43,12 +41,7 @@ export const organizationKeys = {
}: TListOrgIdentitiesDTO) =>
[...organizationKeys.getOrgIdentityMemberships(orgId), params] as const,
getOrgGroups: (orgId: string) => [{ orgId }, "organization-groups"] as const,
getOrgIntegrationAuths: (orgId: string) => [{ orgId }, "integration-auths"] as const,
getOrgSshCas: ({ orgId }: { orgId: string }) => [{ orgId }, "org-ssh-cas"] as const,
allOrgSshCertificates: () => ["org-ssh-certificates"] as const,
specificOrgSshCertificates: ({ offset, limit }: { offset: number; limit: number }) =>
[...organizationKeys.allOrgSshCertificates(), { offset, limit }] as const,
getOrgSshCertificateTemplates: () => ["org-ssh-certificate-templates"] as const
getOrgIntegrationAuths: (orgId: string) => [{ orgId }, "integration-auths"] as const
};
export const fetchOrganizations = async () => {
@@ -502,63 +495,3 @@ export const useGetOrgIntegrationAuths = <TData = IntegrationAuth[],>(
select
});
};
export const useListOrgSshCas = ({ orgId }: { orgId: string }) => {
return useQuery({
queryKey: organizationKeys.getOrgSshCas({ orgId }),
queryFn: async () => {
const {
data: { cas }
} = await apiRequest.get<{ cas: Omit<TSshCertificateAuthority, "publicKey">[] }>(
`/api/v1/organization/${orgId}/ssh-cas`
);
return cas;
},
enabled: Boolean(orgId)
});
};
export const useListOrgSshCertificates = ({
orgId,
offset,
limit
}: {
orgId: string;
offset: number;
limit: number;
}) => {
return useQuery({
queryKey: organizationKeys.specificOrgSshCertificates({
offset,
limit
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit)
});
const { data } = await apiRequest.get<{
certificates: TSshCertificate[];
totalCount: number;
}>(`/api/v1/organization/${orgId}/ssh-certificates`, {
params
});
return data;
},
enabled: Boolean(orgId)
});
};
export const useListOrgSshCertificateTemplates = ({ orgId }: { orgId: string }) => {
return useQuery({
queryKey: organizationKeys.getOrgSshCertificateTemplates(),
queryFn: async () => {
const { data } = await apiRequest.get<{ certificateTemplates: TSshCertificateTemplate[] }>(
`/api/v1/organization/${orgId}/ssh-certificate-templates`
);
return data;
},
enabled: Boolean(orgId)
});
};
+11 -11
View File
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries";
import { workspaceKeys } from "../workspace/query-keys";
import {
TCreateSshCaDTO,
TDeleteSshCaDTO,
@@ -27,8 +27,8 @@ export const useCreateSshCa = () => {
} = await apiRequest.post<{ ca: TSshCertificateAuthority }>("/api/v1/ssh/ca/", body);
return ca;
},
onSuccess: ({ orgId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId }));
onSuccess: ({ projectId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
}
});
};
@@ -42,8 +42,8 @@ export const useUpdateSshCa = () => {
} = await apiRequest.patch<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`, body);
return ca;
},
onSuccess: ({ orgId }, { caId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId }));
onSuccess: ({ projectId }, { caId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
queryClient.invalidateQueries(sshCaKeys.getSshCaById(caId));
}
});
@@ -58,8 +58,8 @@ export const useDeleteSshCa = () => {
} = await apiRequest.delete<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`);
return ca;
},
onSuccess: ({ orgId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId }));
onSuccess: ({ projectId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
}
});
};
@@ -71,8 +71,8 @@ export const useSignSshKey = () => {
const { data } = await apiRequest.post<TSignSshKeyResponse>("/api/v1/ssh/sign", body);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries(organizationKeys.allOrgSshCertificates());
onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries(workspaceKeys.allWorkspaceSshCertificates(projectId));
}
});
};
@@ -84,8 +84,8 @@ export const useIssueSshCreds = () => {
const { data } = await apiRequest.post<TIssueSshCredsResponse>("/api/v1/ssh/issue", body);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries(organizationKeys.allOrgSshCertificates());
onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries(workspaceKeys.allWorkspaceSshCertificates(projectId));
}
});
};
+4 -2
View File
@@ -7,7 +7,6 @@ export type TSshCertificate = {
sshCertificateTemplateId: string;
serialNumber: string;
certType: SshCertType;
publicKey: string;
principals: string[];
keyId: string;
notBefore: string;
@@ -16,7 +15,7 @@ export type TSshCertificate = {
export type TSshCertificateAuthority = {
id: string;
orgId: string;
projectId: string;
status: SshCaStatus;
friendlyName: string;
keyAlgorithm: CertKeyAlgorithm;
@@ -26,6 +25,7 @@ export type TSshCertificateAuthority = {
};
export type TCreateSshCaDTO = {
projectId: string;
friendlyName?: string;
keyAlgorithm: CertKeyAlgorithm;
};
@@ -41,6 +41,7 @@ export type TDeleteSshCaDTO = {
};
export type TSignSshKeyDTO = {
projectId: string;
templateName: string;
publicKey?: string;
certType: SshCertType;
@@ -55,6 +56,7 @@ export type TSignSshKeyResponse = {
};
export type TIssueSshCredsDTO = {
projectId: string;
templateName: string;
keyAlgorithm: CertKeyAlgorithm;
certType: SshCertType;
@@ -32,6 +32,9 @@ export {
useListWorkspaceGroups,
useListWorkspacePkiAlerts,
useListWorkspacePkiCollections,
useListWorkspaceSshCas,
useListWorkspaceSshCertificates,
useListWorkspaceSshCertificateTemplates,
useNameWorkspaceSecrets,
useToggleAutoCapitalization,
useUpdateIdentityWorkspaceRole,
@@ -15,6 +15,8 @@ import { TIntegration } from "../integrations/types";
import { TPkiAlert } from "../pkiAlerts/types";
import { TPkiCollection } from "../pkiCollections/types";
import { EncryptedSecret } from "../secrets/types";
import { TSshCertificate, TSshCertificateAuthority } from "../ssh-ca/types";
import { TSshCertificateTemplate } from "../sshCertificateTemplates/types";
import { userKeys } from "../users/query-keys";
import { TWorkspaceUser } from "../users/types";
import { ProjectSlackConfig } from "../workflowIntegrations/types";
@@ -713,6 +715,67 @@ export const useListWorkspaceCertificateTemplates = ({ workspaceId }: { workspac
});
};
export const useListWorkspaceSshCertificates = ({
offset,
limit,
projectId
}: {
offset: number;
limit: number;
projectId: string;
}) => {
return useQuery({
queryKey: workspaceKeys.specificWorkspaceSshCertificates({
offset,
limit,
projectId
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit)
});
const { data } = await apiRequest.get<{
certificates: TSshCertificate[];
totalCount: number;
}>(`/api/v2/workspace/${projectId}/ssh-certificates`, {
params
});
return data;
},
enabled: Boolean(projectId)
});
};
export const useListWorkspaceSshCas = (projectId: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceSshCas(projectId),
queryFn: async () => {
const {
data: { cas }
} = await apiRequest.get<{ cas: Omit<TSshCertificateAuthority, "publicKey">[] }>(
`/api/v2/workspace/${projectId}/ssh-cas`
);
return cas;
},
enabled: Boolean(projectId)
});
};
export const useListWorkspaceSshCertificateTemplates = (projectId: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceSshCertificateTemplates(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<{ certificateTemplates: TSshCertificateTemplate[] }>(
`/api/v2/workspace/${projectId}/ssh-certificate-templates`
);
return data;
},
enabled: Boolean(projectId)
});
};
export const useGetWorkspaceSlackConfig = ({ workspaceId }: { workspaceId: string }) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceSlackConfig(workspaceId),
@@ -52,5 +52,19 @@ export const workspaceKeys = {
getWorkspaceCertificateTemplates: (workspaceId: string) =>
[{ workspaceId }, "workspace-certificate-templates"] as const,
getWorkspaceSlackConfig: (workspaceId: string) =>
[{ workspaceId }, "workspace-slack-config"] as const
[{ workspaceId }, "workspace-slack-config"] as const,
getWorkspaceSshCas: (projectId: string) => [{ projectId }, "workspace-ssh-cas"] as const,
allWorkspaceSshCertificates: (projectId: string) =>
[{ projectId }, "workspace-ssh-certificates"] as const,
specificWorkspaceSshCertificates: ({
offset,
limit,
projectId
}: {
offset: number;
limit: number;
projectId: string;
}) => [...workspaceKeys.allWorkspaceSshCertificates(projectId), { offset, limit }] as const,
getWorkspaceSshCertificateTemplates: (projectId: string) =>
[{ projectId }, "workspace-ssh-certificate-templates"] as const
};
+10 -10
View File
@@ -424,6 +424,16 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?.id}/ssh`} passHref>
<a>
<MenuItem
isSelected={router.asPath === `/project/${currentWorkspace?.id}/ssh`}
icon="system-outline-90-lock-closed"
>
SSH
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?.id}/members`} passHref>
<a>
<MenuItem
@@ -513,16 +523,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem>
</a>
</Link>
<Link href={`/org/${currentOrg?.id}/ssh`} passHref>
<a>
<MenuItem
isSelected={router.asPath === `/org/${currentOrg?.id}/ssh`}
icon="system-outline-90-lock-closed"
>
SSH
</MenuItem>
</a>
</Link>
<Link href={`/org/${currentOrg?.id}/secret-scanning`} passHref>
<a>
<MenuItem
-26
View File
@@ -1,26 +0,0 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { SshPage } from "@app/views/Org/SshPage";
const Ssh = () => {
const { t } = useTranslation();
return (
<>
<Head>
<title>{t("common.head-title", { title: t("approval.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={String(t("approval.og-title"))} />
<meta name="og:description" content={String(t("approval.og-description"))} />
</Head>
<div className="h-full">
<SshPage />
</div>
</>
);
};
export default Ssh;
Ssh.requireAuth = true;
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import Head from "next/head";
import { SshCaPage } from "@app/views/Org/SshCaPage";
import { SshCaPage } from "@app/views/Project/SshCaPage";
export default function SshCa() {
return (
@@ -0,0 +1,23 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { SshPage } from "@app/views/Project/SshPage";
const Ssh = () => {
const { t } = useTranslation();
return (
<div className="h-full bg-bunker-800">
<Head>
<title>{t("common.head-title", { title: "Certificates" })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<SshPage />
</div>
);
};
export default Ssh;
Ssh.requireAuth = true;
@@ -49,10 +49,7 @@ export const formSchema = z.object({
identity: generalPermissionSchema,
"organization-admin-console": adminConsolePermissionSchmea,
[OrgPermissionSubjects.Kms]: generalPermissionSchema,
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema,
[OrgPermissionSubjects.SshCertificateAuthorities]: generalPermissionSchema,
[OrgPermissionSubjects.SshCertificates]: generalPermissionSchema,
[OrgPermissionSubjects.SshCertificateTemplates]: generalPermissionSchema
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema
})
.optional()
});
@@ -69,19 +69,7 @@ const SIMPLE_PERMISSION_OPTIONS = [
title: "External KMS",
formName: OrgPermissionSubjects.Kms
},
{ title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates },
{
title: "SSH Certificate Authorities",
formName: OrgPermissionSubjects.SshCertificateAuthorities
},
{
title: "SSH Certificates",
formName: OrgPermissionSubjects.SshCertificates
},
{
title: "SSH Certificate Templates",
formName: OrgPermissionSubjects.SshCertificateTemplates
}
{ title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates }
] as const;
type Props = {
@@ -121,6 +121,11 @@ export const projectRoleFormSchema = z.object({
[ProjectPermissionSub.PkiAlerts]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.PkiCollections]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.CertificateTemplates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SshCertificateAuthorities]: GeneralPolicyActionSchema.array().default(
[]
),
[ProjectPermissionSub.SshCertificates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SshCertificateTemplates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretApproval]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretRollback]: SecretRollbackPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Project]: WorkspacePolicyActionSchema.array().default([]),
@@ -203,6 +208,9 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
ProjectPermissionSub.PkiAlerts,
ProjectPermissionSub.PkiCollections,
ProjectPermissionSub.CertificateTemplates,
ProjectPermissionSub.SshCertificateAuthorities,
ProjectPermissionSub.SshCertificates,
ProjectPermissionSub.SshCertificateTemplates,
ProjectPermissionSub.SecretApproval,
ProjectPermissionSub.Tags,
ProjectPermissionSub.SecretRotation,
@@ -589,6 +597,33 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.SshCertificateAuthorities]: {
title: "SSH Certificate Authorities",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.SshCertificates]: {
title: "SSH Certificates",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.SshCertificateTemplates]: {
title: "SSH Certificate Templates",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.PkiCollections]: {
title: "PKI Collections",
actions: [
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Button,
DeleteActionModal,
@@ -15,17 +15,18 @@ import {
DropdownMenuTrigger,
Tooltip
} from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { withPermission } from "@app/hoc";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { withProjectPermission } from "@app/hoc";
import { useDeleteSshCa, useGetSshCaById } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { SshCaModal } from "../SshPage/components/SshCaModal";
import { SshCaDetailsSection, SshCertificateTemplatesSection } from "./components";
export const SshCaPage = withPermission(
export const SshCaPage = withProjectPermission(
() => {
const { currentOrg } = useOrganization();
const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const router = useRouter();
const caId = router.query.caId as string;
const { data } = useGetSshCaById(caId);
@@ -39,7 +40,7 @@ export const SshCaPage = withPermission(
const onRemoveCaSubmit = async (caIdToDelete: string) => {
try {
if (!currentOrg?.id) return;
if (!projectId) return;
await deleteSshCa({ caId: caIdToDelete });
@@ -49,7 +50,7 @@ export const SshCaPage = withPermission(
});
handlePopUpClose("deleteSshCa");
router.push(`/org/${currentOrg.id}/ssh`);
router.push(`/project/${projectId}/ssh`);
} catch (err) {
console.error(err);
createNotification({
@@ -67,7 +68,7 @@ export const SshCaPage = withPermission(
variant="link"
type="submit"
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => router.push(`/org/${currentOrg?.id}/ssh`)}
onClick={() => router.push(`/project/${projectId}/ssh`)}
className="mb-4"
>
SSH Certificate Authorities
@@ -83,9 +84,9 @@ export const SshCaPage = withPermission(
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan
I={OrgPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateAuthorities}
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.SshCertificateAuthorities}
>
{(isAllowed) => (
<DropdownMenuItem
@@ -104,7 +105,7 @@ export const SshCaPage = withPermission(
Delete SSH CA
</DropdownMenuItem>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</div>
@@ -131,5 +132,5 @@ export const SshCaPage = withPermission(
</div>
);
},
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.SshCertificateAuthorities }
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.SshCertificateAuthorities }
);
@@ -1,10 +1,10 @@
import { faCheck, faCopy, faDownload,faPencil } from "@fortawesome/free-solid-svg-icons";
import { faCheck, faCopy, faDownload, faPencil } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import FileSaver from "file-saver";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import { IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useTimedReset } from "@app/hooks";
import { useGetSshCaById } from "@app/hooks/api";
import { caStatusToNameMap } from "@app/hooks/api/ca/constants";
@@ -35,9 +35,9 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">SSH CA Details</h3>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateAuthorities}
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.SshCertificateAuthorities}
>
{(isAllowed) => {
return (
@@ -59,7 +59,7 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
</Tooltip>
);
}}
</OrgPermissionCan>
</ProjectPermissionCan>
</div>
<div className="pt-4">
<div className="mb-4">
@@ -13,13 +13,14 @@ import {
Select,
SelectItem
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useWorkspace } from "@app/context";
import {
SshCertTemplateStatus,
useGetSshCertTemplate,
useIssueSshCreds,
useListOrgSshCertificateTemplates,
useSignSshKey} from "@app/hooks/api";
useListWorkspaceSshCertificateTemplates,
useSignSshKey
} from "@app/hooks/api";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
import { SshCertType } from "@app/hooks/api/ssh-ca/constants";
@@ -62,7 +63,8 @@ enum SshCertificateOperation {
}
export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentOrg } = useOrganization();
const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const [operation, setOperation] = useState<SshCertificateOperation>(
SshCertificateOperation.SIGN_SSH_KEY
);
@@ -77,9 +79,7 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
templateId: string;
};
const { data: templatesData } = useListOrgSshCertificateTemplates({
orgId: currentOrg?.id || ""
});
const { data: templatesData } = useListWorkspaceSshCertificateTemplates(projectId);
const {
control,
@@ -117,10 +117,12 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
}: FormData) => {
try {
if (!templateData) return;
if (!projectId) return;
switch (operation) {
case SshCertificateOperation.SIGN_SSH_KEY: {
const { serialNumber, signedKey } = await signSshKey({
projectId: currentWorkspace?.id || "",
templateName: templateData.name,
publicKey: existingPublicKey,
certType,
@@ -137,6 +139,7 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
}
case SshCertificateOperation.ISSUE_SSH_CREDS: {
const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({
projectId,
templateName: templateData.name,
keyAlgorithm,
certType,
@@ -14,12 +14,12 @@ import {
SelectItem,
Switch
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useWorkspace } from "@app/context";
import {
useCreateSshCertTemplate,
useGetSshCaById,
useGetSshCertTemplate,
useListOrgSshCas,
useListWorkspaceSshCas,
useUpdateSshCertTemplate
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -48,7 +48,7 @@ type Props = {
};
export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId }: Props) => {
const { currentOrg } = useOrganization();
const { currentWorkspace } = useWorkspace();
const { data: ca } = useGetSshCaById(sshCaId);
@@ -56,9 +56,7 @@ export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId
(popUp?.sshCertificateTemplate?.data as { id: string })?.id || ""
);
const { data: cas } = useListOrgSshCas({
orgId: currentOrg?.id ?? ""
});
const { data: cas } = useListWorkspaceSshCas(currentWorkspace?.id || "");
const { mutateAsync: createSshCertTemplate } = useCreateSshCertTemplate();
const { mutateAsync: updateSshCertTemplate } = useUpdateSshCertTemplate();
@@ -2,14 +2,15 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import { DeleteActionModal, IconButton } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { usePopUp } from "@app/hooks";
import {
SshCertTemplateStatus,
useDeleteSshCertTemplate,
useUpdateSshCertTemplate} from "@app/hooks/api";
useUpdateSshCertTemplate
} from "@app/hooks/api";
import { SshCertificateModal } from "./SshCertificateModal";
import { SshCertificateTemplateModal } from "./SshCertificateTemplateModal";
@@ -85,9 +86,9 @@ export const SshCertificateTemplatesSection = ({ caId }: Props) => {
<div className="h-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Certificate Templates</h3>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.SshCertificateTemplates}
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.SshCertificateTemplates}
>
{(isAllowed) => (
<IconButton
@@ -100,7 +101,7 @@ export const SshCertificateTemplatesSection = ({ caId }: Props) => {
<FontAwesomeIcon icon={faPlus} />
</IconButton>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</div>
<div className="py-4">
<SshCertificateTemplatesTable handlePopUpOpen={handlePopUpOpen} sshCaId={caId} />
@@ -8,7 +8,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Badge,
DropdownMenu,
@@ -26,8 +26,8 @@ import {
Tooltip,
Tr
} from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { SshCertTemplateStatus,useGetSshCaCertTemplates } from "@app/hooks/api";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { SshCertTemplateStatus, useGetSshCaCertTemplates } from "@app/hooks/api";
import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -89,9 +89,9 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates}
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.SshCertificateTemplates}
>
{(isAllowed) => (
<DropdownMenuItem
@@ -118,10 +118,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
} Template`}
</DropdownMenuItem>
)}
</OrgPermissionCan>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates}
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.SshCertificateTemplates}
>
<DropdownMenuItem
onClick={() => {
@@ -136,10 +136,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
>
Issue Certificate
</DropdownMenuItem>
</OrgPermissionCan>
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates}
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.SshCertificateTemplates}
>
<DropdownMenuItem
onClick={() =>
@@ -151,10 +151,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
>
Edit Template
</DropdownMenuItem>
</OrgPermissionCan>
<OrgPermissionCan
I={OrgPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateTemplates}
</ProjectPermissionCan>
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.SshCertificateTemplates}
>
{(isAllowed) => (
<DropdownMenuItem
@@ -173,7 +173,7 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
Delete Template
</DropdownMenuItem>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Td>
@@ -1,8 +1,8 @@
import { motion } from "framer-motion";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { withPermission } from "@app/hoc";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { withProjectPermission } from "@app/hoc";
import { SshCaSection, SshCertificatesSection } from "./components";
@@ -11,7 +11,7 @@ enum TabSections {
SshCertificates = "ssh-certificates"
}
export const SshPage = withPermission(
export const SshPage = withProjectPermission(
() => {
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
@@ -49,5 +49,5 @@ export const SshPage = withPermission(
</div>
);
},
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.SshCertificateAuthorities }
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.SshCertificateAuthorities }
);
@@ -13,6 +13,7 @@ import {
Select,
SelectItem
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { useCreateSshCa, useGetSshCaById, useUpdateSshCa } from "@app/hooks/api";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
@@ -38,6 +39,8 @@ const schema = z
export type FormData = z.infer<typeof schema>;
export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const { data: ca } = useGetSshCaById((popUp?.sshCa?.data as { caId: string })?.caId || "");
const { mutateAsync: createMutateAsync } = useCreateSshCa();
@@ -72,6 +75,8 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const onFormSubmit = async ({ friendlyName, keyAlgorithm }: FormData) => {
try {
if (!projectId) return;
if (ca) {
await updateMutateAsync({
caId: ca.id,
@@ -79,6 +84,7 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
});
} else {
await createMutateAsync({
projectId,
friendlyName,
keyAlgorithm
});
@@ -2,9 +2,9 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { SshCaStatus, useDeleteSshCa, useUpdateSshCa } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
@@ -64,9 +64,9 @@ export const SshCaSection = () => {
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Certificate Authorities</p>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.SshCertificateAuthorities}
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.SshCertificateAuthorities}
>
{(isAllowed) => (
<Button
@@ -79,7 +79,7 @@ export const SshCaSection = () => {
Create SSH CA
</Button>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</div>
<SshCaTable handlePopUpOpen={handlePopUpOpen} />
<SshCaModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
@@ -3,7 +3,7 @@ import { faBan, faCertificate, faEllipsis, faTrash } from "@fortawesome/free-sol
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Badge,
DropdownMenu,
@@ -21,8 +21,8 @@ import {
Tooltip,
Tr
} from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
import { SshCaStatus , useListOrgSshCas } from "@app/hooks/api";
import { ProjectPermissionActions, ProjectPermissionSub,useWorkspace } from "@app/context";
import { SshCaStatus, useListWorkspaceSshCas } from "@app/hooks/api";
import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -35,10 +35,8 @@ type Props = {
export const SshCaTable = ({ handlePopUpOpen }: Props) => {
const router = useRouter();
const { currentOrg } = useOrganization();
const { data, isLoading } = useListOrgSshCas({
orgId: currentOrg?.id ?? ""
});
const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useListWorkspaceSshCas(currentWorkspace?.id || "");
return (
<div>
@@ -61,7 +59,7 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
<Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
key={`ca-${ca.id}`}
onClick={() => router.push(`/org/${currentOrg?.id}/ssh/ca/${ca.id}`)}
onClick={() => router.push(`/project/${currentWorkspace?.id}/ssh/ca/${ca.id}`)}
>
<Td>{ca.friendlyName}</Td>
<Td>
@@ -81,9 +79,9 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
<DropdownMenuContent align="start" className="p-1">
{(ca.status === SshCaStatus.ACTIVE ||
ca.status === SshCaStatus.DISABLED) && (
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateAuthorities}
<ProjectPermissionCan
I={ProjectPermissionActions.Edit}
a={ProjectPermissionSub.SshCertificateAuthorities}
>
{(isAllowed) => (
<DropdownMenuItem
@@ -109,11 +107,11 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
} SSH CA`}
</DropdownMenuItem>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
)}
<OrgPermissionCan
I={OrgPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateAuthorities}
<ProjectPermissionCan
I={ProjectPermissionActions.Delete}
a={ProjectPermissionSub.SshCertificateAuthorities}
>
{(isAllowed) => (
<DropdownMenuItem
@@ -132,7 +130,7 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
Delete SSH CA
</DropdownMenuItem>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</DropdownMenuContent>
</DropdownMenu>
</Td>
@@ -1,9 +1,9 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { usePopUp } from "@app/hooks/usePopUp";
import { SshCertificateModal } from "../../SshCaPage/components/SshCertificateModal";
@@ -15,7 +15,10 @@ export const SshCertificatesSection = () => {
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Certificates</p>
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.SshCertificates}>
<ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.SshCertificates}
>
{(isAllowed) => (
<Button
colorSchema="primary"
@@ -27,7 +30,7 @@ export const SshCertificatesSection = () => {
Request
</Button>
)}
</OrgPermissionCan>
</ProjectPermissionCan>
</div>
<SshCertificatesTable />
<SshCertificateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
@@ -15,20 +15,20 @@ import {
THead,
Tr
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useListOrgSshCertificates } from "@app/hooks/api";
import { useWorkspace } from "@app/context";
import { useListWorkspaceSshCertificates } from "@app/hooks/api";
import { getSshCertStatusBadgeDetails } from "./SshCertificatesTable.utils";
const PER_PAGE_INIT = 25;
export const SshCertificatesTable = () => {
const { currentOrg } = useOrganization();
const { currentWorkspace } = useWorkspace();
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(PER_PAGE_INIT);
const { data, isLoading } = useListOrgSshCertificates({
orgId: currentOrg?.id ?? "",
const { data, isLoading } = useListWorkspaceSshCertificates({
projectId: currentWorkspace?.id || "",
offset: (page - 1) * perPage,
limit: perPage
});