mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 02:26:26 +00:00
Add sign SSH key operation to frontend
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
export { SshCaStatus } from "./enums";
|
export { SshCaStatus } from "./enums";
|
||||||
export { useCreateSshCa, useDeleteSshCa, useIssueSshCreds,useUpdateSshCa } from "./mutations";
|
export {
|
||||||
|
useCreateSshCa,
|
||||||
|
useDeleteSshCa,
|
||||||
|
useIssueSshCreds,
|
||||||
|
useSignSshKey,
|
||||||
|
useUpdateSshCa} from "./mutations";
|
||||||
export { useGetSshCaById, useGetSshCaCertTemplates } from "./queries";
|
export { useGetSshCaById, useGetSshCaCertTemplates } from "./queries";
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
TDeleteSshCaDTO,
|
TDeleteSshCaDTO,
|
||||||
TIssueSshCredsDTO,
|
TIssueSshCredsDTO,
|
||||||
TIssueSshCredsResponse,
|
TIssueSshCredsResponse,
|
||||||
|
TSignSshKeyDTO,
|
||||||
|
TSignSshKeyResponse,
|
||||||
TSshCertificateAuthority,
|
TSshCertificateAuthority,
|
||||||
TUpdateSshCaDTO} from "./types";
|
TUpdateSshCaDTO} from "./types";
|
||||||
|
|
||||||
@@ -61,6 +63,15 @@ export const useDeleteSshCa = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSignSshKey = () => {
|
||||||
|
return useMutation<TSignSshKeyResponse, {}, TSignSshKeyDTO>({
|
||||||
|
mutationFn: async (body) => {
|
||||||
|
const { data } = await apiRequest.post<TSignSshKeyResponse>("/api/v1/ssh/sign", body);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useIssueSshCreds = () => {
|
export const useIssueSshCreds = () => {
|
||||||
return useMutation<TIssueSshCredsResponse, {}, TIssueSshCredsDTO>({
|
return useMutation<TIssueSshCredsResponse, {}, TIssueSshCredsDTO>({
|
||||||
mutationFn: async (body) => {
|
mutationFn: async (body) => {
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ export type TDeleteSshCaDTO = {
|
|||||||
caId: string;
|
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 = {
|
export type TIssueSshCredsDTO = {
|
||||||
templateName: string;
|
templateName: string;
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { useTimedReset } from "@app/hooks";
|
|||||||
type Props = {
|
type Props = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
signedKey: string;
|
signedKey: string;
|
||||||
privateKey: string;
|
privateKey?: string;
|
||||||
publicKey: string;
|
publicKey?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SshCertificateContent = ({
|
export const SshCertificateContent = ({
|
||||||
@@ -119,7 +119,7 @@ export const SshCertificateContent = ({
|
|||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
className="group relative ml-2"
|
className="group relative ml-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
downloadTxtFile("user_key", privateKey);
|
downloadTxtFile("user_key.pem", privateKey);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faDownload} />
|
<FontAwesomeIcon icon={faDownload} />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem
|
||||||
} from "@app/components/v2";
|
} 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 { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
|
||||||
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
|
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
|
||||||
import { SshCertType } from "@app/hooks/api/ssh-ca/enums";
|
import { SshCertType } from "@app/hooks/api/ssh-ca/enums";
|
||||||
@@ -29,6 +29,7 @@ import { SshCertificateContent } from "./SshCertificateContent";
|
|||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
templateName: z.string(),
|
templateName: z.string(),
|
||||||
|
publicKey: z.string().optional(),
|
||||||
keyAlgorithm: z.enum([
|
keyAlgorithm: z.enum([
|
||||||
CertKeyAlgorithm.RSA_2048,
|
CertKeyAlgorithm.RSA_2048,
|
||||||
CertKeyAlgorithm.RSA_4096,
|
CertKeyAlgorithm.RSA_4096,
|
||||||
@@ -50,13 +51,23 @@ type Props = {
|
|||||||
|
|
||||||
type TSshCertificateDetails = {
|
type TSshCertificateDetails = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
privateKey: string;
|
|
||||||
publicKey: string;
|
|
||||||
signedKey: 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) => {
|
export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||||
|
const [operation, setOperation] = useState<SshCertificateOperation>(
|
||||||
|
SshCertificateOperation.SIGN_SSH_KEY
|
||||||
|
);
|
||||||
const [certificateDetails, setCertificateDetails] = useState<TSshCertificateDetails | null>(null);
|
const [certificateDetails, setCertificateDetails] = useState<TSshCertificateDetails | null>(null);
|
||||||
|
|
||||||
|
const { mutateAsync: signSshKey } = useSignSshKey();
|
||||||
const { mutateAsync: issueSshCreds } = useIssueSshCreds();
|
const { mutateAsync: issueSshCreds } = useIssueSshCreds();
|
||||||
|
|
||||||
const popUpData = popUp?.sshCertificate?.data as { sshCaId: string; templateName: string };
|
const popUpData = popUp?.sshCertificate?.data as { sshCaId: string; templateName: string };
|
||||||
@@ -87,29 +98,53 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
templateName,
|
templateName,
|
||||||
keyAlgorithm,
|
keyAlgorithm,
|
||||||
certType,
|
certType,
|
||||||
|
publicKey: existingPublicKey,
|
||||||
principals,
|
principals,
|
||||||
ttl,
|
ttl,
|
||||||
keyId
|
keyId
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({
|
switch (operation) {
|
||||||
templateName,
|
case SshCertificateOperation.SIGN_SSH_KEY: {
|
||||||
keyAlgorithm,
|
const { serialNumber, signedKey } = await signSshKey({
|
||||||
certType,
|
templateName,
|
||||||
principals: principals.split(",").map((user) => user.trim()),
|
publicKey: existingPublicKey,
|
||||||
ttl,
|
certType,
|
||||||
keyId
|
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();
|
reset();
|
||||||
|
|
||||||
setCertificateDetails({
|
|
||||||
serialNumber,
|
|
||||||
privateKey,
|
|
||||||
publicKey,
|
|
||||||
signedKey
|
|
||||||
});
|
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
text: "Successfully created SSH certificate",
|
text: "Successfully created SSH certificate",
|
||||||
type: "success"
|
type: "success"
|
||||||
@@ -162,20 +197,21 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
<FormControl label="Operation">
|
||||||
control={control}
|
<Select
|
||||||
name="principals"
|
defaultValue="issue-ssh-creds"
|
||||||
render={({ field, fieldState: { error } }) => (
|
value={operation}
|
||||||
<FormControl
|
onValueChange={(e) => setOperation(e as SshCertificateOperation)}
|
||||||
label="Principal(s)"
|
className="w-full"
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<SelectItem value="sign-ssh-key" key="sign-ssh-key">
|
||||||
isRequired
|
Sign SSH Key
|
||||||
>
|
</SelectItem>
|
||||||
<Input {...field} placeholder="ec2-user" />
|
<SelectItem value="issue-ssh-creds" key="issue-ssh-creds">
|
||||||
</FormControl>
|
Issue SSH Credentials
|
||||||
)}
|
</SelectItem>
|
||||||
/>
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="certType"
|
name="certType"
|
||||||
@@ -198,31 +234,64 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{operation === SshCertificateOperation.SIGN_SSH_KEY && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="publicKey"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="SSH Public Key"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="ssh-rsa AAA ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{operation === SshCertificateOperation.ISSUE_SSH_CREDS && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="keyAlgorithm"
|
||||||
|
defaultValue={CertKeyAlgorithm.RSA_2048}
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Key Algorithm"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{certKeyAlgorithms.map(({ label, value }) => (
|
||||||
|
<SelectItem value={String(value || "")} key={label}>
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="keyAlgorithm"
|
name="principals"
|
||||||
defaultValue={CertKeyAlgorithm.RSA_2048}
|
render={({ field, fieldState: { error } }) => (
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Key Algorithm"
|
label="Principal(s)"
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
isRequired
|
||||||
>
|
>
|
||||||
<Select
|
<Input {...field} placeholder="ec2-user" />
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{certKeyAlgorithms.map(({ label, value }) => (
|
|
||||||
<SelectItem value={String(value || "")} key={label}>
|
|
||||||
{label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="ttl"
|
name="ttl"
|
||||||
|
|||||||
Reference in New Issue
Block a user