mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 00:52:09 +00:00
Add permissioning to SSH, add publicKey return for SSH CA, polish
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
export { OrgPermissionProvider, useOrgPermission } from "./OrgPermissionContext";
|
||||
export type { TOrgPermission } from "./types";
|
||||
export { OrgPermissionActions, OrgPermissionSubjects } from "./types";
|
||||
export {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSshCertificateTemplateActions,
|
||||
OrgPermissionSubjects} from "./types";
|
||||
|
||||
@@ -7,6 +7,15 @@ export enum OrgPermissionActions {
|
||||
Delete = "delete"
|
||||
}
|
||||
|
||||
export enum OrgPermissionSshCertificateTemplateActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
SignSshKey = "sign-ssh-key",
|
||||
IssueSshCredentials = "issue-ssh-credentials"
|
||||
}
|
||||
|
||||
export enum OrgPermissionSubjects {
|
||||
Workspace = "workspace",
|
||||
Role = "role",
|
||||
@@ -51,6 +60,6 @@ export type OrgPermissionSet =
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateAuthorities]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateTemplates];
|
||||
| [OrgPermissionSshCertificateTemplateActions, OrgPermissionSubjects.SshCertificateTemplates];
|
||||
|
||||
export type TOrgPermission = MongoAbility<OrgPermissionSet>;
|
||||
|
||||
@@ -4,6 +4,7 @@ export type { TOrgPermission } from "./OrgPermissionContext";
|
||||
export {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionProvider,
|
||||
OrgPermissionSshCertificateTemplateActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrgPermission
|
||||
} from "./OrgPermissionContext";
|
||||
|
||||
@@ -504,7 +504,7 @@ export const useListOrgSshCas = ({ orgId }: { orgId: string }) => {
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { cas }
|
||||
} = await apiRequest.get<{ cas: TSshCertificateAuthority[] }>(
|
||||
} = await apiRequest.get<{ cas: Omit<TSshCertificateAuthority, "publicKey">[] }>(
|
||||
`/api/v1/organization/${orgId}/ssh-cas`
|
||||
);
|
||||
return cas;
|
||||
|
||||
@@ -9,6 +9,7 @@ export type TSshCertificateAuthority = {
|
||||
keyAlgorithm: CertKeyAlgorithm;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
publicKey: string;
|
||||
};
|
||||
|
||||
export type TCreateSshCaDTO = {
|
||||
@@ -18,6 +19,7 @@ export type TCreateSshCaDTO = {
|
||||
|
||||
export type TUpdateSshCaDTO = {
|
||||
caId: string;
|
||||
friendlyName?: string;
|
||||
status?: SshCaStatus;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,17 @@ const generalPermissionSchema = z
|
||||
})
|
||||
.optional();
|
||||
|
||||
const sshCertificateTemplatePermissionSchmea = z
|
||||
.object({
|
||||
read: z.boolean().optional(),
|
||||
edit: z.boolean().optional(),
|
||||
delete: z.boolean().optional(),
|
||||
create: z.boolean().optional(),
|
||||
"sign-ssh-key": z.boolean().optional(),
|
||||
"issue-ssh-credentials": z.boolean().optional()
|
||||
})
|
||||
.optional();
|
||||
|
||||
const adminConsolePermissionSchmea = z
|
||||
.object({
|
||||
"access-all-projects": z.boolean().optional()
|
||||
@@ -49,7 +60,9 @@ export const formSchema = z.object({
|
||||
identity: generalPermissionSchema,
|
||||
"organization-admin-console": adminConsolePermissionSchmea,
|
||||
[OrgPermissionSubjects.Kms]: generalPermissionSchema,
|
||||
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema
|
||||
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema,
|
||||
[OrgPermissionSubjects.SshCertificateAuthorities]: generalPermissionSchema,
|
||||
[OrgPermissionSubjects.SshCertificateTemplates]: sshCertificateTemplatePermissionSchmea
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
|
||||
@@ -70,12 +70,6 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
|
||||
const onFormSubmit = async ({ name, description, slug }: FormData) => {
|
||||
try {
|
||||
console.log("onFormSubmit args: ", {
|
||||
name,
|
||||
description,
|
||||
slug
|
||||
});
|
||||
|
||||
if (!orgId) return;
|
||||
|
||||
if (role) {
|
||||
|
||||
+12
-1
@@ -51,6 +51,15 @@ const PROJECT_TEMPLATES_PERMISSIONS = [
|
||||
{ action: "delete", label: "Remove" }
|
||||
] as const;
|
||||
|
||||
const SSH_CERTIFICATE_TEMPLATES_PERMISSIONS = [
|
||||
{ action: "read", label: "Read" },
|
||||
{ action: "create", label: "Create" },
|
||||
{ action: "edit", label: "Modify" },
|
||||
{ action: "delete", label: "Remove" },
|
||||
{ action: "sign-ssh-key", label: "Sign SSH Key" },
|
||||
{ action: "issue-ssh-credentials", label: "Issue SSH Credentials" }
|
||||
] as const;
|
||||
|
||||
const getPermissionList = (option: string) => {
|
||||
switch (option) {
|
||||
case "secret-scanning":
|
||||
@@ -63,6 +72,8 @@ const getPermissionList = (option: string) => {
|
||||
return MEMBERS_PERMISSIONS;
|
||||
case OrgPermissionSubjects.ProjectTemplates:
|
||||
return PROJECT_TEMPLATES_PERMISSIONS;
|
||||
case OrgPermissionSubjects.SshCertificateTemplates:
|
||||
return SSH_CERTIFICATE_TEMPLATES_PERMISSIONS;
|
||||
default:
|
||||
return PERMISSIONS;
|
||||
}
|
||||
@@ -97,7 +108,7 @@ export const RolePermissionRow = ({ isEditable, title, formName, control, setVal
|
||||
|
||||
const selectedPermissionCategory = useMemo(() => {
|
||||
const actions = Object.keys(rule || {}) as Array<keyof typeof rule>;
|
||||
const totalActions = PERMISSIONS.length;
|
||||
const totalActions = getPermissionList(formName).length;
|
||||
const score = actions.map((key) => (rule?.[key] ? 1 : 0)).reduce((a, b) => a + b, 0 as number);
|
||||
|
||||
if (isCustom) return Permission.Custom;
|
||||
|
||||
+9
-1
@@ -69,7 +69,15 @@ const SIMPLE_PERMISSION_OPTIONS = [
|
||||
title: "External KMS",
|
||||
formName: OrgPermissionSubjects.Kms
|
||||
},
|
||||
{ title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates }
|
||||
{ title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates },
|
||||
{
|
||||
title: "SSH Certificate Authorities",
|
||||
formName: OrgPermissionSubjects.SshCertificateAuthorities
|
||||
},
|
||||
{
|
||||
title: "SSH Certificate Templates",
|
||||
formName: OrgPermissionSubjects.SshCertificateTemplates
|
||||
}
|
||||
] as const;
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { withPermission } 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(
|
||||
@@ -123,6 +124,7 @@ export const SshCaPage = withPermission(
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<SshCaModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteSshCa.isOpen}
|
||||
title="Are you sure want to remove the SSH CA from the project?"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { faCheck, faCopy, 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 { IconButton, Tooltip } from "@app/components/v2";
|
||||
@@ -19,13 +20,21 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
|
||||
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
|
||||
initialState: "Copy ID to clipboard"
|
||||
});
|
||||
const [downloadText, isDownloading, setDownloadText] = useTimedReset<string>({
|
||||
initialState: "Save public key"
|
||||
});
|
||||
|
||||
const { data: ca } = useGetSshCaById(caId);
|
||||
|
||||
const downloadTxtFile = (filename: string, content: string) => {
|
||||
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
||||
FileSaver.saveAs(blob, filename);
|
||||
};
|
||||
|
||||
return ca ? (
|
||||
<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">CA Details</h3>
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">SSH CA Details</h3>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
a={OrgPermissionSubjects.SshCertificateAuthorities}
|
||||
@@ -82,10 +91,31 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Status</p>
|
||||
<p className="text-sm text-mineshaft-300">{caStatusToNameMap[ca.status]}</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Key Algorithm</p>
|
||||
<p className="text-sm text-mineshaft-300">{certKeyAlgorithmToNameMap[ca.keyAlgorithm]}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-mineshaft-300">Public Key</p>
|
||||
<div className="group flex align-top">
|
||||
<p className="flex-1 text-sm text-mineshaft-300">{ca.publicKey.substring(0, 20)}...</p>
|
||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||
<Tooltip content={downloadText}>
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative ml-2"
|
||||
onClick={() => {
|
||||
setDownloadText("Saved");
|
||||
downloadTxtFile("ssh_ca.pub", ca.publicKey);
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={isDownloading ? faCheck : faDownload} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -185,7 +185,7 @@ export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="My SSH Certificate Template" />
|
||||
<Input {...field} placeholder="administrator" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { DeleteActionModal, IconButton } from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { OrgPermissionSshCertificateTemplateActions,OrgPermissionSubjects } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useDeleteSshCertTemplate } from "@app/hooks/api";
|
||||
|
||||
@@ -50,7 +50,7 @@ export const SshCertificateTemplatesSection = ({ caId }: Props) => {
|
||||
<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}
|
||||
I={OrgPermissionSshCertificateTemplateActions.Create}
|
||||
a={OrgPermissionSubjects.SshCertificateTemplates}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { OrgPermissionSshCertificateTemplateActions,OrgPermissionSubjects } from "@app/context";
|
||||
import { useGetSshCaCertTemplates } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -66,18 +66,23 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
handlePopUpOpen("sshCertificateTemplate", {
|
||||
id: certificateTemplate.id
|
||||
})
|
||||
}
|
||||
icon={<FontAwesomeIcon icon={faFileAlt} size="sm" className="mr-1" />}
|
||||
>
|
||||
Edit Template
|
||||
</DropdownMenuItem>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Delete}
|
||||
I={OrgPermissionSshCertificateTemplateActions.Edit}
|
||||
a={OrgPermissionSubjects.SshCertificateTemplates}
|
||||
>
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
handlePopUpOpen("sshCertificateTemplate", {
|
||||
id: certificateTemplate.id
|
||||
})
|
||||
}
|
||||
icon={<FontAwesomeIcon icon={faFileAlt} size="sm" className="mr-1" />}
|
||||
>
|
||||
Edit Template
|
||||
</DropdownMenuItem>
|
||||
</OrgPermissionCan>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionSshCertificateTemplateActions.Delete}
|
||||
a={OrgPermissionSubjects.SshCertificateTemplates}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
@@ -55,15 +56,28 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (ca) {
|
||||
reset({
|
||||
friendlyName: ca.friendlyName,
|
||||
keyAlgorithm: ca.keyAlgorithm
|
||||
});
|
||||
} else {
|
||||
reset({
|
||||
friendlyName: "",
|
||||
keyAlgorithm: CertKeyAlgorithm.RSA_2048
|
||||
});
|
||||
}
|
||||
}, [ca]);
|
||||
|
||||
const onFormSubmit = async ({ friendlyName, keyAlgorithm }: FormData) => {
|
||||
try {
|
||||
if (ca) {
|
||||
// update
|
||||
await updateMutateAsync({
|
||||
caId: ca.id
|
||||
caId: ca.id,
|
||||
friendlyName
|
||||
});
|
||||
} else {
|
||||
// create
|
||||
await createMutateAsync({
|
||||
friendlyName,
|
||||
keyAlgorithm
|
||||
@@ -112,7 +126,7 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
errorText={error?.message}
|
||||
isRequired
|
||||
>
|
||||
<Input {...field} placeholder="My SSH CA" isDisabled={Boolean(ca)} />
|
||||
<Input {...field} placeholder="My SSH CA" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user