diff --git a/frontend/src/hooks/api/ssh-ca/index.tsx b/frontend/src/hooks/api/ssh-ca/index.tsx index 70727225d..0bdd4eb61 100644 --- a/frontend/src/hooks/api/ssh-ca/index.tsx +++ b/frontend/src/hooks/api/ssh-ca/index.tsx @@ -1,3 +1,8 @@ export { SshCaStatus } from "./enums"; -export { useCreateSshCa, useDeleteSshCa, useIssueSshCreds,useUpdateSshCa } from "./mutations"; +export { + useCreateSshCa, + useDeleteSshCa, + useIssueSshCreds, + useSignSshKey, + useUpdateSshCa} from "./mutations"; export { useGetSshCaById, useGetSshCaCertTemplates } from "./queries"; diff --git a/frontend/src/hooks/api/ssh-ca/mutations.tsx b/frontend/src/hooks/api/ssh-ca/mutations.tsx index 9b2fa65e3..a2eb61ed4 100644 --- a/frontend/src/hooks/api/ssh-ca/mutations.tsx +++ b/frontend/src/hooks/api/ssh-ca/mutations.tsx @@ -8,6 +8,8 @@ import { TDeleteSshCaDTO, TIssueSshCredsDTO, TIssueSshCredsResponse, + TSignSshKeyDTO, + TSignSshKeyResponse, TSshCertificateAuthority, TUpdateSshCaDTO} from "./types"; @@ -61,6 +63,15 @@ export const useDeleteSshCa = () => { }); }; +export const useSignSshKey = () => { + return useMutation({ + mutationFn: async (body) => { + const { data } = await apiRequest.post("/api/v1/ssh/sign", body); + return data; + } + }); +}; + export const useIssueSshCreds = () => { return useMutation({ mutationFn: async (body) => { diff --git a/frontend/src/hooks/api/ssh-ca/types.ts b/frontend/src/hooks/api/ssh-ca/types.ts index f263bb489..454e0f41a 100644 --- a/frontend/src/hooks/api/ssh-ca/types.ts +++ b/frontend/src/hooks/api/ssh-ca/types.ts @@ -27,6 +27,20 @@ export type TDeleteSshCaDTO = { caId: string; }; +export type TSignSshKeyDTO = { + templateName: string; + publicKey?: string; + certType: SshCertType; + principals: string[]; + ttl?: string; + keyId?: string; +}; + +export type TSignSshKeyResponse = { + serialNumber: string; + signedKey: string; +}; + export type TIssueSshCredsDTO = { templateName: string; keyAlgorithm: CertKeyAlgorithm; diff --git a/frontend/src/views/Org/SshCaPage/components/SshCertificateContent.tsx b/frontend/src/views/Org/SshCaPage/components/SshCertificateContent.tsx index a71a2601d..fb462742e 100644 --- a/frontend/src/views/Org/SshCaPage/components/SshCertificateContent.tsx +++ b/frontend/src/views/Org/SshCaPage/components/SshCertificateContent.tsx @@ -8,8 +8,8 @@ import { useTimedReset } from "@app/hooks"; type Props = { serialNumber: string; signedKey: string; - privateKey: string; - publicKey: string; + privateKey?: string; + publicKey?: string; }; export const SshCertificateContent = ({ @@ -119,7 +119,7 @@ export const SshCertificateContent = ({ colorSchema="secondary" className="group relative ml-2" onClick={() => { - downloadTxtFile("user_key", privateKey); + downloadTxtFile("user_key.pem", privateKey); }} > diff --git a/frontend/src/views/Org/SshCaPage/components/SshCertificateModal.tsx b/frontend/src/views/Org/SshCaPage/components/SshCertificateModal.tsx index c5ac70e1d..aa9ccbc88 100644 --- a/frontend/src/views/Org/SshCaPage/components/SshCertificateModal.tsx +++ b/frontend/src/views/Org/SshCaPage/components/SshCertificateModal.tsx @@ -13,7 +13,7 @@ import { Select, SelectItem } from "@app/components/v2"; -import { useGetSshCaCertTemplates,useIssueSshCreds } from "@app/hooks/api"; +import { useGetSshCaCertTemplates, useIssueSshCreds, 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/enums"; @@ -29,6 +29,7 @@ import { SshCertificateContent } from "./SshCertificateContent"; const schema = z.object({ templateName: z.string(), + publicKey: z.string().optional(), keyAlgorithm: z.enum([ CertKeyAlgorithm.RSA_2048, CertKeyAlgorithm.RSA_4096, @@ -50,13 +51,23 @@ type Props = { type TSshCertificateDetails = { serialNumber: string; - privateKey: string; - publicKey: string; signedKey: string; + privateKey?: string; + publicKey?: string; }; +enum SshCertificateOperation { + SIGN_SSH_KEY = "sign-ssh-key", + ISSUE_SSH_CREDS = "issue-ssh-creds" +} + export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => { + const [operation, setOperation] = useState( + SshCertificateOperation.SIGN_SSH_KEY + ); const [certificateDetails, setCertificateDetails] = useState(null); + + const { mutateAsync: signSshKey } = useSignSshKey(); const { mutateAsync: issueSshCreds } = useIssueSshCreds(); const popUpData = popUp?.sshCertificate?.data as { sshCaId: string; templateName: string }; @@ -87,29 +98,53 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => { templateName, keyAlgorithm, certType, + publicKey: existingPublicKey, principals, ttl, keyId }: FormData) => { try { - const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({ - templateName, - keyAlgorithm, - certType, - principals: principals.split(",").map((user) => user.trim()), - ttl, - keyId - }); + switch (operation) { + case SshCertificateOperation.SIGN_SSH_KEY: { + const { serialNumber, signedKey } = await signSshKey({ + templateName, + publicKey: existingPublicKey, + certType, + principals: principals.split(",").map((user) => user.trim()), + ttl, + keyId + }); + setCertificateDetails({ + serialNumber, + signedKey + }); + break; + } + case SshCertificateOperation.ISSUE_SSH_CREDS: { + const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({ + templateName, + keyAlgorithm, + certType, + principals: principals.split(",").map((user) => user.trim()), + ttl, + keyId + }); + + setCertificateDetails({ + serialNumber, + privateKey, + publicKey, + signedKey + }); + break; + } + default: { + break; + } + } reset(); - setCertificateDetails({ - serialNumber, - privateKey, - publicKey, - signedKey - }); - createNotification({ text: "Successfully created SSH certificate", type: "success" @@ -162,20 +197,21 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => { )} /> - ( - - - - )} - /> + + + { )} /> + {operation === SshCertificateOperation.SIGN_SSH_KEY && ( + ( + + + + )} + /> + )} + {operation === SshCertificateOperation.ISSUE_SSH_CREDS && ( + ( + + + + )} + /> + )} ( + name="principals" + render={({ field, fieldState: { error } }) => ( - + )} /> +