mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 14:27:30 +00:00
Merge pull request #1014 from G3root/e2e-warning
feat: display warning message in integrations page when e2e is enabled
This commit is contained in:
@@ -8,7 +8,7 @@ module.exports = {
|
|||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2021: true,
|
es2021: true,
|
||||||
"es6": true
|
es6: true
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
"airbnb",
|
"airbnb",
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react";
|
||||||
|
|
||||||
|
import { Alert, AlertDescription } from "./Alert";
|
||||||
|
|
||||||
|
const meta: Meta<typeof Alert> = {
|
||||||
|
title: "Components/Alert",
|
||||||
|
component: Alert,
|
||||||
|
tags: ["v2"]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof Alert>;
|
||||||
|
|
||||||
|
const ExampleComponent = () => <AlertDescription>this is a description</AlertDescription>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Warning: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />,
|
||||||
|
variant: "warning"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Danger: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />,
|
||||||
|
variant: "danger"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithCustomIcon: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />,
|
||||||
|
variant: "warning",
|
||||||
|
icon: <FontAwesomeIcon icon={faPlus} />
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithOutIcon: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />,
|
||||||
|
variant: "warning",
|
||||||
|
icon: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithOutTitle: Story = {
|
||||||
|
args: {
|
||||||
|
children: <ExampleComponent />,
|
||||||
|
variant: "warning",
|
||||||
|
hideTitle: true
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { forwardRef } from "react";
|
||||||
|
import {
|
||||||
|
faExclamationCircle,
|
||||||
|
faExclamationTriangle,
|
||||||
|
faInfoCircle
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { type VariantProps, cva } from "cva";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
const alertVariants = cva(
|
||||||
|
"w-full bg-mineshaft-800 rounded-lg border px-4 py-3 text-sm flex items-center gap-x-4",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "",
|
||||||
|
danger: "text-red border-red",
|
||||||
|
warning: "text-yellow border-yellow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type AlertProps = {
|
||||||
|
title?: string;
|
||||||
|
hideTitle?: boolean;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const variantTitleMap = {
|
||||||
|
default: "Info",
|
||||||
|
danger: "Danger",
|
||||||
|
warning: "Warning"
|
||||||
|
};
|
||||||
|
|
||||||
|
const variantIconMap = {
|
||||||
|
default: faInfoCircle,
|
||||||
|
danger: faExclamationCircle,
|
||||||
|
warning: faExclamationTriangle
|
||||||
|
};
|
||||||
|
|
||||||
|
const Alert = forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> & AlertProps
|
||||||
|
>(({ className, variant, title, icon, hideTitle = false, children, ...props }, ref) => {
|
||||||
|
const defaultTitle = title ?? variantTitleMap[variant ?? "default"];
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="alert"
|
||||||
|
className={twMerge(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{typeof icon !== "undefined" ? (
|
||||||
|
<>{icon} </>
|
||||||
|
) : (
|
||||||
|
<FontAwesomeIcon className="text-lg" icon={variantIconMap[variant ?? "default"]} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-y-1">
|
||||||
|
{hideTitle ? null : (
|
||||||
|
<h5 className="font-medium leading-none tracking-tight" {...props}>
|
||||||
|
{defaultTitle}
|
||||||
|
</h5>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Alert.displayName = "Alert";
|
||||||
|
|
||||||
|
const AlertDescription = forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={twMerge("text-sm [&_p]:leading-relaxed", className)} {...props} />
|
||||||
|
));
|
||||||
|
AlertDescription.displayName = "AlertDescription";
|
||||||
|
|
||||||
|
export { Alert, AlertDescription };
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Alert, AlertDescription } from "./Alert";
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from "./Accordion";
|
export * from "./Accordion";
|
||||||
|
export * from "./Alert";
|
||||||
export * from "./Button";
|
export * from "./Button";
|
||||||
export * from "./Card";
|
export * from "./Card";
|
||||||
export * from "./Checkbox";
|
export * from "./Checkbox";
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ export const IntegrationsPage = withProjectPermission(
|
|||||||
integrations={integrations}
|
integrations={integrations}
|
||||||
environments={environments}
|
environments={environments}
|
||||||
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
|
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
|
||||||
|
isBotActive={bot?.isActive}
|
||||||
|
workspaceId={workspaceId}
|
||||||
/>
|
/>
|
||||||
<CloudIntegrationSection
|
<CloudIntegrationSection
|
||||||
isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading}
|
isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading}
|
||||||
|
|||||||
+26
-3
@@ -1,9 +1,12 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||||
|
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -23,17 +26,22 @@ type Props = {
|
|||||||
integrations?: TIntegration[];
|
integrations?: TIntegration[];
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
|
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
|
||||||
|
isBotActive: boolean | undefined;
|
||||||
|
workspaceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IntegrationsSection = ({
|
export const IntegrationsSection = ({
|
||||||
integrations = [],
|
integrations = [],
|
||||||
environments = [],
|
environments = [],
|
||||||
isLoading,
|
isLoading,
|
||||||
onIntegrationDelete
|
onIntegrationDelete,
|
||||||
|
isBotActive,
|
||||||
|
workspaceId
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
"deleteConfirmation"
|
"deleteConfirmation"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="mx-4 mb-4 mt-6 flex flex-col items-start justify-between px-2 text-xl">
|
<div className="mx-4 mb-4 mt-6 flex flex-col items-start justify-between px-2 text-xl">
|
||||||
@@ -45,7 +53,22 @@ export const IntegrationsSection = ({
|
|||||||
<Skeleton className="h-28" />
|
<Skeleton className="h-28" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isLoading && !integrations.length && (
|
|
||||||
|
{!isBotActive && (
|
||||||
|
<div className="px-6 py-4">
|
||||||
|
<Alert hideTitle variant="warning">
|
||||||
|
<AlertDescription>
|
||||||
|
All the active integrations will be disabled. Disable End-to-End Encryption in{" "}
|
||||||
|
<Link href={`/project/${workspaceId}/settings`} passHref>
|
||||||
|
<a className="underline underline-offset-2">project settings </a>
|
||||||
|
</Link>
|
||||||
|
to re-enable it .
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading && !integrations.length && isBotActive && (
|
||||||
<div className="mx-6">
|
<div className="mx-6">
|
||||||
<EmptyState
|
<EmptyState
|
||||||
className="rounded-md border border-mineshaft-700 pt-8 pb-4"
|
className="rounded-md border border-mineshaft-700 pt-8 pb-4"
|
||||||
@@ -53,7 +76,7 @@ export const IntegrationsSection = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isLoading && (
|
{!isLoading && isBotActive && (
|
||||||
<div className="flex flex-col space-y-4 p-6 pt-0">
|
<div className="flex flex-col space-y-4 p-6 pt-0">
|
||||||
{integrations?.map((integration) => (
|
{integrations?.map((integration) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
+25
-16
@@ -3,7 +3,7 @@ import {
|
|||||||
decryptAssymmetric,
|
decryptAssymmetric,
|
||||||
encryptAssymmetric
|
encryptAssymmetric
|
||||||
} from "@app/components/utilities/cryptography/crypto";
|
} from "@app/components/utilities/cryptography/crypto";
|
||||||
import { Checkbox } from "@app/components/v2";
|
import { Alert, AlertDescription, Checkbox } from "@app/components/v2";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||||
import { useGetUserWsKey, useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api";
|
import { useGetUserWsKey, useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api";
|
||||||
|
|
||||||
@@ -76,30 +76,39 @@ export const E2EESection = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return bot ? (
|
return bot ? (
|
||||||
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<p className="mb-3 text-xl font-semibold">End-to-End Encryption</p>
|
<p className="mb-3 text-xl font-semibold">End-to-End Encryption</p>
|
||||||
<p className="text-gray-400 mb-8">
|
<p className="mb-8 text-gray-400">
|
||||||
Disabling, end-to-end encryption (E2EE) unlocks capabilities like native integrations to
|
Disabling, end-to-end encryption (E2EE) unlocks capabilities like native integrations to
|
||||||
cloud providers as well as HTTP calls to get secrets back raw but enables the server to
|
cloud providers as well as HTTP calls to get secrets back raw but enables the server to
|
||||||
read/decrypt your secret values.
|
read/decrypt your secret values.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-400 mb-8">
|
<p className="mb-8 text-gray-400">
|
||||||
Note that, even with E2EE disabled, your secrets are always encrypted at rest.
|
Note that, even with E2EE disabled, your secrets are always encrypted at rest.
|
||||||
</p>
|
</p>
|
||||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<div className="w-max">
|
<div className="flex w-full flex-col gap-y-3">
|
||||||
<Checkbox
|
<div className="w-max">
|
||||||
className="data-[state=checked]:bg-primary"
|
<Checkbox
|
||||||
id="autoCapitalization"
|
className="data-[state=checked]:bg-primary"
|
||||||
isChecked={!bot.isActive}
|
id="autoCapitalization"
|
||||||
isDisabled={!isAllowed}
|
isChecked={!bot.isActive}
|
||||||
onCheckedChange={async () => {
|
isDisabled={!isAllowed}
|
||||||
await toggleBotActivate();
|
onCheckedChange={async () => {
|
||||||
}}
|
await toggleBotActivate();
|
||||||
>
|
}}
|
||||||
End-to-end encryption enabled
|
>
|
||||||
</Checkbox>
|
End-to-end encryption enabled
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Alert variant="warning">
|
||||||
|
<AlertDescription>
|
||||||
|
Enabling End-to-end encryption disables all the integrations
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
|||||||
Reference in New Issue
Block a user