mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Update permission logic
This commit is contained in:
@@ -68,7 +68,6 @@ export enum ProjectPermissionGroupActions {
|
||||
}
|
||||
|
||||
export enum ProjectPermissionSshHostActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
@@ -629,7 +628,6 @@ const buildAdminPermissionRules = () => {
|
||||
|
||||
can(
|
||||
[
|
||||
ProjectPermissionSshHostActions.Read,
|
||||
ProjectPermissionSshHostActions.Edit,
|
||||
ProjectPermissionSshHostActions.Create,
|
||||
ProjectPermissionSshHostActions.Delete,
|
||||
|
||||
@@ -337,7 +337,7 @@ export const sshHostServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId: host.projectId,
|
||||
@@ -346,13 +346,6 @@ export const sshHostServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.SSH
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionSshHostActions.Read,
|
||||
subject(ProjectPermissionSub.SshHosts, {
|
||||
hostname: host.hostname
|
||||
})
|
||||
);
|
||||
|
||||
return host;
|
||||
};
|
||||
|
||||
|
||||
@@ -1068,8 +1068,7 @@ export const projectServiceFactory = ({
|
||||
actor,
|
||||
projectId
|
||||
}: TListProjectSshHostsDTO) => {
|
||||
console.log("listProjectSshHosts: ", actor, actorId, actorAuthMethod, actorOrgId, projectId);
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -1078,10 +1077,7 @@ export const projectServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.SSH
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHosts);
|
||||
|
||||
const hosts = await sshHostDAL.findSshHostsWithLoginMappings(projectId);
|
||||
|
||||
return hosts;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ export enum ProjectPermissionGroupActions {
|
||||
}
|
||||
|
||||
export enum ProjectPermissionSshHostActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { useCreateSshHost, useDeleteSshHost, useUpdateSshHost } from "./mutations";
|
||||
export { useGetSshHostById } from "./queries";
|
||||
export { fetchSshHostUserCaPublicKey, useGetSshHostById } from "./queries";
|
||||
|
||||
@@ -5,7 +5,8 @@ import { apiRequest } from "@app/config/request";
|
||||
import { TSshHost } from "./types";
|
||||
|
||||
export const sshHostKeys = {
|
||||
getSshHostById: (sshHostId: string) => [{ sshHostId }, "ssh-host"]
|
||||
getSshHostById: (sshHostId: string) => [{ sshHostId }, "ssh-host"],
|
||||
getSshHostUserCaPublicKey: (sshHostId: string) => [{ sshHostId }, "ssh-host-user-ca-public-key"]
|
||||
};
|
||||
|
||||
export const useGetSshHostById = (sshHostId: string) => {
|
||||
@@ -18,3 +19,10 @@ export const useGetSshHostById = (sshHostId: string) => {
|
||||
enabled: Boolean(sshHostId)
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchSshHostUserCaPublicKey = async (sshHostId: string): Promise<string> => {
|
||||
const { data } = await apiRequest.get<string>(
|
||||
`/api/v1/ssh/hosts/${sshHostId}/user-ca-public-key`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -133,7 +133,7 @@ export const ProjectLayout = () => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
{/* <Link
|
||||
to={`/${ProjectType.SSH}/$projectId/certificates` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
@@ -144,8 +144,8 @@ export const ProjectLayout = () => {
|
||||
Certificates
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
</Link> */}
|
||||
{/* <Link
|
||||
to={`/${ProjectType.SSH}/$projectId/cas` as const}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
@@ -160,7 +160,7 @@ export const ProjectLayout = () => {
|
||||
Certificate Authorities
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
</Link> */}
|
||||
</>
|
||||
)}
|
||||
{isSecretManager && (
|
||||
|
||||
@@ -100,7 +100,6 @@ const GroupPolicyActionSchema = z.object({
|
||||
});
|
||||
|
||||
const SshHostPolicyActionSchema = z.object({
|
||||
[ProjectPermissionSshHostActions.Read]: z.boolean().optional(),
|
||||
[ProjectPermissionSshHostActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionSshHostActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionSshHostActions.Delete]: z.boolean().optional(),
|
||||
@@ -559,28 +558,27 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.SshHosts) {
|
||||
const canRead = action.includes(ProjectPermissionSshHostActions.Read);
|
||||
const canEdit = action.includes(ProjectPermissionSshHostActions.Edit);
|
||||
const canDelete = action.includes(ProjectPermissionSshHostActions.Delete);
|
||||
const canCreate = action.includes(ProjectPermissionSshHostActions.Create);
|
||||
const canIssueUserCert = action.includes(ProjectPermissionSshHostActions.IssueUserCert);
|
||||
const canIssueHostCert = action.includes(ProjectPermissionSshHostActions.IssueHostCert);
|
||||
if (!formVal[subject]) formVal[subject] = [];
|
||||
|
||||
if (!formVal[subject]) formVal[subject] = [{ conditions: [] }];
|
||||
|
||||
if (canRead) formVal[subject]![0][ProjectPermissionSshHostActions.Read] = true;
|
||||
if (canEdit) formVal[subject]![0][ProjectPermissionSshHostActions.Edit] = true;
|
||||
if (canDelete) formVal[subject]![0][ProjectPermissionSshHostActions.Delete] = true;
|
||||
if (canCreate) formVal[subject]![0][ProjectPermissionSshHostActions.Create] = true;
|
||||
if (canIssueUserCert)
|
||||
formVal[subject]![0][ProjectPermissionSshHostActions.IssueUserCert] = true;
|
||||
if (canIssueHostCert)
|
||||
formVal[subject]![0][ProjectPermissionSshHostActions.IssueHostCert] = true;
|
||||
|
||||
formVal[subject]![0].conditions = conditions
|
||||
? convertCaslConditionToFormOperator(conditions)
|
||||
: [];
|
||||
formVal[subject]![0].inverted = inverted;
|
||||
formVal[subject]!.push({
|
||||
[ProjectPermissionSshHostActions.Edit]: action.includes(
|
||||
ProjectPermissionSshHostActions.Edit
|
||||
),
|
||||
[ProjectPermissionSshHostActions.Delete]: action.includes(
|
||||
ProjectPermissionSshHostActions.Delete
|
||||
),
|
||||
[ProjectPermissionSshHostActions.Create]: action.includes(
|
||||
ProjectPermissionSshHostActions.Create
|
||||
),
|
||||
[ProjectPermissionSshHostActions.IssueUserCert]: action.includes(
|
||||
ProjectPermissionSshHostActions.IssueUserCert
|
||||
),
|
||||
[ProjectPermissionSshHostActions.IssueHostCert]: action.includes(
|
||||
ProjectPermissionSshHostActions.IssueHostCert
|
||||
),
|
||||
conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [],
|
||||
inverted
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -910,11 +908,10 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
[ProjectPermissionSub.SshHosts]: {
|
||||
title: "SSH Hosts",
|
||||
actions: [
|
||||
{ label: "Read", value: ProjectPermissionSshHostActions.Read },
|
||||
{ label: "Create", value: ProjectPermissionSshHostActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionSshHostActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionSshHostActions.Delete },
|
||||
{ label: "Connect", value: ProjectPermissionSshHostActions.IssueUserCert },
|
||||
{ label: "Issue User Certificate", value: ProjectPermissionSshHostActions.IssueUserCert },
|
||||
{ label: "Issue Host Certificate", value: ProjectPermissionSshHostActions.IssueHostCert }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,12 +57,12 @@ export const SshCertificatesTable = () => {
|
||||
</Td>
|
||||
<Td>
|
||||
{certificate.notBefore
|
||||
? format(new Date(certificate.notBefore), "yyyy-MM-dd")
|
||||
? format(new Date(certificate.notBefore), "yyyy-MM-dd | HH:mm:ss")
|
||||
: "-"}
|
||||
</Td>
|
||||
<Td>
|
||||
{certificate.notAfter
|
||||
? format(new Date(certificate.notAfter), "yyyy-MM-dd")
|
||||
? format(new Date(certificate.notAfter), "yyyy-MM-dd | HH:mm:ss")
|
||||
: "-"}
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@@ -43,17 +43,19 @@ export const SshHostsSection = () => {
|
||||
<div className="mb-4 flex justify-between">
|
||||
<p className="text-xl font-semibold text-mineshaft-100">Hosts</p>
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.SshHosts}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("sshHost")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Add Host
|
||||
</Button>
|
||||
)}
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("sshHost")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Add Host
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
<SshHostsTable handlePopUpOpen={handlePopUpOpen} />
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { faEllipsis, faPencil, faServer, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import {
|
||||
faDownload,
|
||||
faEllipsis,
|
||||
faPencil,
|
||||
faServer,
|
||||
faTrash
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import FileSaver from "file-saver";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -20,7 +28,7 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { useListWorkspaceSshHosts } from "@app/hooks/api";
|
||||
import { fetchSshHostUserCaPublicKey, useListWorkspaceSshHosts } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@@ -34,6 +42,24 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data, isPending } = useListWorkspaceSshHosts(currentWorkspace?.id || "");
|
||||
|
||||
const downloadTxtFile = (filename: string, content: string) => {
|
||||
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
|
||||
FileSaver.saveAs(blob, filename);
|
||||
};
|
||||
|
||||
const handleDownloadUserCaKey = async (sshHostId: string) => {
|
||||
try {
|
||||
const publicKey = await fetchSshHostUserCaPublicKey(sshHostId);
|
||||
downloadTxtFile("infisical_user_ca.pub", publicKey);
|
||||
} catch (err) {
|
||||
console.error("Failed to download User CA public key", err);
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to download User CA public key"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableContainer>
|
||||
@@ -41,6 +67,7 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Hostname</Th>
|
||||
<Th>Login User - Authorized Principals Mapping</Th>
|
||||
<Th />
|
||||
</Tr>
|
||||
</THead>
|
||||
@@ -52,11 +79,28 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
||||
data.map((host) => {
|
||||
return (
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
// className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
className="h-10"
|
||||
key={`ssh-host-${host.id}`}
|
||||
>
|
||||
<Td>{host.hostname}</Td>
|
||||
<Td className="flex justify-end">
|
||||
<Td>
|
||||
{host.loginMappings.length === 0 ? (
|
||||
<span className="italic text-mineshaft-400">None</span>
|
||||
) : (
|
||||
host.loginMappings.map(({ loginUser, allowedPrincipals }) => (
|
||||
<div key={`${host.id}-${loginUser}`} className="mb-2">
|
||||
<div className="text-mineshaft-200">{loginUser}</div>
|
||||
{allowedPrincipals.map((principal) => (
|
||||
<div key={principal} className="ml-4">
|
||||
└─ {principal}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</Td>
|
||||
<Td className="text-right align-middle">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||
@@ -66,6 +110,15 @@ export const SshHostsTable = ({ handlePopUpOpen }: Props) => {
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<DropdownMenuItem
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
handleDownloadUserCaKey(host.id);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
>
|
||||
Download User CA Public Key
|
||||
</DropdownMenuItem>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.SshHosts}
|
||||
|
||||
Reference in New Issue
Block a user