mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 18:29:06 +00:00
feat(rbac): added frontend permission validation for missing pages and merged new features
This commit is contained in:
@@ -138,6 +138,17 @@ export const getRoles = async (req: Request, res: Response) => {
|
||||
|
||||
const customRoles = await Role.find({ organization: orgId, isOrgRole, workspace: workspaceId });
|
||||
const roles = [
|
||||
...(isOrgRole
|
||||
? [
|
||||
{
|
||||
_id: "owner",
|
||||
name: "Owner",
|
||||
slug: "owner",
|
||||
description: "Complete administration access over the organization.",
|
||||
permissions: adminPermissions.rules
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
_id: "admin",
|
||||
name: "Admin",
|
||||
@@ -152,24 +163,19 @@ export const getRoles = async (req: Request, res: Response) => {
|
||||
description: "Non-administrative role in an organization",
|
||||
permissions: isOrgRole ? memberPermissions.rules : adminProjectPermissions.rules
|
||||
},
|
||||
...(isOrgRole
|
||||
? []
|
||||
: [
|
||||
{
|
||||
_id: "viewer",
|
||||
name: "Viewer",
|
||||
slug: "viewer",
|
||||
description: "Non-administrative role in an organization",
|
||||
permissions: isOrgRole ? viewerProjectPermission.rules : viewerProjectPermission.rules
|
||||
},
|
||||
}
|
||||
]),
|
||||
...customRoles
|
||||
];
|
||||
if (isOrgRole) {
|
||||
roles.unshift({
|
||||
_id: "owner",
|
||||
name: "Owner",
|
||||
slug: "owner",
|
||||
description: "Complete administration access over the organization.",
|
||||
permissions: adminPermissions.rules
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
message: "Successfully fetched role list",
|
||||
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
} & BoundCanProps<TProjectPermission>;
|
||||
|
||||
export const ProjectPermissionCan: FunctionComponent<Props> = ({
|
||||
label = "Permission Denied. Kindly contact your org admin",
|
||||
label = "Permission Denied. Kindly contact your project admin",
|
||||
children,
|
||||
passThrough = true,
|
||||
...props
|
||||
|
||||
@@ -4,7 +4,8 @@ import { useRouter } from "next/router";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { Button, Modal, ModalContent } from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { withProjectPermission } from "@app/hoc";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
useDeleteIntegration,
|
||||
@@ -31,7 +32,8 @@ type Props = {
|
||||
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
|
||||
};
|
||||
|
||||
export const IntegrationsPage = ({ frameworkIntegrations }: Props) => {
|
||||
export const IntegrationsPage = withProjectPermission(
|
||||
({ frameworkIntegrations }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const router = useRouter();
|
||||
@@ -218,4 +220,6 @@ export const IntegrationsPage = ({ frameworkIntegrations }: Props) => {
|
||||
<FrameworkIntegrationSection frameworks={frameworkIntegrations} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
{ action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Integrations }
|
||||
);
|
||||
|
||||
+16
@@ -2,7 +2,9 @@ import { useTranslation } from "react-i18next";
|
||||
import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { DeleteActionModal, Skeleton, Tooltip } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
|
||||
|
||||
@@ -28,6 +30,8 @@ export const CloudIntegrationSection = ({
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"deleteConfirmation"
|
||||
] as const);
|
||||
const permission = useProjectPermission();
|
||||
const { createNotification } = useNotificationContext();
|
||||
|
||||
const isEmpty = !isLoading && !cloudIntegrations?.length;
|
||||
|
||||
@@ -57,6 +61,18 @@ export const CloudIntegrationSection = ({
|
||||
} flex h-32 flex-row items-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4`}
|
||||
onClick={() => {
|
||||
if (!cloudIntegration.isAvailable) return;
|
||||
if (
|
||||
permission.cannot(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Integrations
|
||||
)
|
||||
) {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Permission Denied. Kindly contact your project admin"
|
||||
});
|
||||
return;
|
||||
}
|
||||
onIntegrationStart(cloudIntegration.slug);
|
||||
}}
|
||||
key={cloudIntegration.slug}
|
||||
|
||||
+10
@@ -2,6 +2,7 @@ import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
DeleteActionModal,
|
||||
EmptyState,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
Skeleton,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { TIntegration } from "@app/hooks/api/types";
|
||||
|
||||
@@ -125,11 +127,17 @@ export const IntegrationsSection = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex cursor-default items-center">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={ProjectPermissionSub.Integrations}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<div className="ml-2 opacity-80 duration-200 hover:opacity-100">
|
||||
<Tooltip content="Remove Integration">
|
||||
<IconButton
|
||||
onClick={() => handlePopUpOpen("deleteConfirmation", integration)}
|
||||
ariaLabel="delete"
|
||||
isDisabled={!isAllowed}
|
||||
colorSchema="danger"
|
||||
variant="star"
|
||||
>
|
||||
@@ -137,6 +145,8 @@ export const IntegrationsSection = ({
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
+2
@@ -42,6 +42,7 @@ export const AutoCapitalizationSection = withProjectPermission(
|
||||
<p className="mb-3 text-xl font-semibold">{t("settings.project.auto-capitalization")}</p>
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
||||
{(isAllowed) => (
|
||||
<div className="w-max">
|
||||
<Checkbox
|
||||
className="data-[state=checked]:bg-primary"
|
||||
id="autoCapitalization"
|
||||
@@ -53,6 +54,7 @@ export const AutoCapitalizationSection = withProjectPermission(
|
||||
>
|
||||
{t("settings.project.auto-capitalization-description")}
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
|
||||
@@ -90,6 +90,7 @@ export const E2EESection = withProjectPermission(
|
||||
</p>
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
||||
{(isAllowed) => (
|
||||
<div className="w-max">
|
||||
<Checkbox
|
||||
className="data-[state=checked]:bg-primary"
|
||||
id="autoCapitalization"
|
||||
@@ -101,6 +102,7 @@ export const E2EESection = withProjectPermission(
|
||||
>
|
||||
End-to-end encryption enabled
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
|
||||
+32
-12
@@ -16,9 +16,7 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import {
|
||||
useReorderWsEnvironment
|
||||
} from "@app/hooks/api";
|
||||
import { useReorderWsEnvironment } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@@ -43,14 +41,19 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
||||
try {
|
||||
if (!currentWorkspace?._id) return;
|
||||
|
||||
const indexOfEnv = currentWorkspace.environments.findIndex((env) => env.name === name && env.slug === slug);
|
||||
const indexOfEnv = currentWorkspace.environments.findIndex(
|
||||
(env) => env.name === name && env.slug === slug
|
||||
);
|
||||
|
||||
// check that this reordering is possible
|
||||
if (indexOfEnv === 0 && shouldMoveUp || indexOfEnv === currentWorkspace.environments.length - 1 && !shouldMoveUp) {
|
||||
return
|
||||
if (
|
||||
(indexOfEnv === 0 && shouldMoveUp) ||
|
||||
(indexOfEnv === currentWorkspace.environments.length - 1 && !shouldMoveUp)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const indexToSwap = shouldMoveUp ? indexOfEnv - 1 : indexOfEnv + 1
|
||||
const indexToSwap = shouldMoveUp ? indexOfEnv - 1 : indexOfEnv + 1;
|
||||
|
||||
await reorderWsEnvironment.mutateAsync({
|
||||
workspaceID: currentWorkspace._id,
|
||||
@@ -92,31 +95,48 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.Environments}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
handleReorderEnv(false, name, slug)
|
||||
handleReorderEnv(false, name, slug);
|
||||
}}
|
||||
colorSchema="primary"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
isDisabled={pos === currentWorkspace.environments.length - 1}
|
||||
isDisabled={pos === currentWorkspace.environments.length - 1 || !isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowDown} />
|
||||
</IconButton>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.Environments}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
className="mr-3 py-2"
|
||||
onClick={() => {
|
||||
handleReorderEnv(true, name, slug)
|
||||
handleReorderEnv(true, name, slug);
|
||||
}}
|
||||
colorSchema="primary"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
isDisabled={pos === 0}
|
||||
isDisabled={pos === 0 || !isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowUp} />
|
||||
</IconButton>
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Environments}
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.Environments}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<IconButton
|
||||
|
||||
+12
-1
@@ -1,3 +1,4 @@
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric
|
||||
@@ -65,9 +66,19 @@ export const ProjectIndexSecretsSection = withProjectPermission(
|
||||
secrets. To access individual secrets by name through the SDK and public API, please
|
||||
enable blind indexing.
|
||||
</p>
|
||||
<Button onClick={onEnableBlindIndices} color="mineshaft" size="sm" type="submit">
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={onEnableBlindIndices}
|
||||
isDisabled={!isAllowed}
|
||||
color="mineshaft"
|
||||
size="sm"
|
||||
type="submit"
|
||||
>
|
||||
Enable Blind Indexing
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
) : (
|
||||
<div />
|
||||
|
||||
Reference in New Issue
Block a user