mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 15:41:27 +00:00
Merge remote-tracking branch 'origin' into certificate-alerting
This commit is contained in:
@@ -106,9 +106,13 @@ export const IntegrationsPage = withProjectPermission(
|
||||
handleProviderIntegration(provider);
|
||||
};
|
||||
|
||||
const handleIntegrationDelete = async (integrationId: string, cb: () => void) => {
|
||||
const handleIntegrationDelete = async (
|
||||
integrationId: string,
|
||||
shouldDeleteIntegrationSecrets: boolean,
|
||||
cb: () => void
|
||||
) => {
|
||||
try {
|
||||
await deleteIntegration({ id: integrationId, workspaceId });
|
||||
await deleteIntegration({ id: integrationId, workspaceId, shouldDeleteIntegrationSecrets });
|
||||
if (cb) cb();
|
||||
createNotification({
|
||||
type: "success",
|
||||
@@ -152,7 +156,7 @@ export const IntegrationsPage = withProjectPermission(
|
||||
isLoading={isIntegrationLoading}
|
||||
integrations={integrations}
|
||||
environments={environments}
|
||||
onIntegrationDelete={({ id }, cb) => handleIntegrationDelete(id, cb)}
|
||||
onIntegrationDelete={handleIntegrationDelete}
|
||||
workspaceId={workspaceId}
|
||||
/>
|
||||
<CloudIntegrationSection
|
||||
|
||||
+57
-9
@@ -7,6 +7,7 @@ import { integrationSlugNameMapping } from "public/data/frequentConstants";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
DeleteActionModal,
|
||||
EmptyState,
|
||||
FormLabel,
|
||||
@@ -16,7 +17,7 @@ import {
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import { useSyncIntegration } from "@app/hooks/api/integrations/queries";
|
||||
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
|
||||
import { TIntegration } from "@app/hooks/api/types";
|
||||
@@ -25,7 +26,11 @@ type Props = {
|
||||
environments: Array<{ name: string; slug: string; id: string }>;
|
||||
integrations?: TIntegration[];
|
||||
isLoading?: boolean;
|
||||
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
|
||||
onIntegrationDelete: (
|
||||
integrationId: string,
|
||||
shouldDeleteIntegrationSecrets: boolean,
|
||||
cb: () => void
|
||||
) => Promise<void>;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@@ -37,10 +42,12 @@ export const IntegrationsSection = ({
|
||||
workspaceId
|
||||
}: Props) => {
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"deleteConfirmation"
|
||||
"deleteConfirmation",
|
||||
"deleteSecretsConfirmation"
|
||||
] as const);
|
||||
|
||||
const { mutate: syncIntegration } = useSyncIntegration();
|
||||
const [shouldDeleteSecrets, setShouldDeleteSecrets] = useToggle(false);
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
@@ -249,7 +256,10 @@ export const IntegrationsSection = ({
|
||||
<div className="flex items-end opacity-80 duration-200 hover:opacity-100">
|
||||
<Tooltip content="Remove Integration">
|
||||
<IconButton
|
||||
onClick={() => handlePopUpOpen("deleteConfirmation", integration)}
|
||||
onClick={() => {
|
||||
setShouldDeleteSecrets.off();
|
||||
handlePopUpOpen("deleteConfirmation", integration);
|
||||
}}
|
||||
ariaLabel="delete"
|
||||
isDisabled={!isAllowed}
|
||||
colorSchema="danger"
|
||||
@@ -281,11 +291,49 @@ export const IntegrationsSection = ({
|
||||
(popUp?.deleteConfirmation?.data as TIntegration)?.integration ||
|
||||
""
|
||||
}
|
||||
onDeleteApproved={async () =>
|
||||
onIntegrationDelete(popUp?.deleteConfirmation.data as TIntegration, () =>
|
||||
handlePopUpClose("deleteConfirmation")
|
||||
)
|
||||
}
|
||||
onDeleteApproved={async () => {
|
||||
if (shouldDeleteSecrets) {
|
||||
handlePopUpOpen("deleteSecretsConfirmation");
|
||||
return;
|
||||
}
|
||||
|
||||
await onIntegrationDelete(
|
||||
(popUp?.deleteConfirmation.data as TIntegration).id,
|
||||
false,
|
||||
() => handlePopUpClose("deleteConfirmation")
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(popUp?.deleteConfirmation?.data as TIntegration)?.integration === "github" && (
|
||||
<div className="mt-4">
|
||||
<Checkbox
|
||||
id="delete-integration-secrets"
|
||||
checkIndicatorBg="text-white"
|
||||
onCheckedChange={() => setShouldDeleteSecrets.toggle()}
|
||||
>
|
||||
Delete previously synced secrets from the destination
|
||||
</Checkbox>
|
||||
</div>
|
||||
)}
|
||||
</DeleteActionModal>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteSecretsConfirmation.isOpen}
|
||||
title={`Are you sure you also want to delete secrets on ${
|
||||
(popUp?.deleteConfirmation.data as TIntegration)?.integration
|
||||
}?`}
|
||||
subTitle="By confirming, you acknowledge that all secrets managed by this integration will be removed from the destination. This action is irreversible."
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteSecretsConfirmation", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={async () => {
|
||||
await onIntegrationDelete(
|
||||
(popUp?.deleteConfirmation.data as TIntegration).id,
|
||||
true,
|
||||
() => {
|
||||
handlePopUpClose("deleteSecretsConfirmation");
|
||||
handlePopUpClose("deleteConfirmation");
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+6
-6
@@ -97,7 +97,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
</Td>
|
||||
<Td>{secretVersion?.secretComment}</Td>
|
||||
<Td>
|
||||
{secretVersion?.tags?.map(({ name, id: tagId, color }) => (
|
||||
{secretVersion?.tags?.map(({ slug, id: tagId, color }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={`${secretVersion.id}-${tagId}`}
|
||||
@@ -106,7 +106,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: color || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
))}
|
||||
</Td>
|
||||
@@ -119,7 +119,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
</Td>
|
||||
<Td>{newVersion?.secretComment}</Td>
|
||||
<Td>
|
||||
{newVersion?.tags?.map(({ name, id: tagId, color }) => (
|
||||
{newVersion?.tags?.map(({ slug, id: tagId, color }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={`${newVersion.id}-${tagId}`}
|
||||
@@ -128,7 +128,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: color || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
))}
|
||||
</Td>
|
||||
@@ -157,7 +157,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
</Td>
|
||||
<Td>
|
||||
{(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map(
|
||||
({ name, id: tagId, color }) => (
|
||||
({ slug, id: tagId, color }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={`${
|
||||
@@ -168,7 +168,7 @@ export const SecretApprovalRequestChangeItem = ({
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: color || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -327,7 +327,7 @@ export const ActionBar = ({
|
||||
</DropdownSubMenuTrigger>
|
||||
<DropdownSubMenuContent className="rounded-l-none">
|
||||
<DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel>
|
||||
{tags.map(({ id, name, color }) => (
|
||||
{tags.map(({ id, slug, color }) => (
|
||||
<DropdownMenuItem
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
@@ -342,7 +342,7 @@ export const ActionBar = ({
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: color || "#bec2c8" }}
|
||||
/>
|
||||
{name}
|
||||
{slug}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
|
||||
@@ -254,7 +254,7 @@ export const SecretDetailSidebar = ({
|
||||
)}
|
||||
<FormControl label="Tags" className="">
|
||||
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
|
||||
{fields.map(({ tagColor, id: formId, name, id }) => (
|
||||
{fields.map(({ tagColor, id: formId, slug, id }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={formId}
|
||||
@@ -271,7 +271,7 @@ export const SecretDetailSidebar = ({
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: tagColor || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
))}
|
||||
<DropdownMenu>
|
||||
@@ -296,7 +296,7 @@ export const SecretDetailSidebar = ({
|
||||
<DropdownMenuContent align="end" className="z-[100]">
|
||||
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
|
||||
{tags.map((tag) => {
|
||||
const { id: tagId, name, color } = tag;
|
||||
const { id: tagId, slug, color } = tag;
|
||||
|
||||
const isSelected = selectedTagsGroupById?.[tagId];
|
||||
return (
|
||||
@@ -311,7 +311,7 @@ export const SecretDetailSidebar = ({
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: color || "#bec2c8" }}
|
||||
/>
|
||||
{name}
|
||||
{slug}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
|
||||
@@ -336,7 +336,7 @@ export const SecretItem = memo(
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
|
||||
{tags.map((tag) => {
|
||||
const { id: tagId, name, color } = tag;
|
||||
const { id: tagId, slug, color } = tag;
|
||||
|
||||
const isTagSelected = selectedTagsGroupById?.[tagId];
|
||||
return (
|
||||
@@ -358,7 +358,7 @@ export const SecretItem = memo(
|
||||
className="mr-2 h-2 w-2 rounded-full"
|
||||
style={{ background: color || "#bec2c8" }}
|
||||
/>
|
||||
{name}
|
||||
{slug}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
|
||||
@@ -49,7 +49,6 @@ export const formSchema = z.object({
|
||||
tags: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
slug: z.string(),
|
||||
tagColor: z.string().optional()
|
||||
})
|
||||
|
||||
@@ -151,7 +151,7 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
|
||||
<Td className="border-r border-mineshaft-600">Tags</Td>
|
||||
{isModified && (
|
||||
<Td className="border-r border-mineshaft-600">
|
||||
{preSecret?.tags?.map(({ name, id: tagId, color }) => (
|
||||
{preSecret?.tags?.map(({ slug, id: tagId, color }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={`${preSecret.id}-${tagId}`}
|
||||
@@ -160,13 +160,13 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: color || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
))}
|
||||
</Td>
|
||||
)}
|
||||
<Td>
|
||||
{postSecret?.tags?.map(({ name, id: tagId, color }) => (
|
||||
{postSecret?.tags?.map(({ slug, id: tagId, color }) => (
|
||||
<Tag
|
||||
className="flex w-min items-center space-x-2"
|
||||
key={`${postSecret.id}-${tagId}`}
|
||||
@@ -175,7 +175,7 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
|
||||
className="h-3 w-3 rounded-full"
|
||||
style={{ backgroundColor: color || "#bec2c8" }}
|
||||
/>
|
||||
<div className="text-sm">{name}</div>
|
||||
<div className="text-sm">{slug}</div>
|
||||
</Tag>
|
||||
))}
|
||||
</Td>
|
||||
|
||||
@@ -62,7 +62,7 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDelet
|
||||
const [isDeletePopupOpen, setIsDeletePopupOpen] = useToggle();
|
||||
const { data } = useGetSSOConfig(currentOrg?.id ?? "");
|
||||
|
||||
const { control, handleSubmit, reset, watch } = useForm<AddSSOFormData>({
|
||||
const { control, handleSubmit, reset, watch, setValue, getValues } = useForm<AddSSOFormData>({
|
||||
defaultValues: {
|
||||
authProvider: AuthProvider.OKTA_SAML
|
||||
},
|
||||
@@ -188,8 +188,8 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDelet
|
||||
entityId: "SP Entity ID",
|
||||
entryPoint: "SSO URL",
|
||||
entryPointPlaceholder: "https://accounts.google.com/o/saml2/idp?idpid=xxx",
|
||||
issuer: "IdP Entity ID",
|
||||
issuerPlaceholder: "https://accounts.google.com/o/saml2/idp?idpid=xxx"
|
||||
issuer: "Issuer",
|
||||
issuerPlaceholder: window.origin
|
||||
};
|
||||
default:
|
||||
return {
|
||||
@@ -204,6 +204,11 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDelet
|
||||
};
|
||||
|
||||
const authProvider = watch("authProvider");
|
||||
useEffect(() => {
|
||||
if (authProvider === AuthProvider.GOOGLE_SAML && getValues("issuer") === "") {
|
||||
setValue("issuer", window.origin);
|
||||
}
|
||||
}, [authProvider]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
+14
-13
@@ -1,6 +1,7 @@
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Modal, ModalClose, ModalContent } from "@app/components/v2";
|
||||
@@ -8,11 +9,13 @@ import { useWorkspace } from "@app/context";
|
||||
import { useCreateWsTag } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const schema = yup.object({
|
||||
name: yup.string().required().label("Tag Name")
|
||||
const schema = z.object({
|
||||
slug: z.string().refine((v) => slugify(v) === v, {
|
||||
message: "Invalid slug. Slug can only contain alphanumeric characters and hyphens."
|
||||
})
|
||||
});
|
||||
|
||||
export type FormData = yup.InferType<typeof schema>;
|
||||
export type FormData = z.infer<typeof schema>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["CreateSecretTag", "deleteTagConfirmation"]>;
|
||||
@@ -26,7 +29,6 @@ type Props = {
|
||||
};
|
||||
|
||||
export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) => {
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const createWsTag = useCreateWsTag();
|
||||
const {
|
||||
@@ -35,17 +37,16 @@ export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }
|
||||
handleSubmit,
|
||||
formState: { isSubmitting }
|
||||
} = useForm<FormData>({
|
||||
resolver: yupResolver(schema)
|
||||
resolver: zodResolver(schema)
|
||||
});
|
||||
|
||||
const onFormSubmit = async ({ name }: FormData) => {
|
||||
const onFormSubmit = async ({ slug }: FormData) => {
|
||||
try {
|
||||
if (!currentWorkspace?.id) return;
|
||||
|
||||
await createWsTag.mutateAsync({
|
||||
workspaceID: currentWorkspace?.id,
|
||||
tagName: name,
|
||||
tagSlug: name.replace(/\s+/g, " ").replace(" ", "_"),
|
||||
tagSlug: slug,
|
||||
tagColor: ""
|
||||
});
|
||||
|
||||
@@ -80,11 +81,11 @@ export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
name="slug"
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Tag Name" isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input {...field} placeholder="Type your tag name" />
|
||||
<FormControl label="Tag Slug" isError={Boolean(error)} errorText={error?.message}>
|
||||
<Input {...field} placeholder="Type your tag slug" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
+3
-5
@@ -40,7 +40,6 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Tag</Th>
|
||||
<Th>Slug</Th>
|
||||
<Th aria-label="button" />
|
||||
</Tr>
|
||||
@@ -49,9 +48,8 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
|
||||
{isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />}
|
||||
{!isLoading &&
|
||||
data &&
|
||||
data.map(({ id, name, slug }) => (
|
||||
<Tr key={name}>
|
||||
<Td>{name}</Td>
|
||||
data.map(({ id, slug }) => (
|
||||
<Tr key={id}>
|
||||
<Td>{slug}</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<ProjectPermissionCan
|
||||
@@ -62,7 +60,7 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
handlePopUpOpen("deleteTagConfirmation", {
|
||||
name,
|
||||
name: slug,
|
||||
id
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export const ShareSecretsTable = ({ handlePopUpOpen }: Props) => {
|
||||
</Table>
|
||||
{!isLoading &&
|
||||
data?.secrets &&
|
||||
data.secrets.length >= perPage &&
|
||||
data?.totalCount >= perPage &&
|
||||
data?.totalCount !== undefined && (
|
||||
<Pagination
|
||||
count={data.totalCount}
|
||||
|
||||
@@ -15,7 +15,6 @@ const formSchema = z.object({
|
||||
authRateLimit: z.number(),
|
||||
inviteUserRateLimit: z.number(),
|
||||
mfaRateLimit: z.number(),
|
||||
creationLimit: z.number(),
|
||||
publicEndpointLimit: z.number()
|
||||
});
|
||||
|
||||
@@ -41,7 +40,6 @@ export const RateLimitPanel = () => {
|
||||
authRateLimit: rateLimit?.authRateLimit ?? 60,
|
||||
inviteUserRateLimit: rateLimit?.inviteUserRateLimit ?? 30,
|
||||
mfaRateLimit: rateLimit?.mfaRateLimit ?? 20,
|
||||
creationLimit: rateLimit?.creationLimit ?? 30,
|
||||
publicEndpointLimit: rateLimit?.publicEndpointLimit ?? 30
|
||||
}
|
||||
});
|
||||
@@ -60,7 +58,6 @@ export const RateLimitPanel = () => {
|
||||
authRateLimit,
|
||||
inviteUserRateLimit,
|
||||
mfaRateLimit,
|
||||
creationLimit,
|
||||
publicEndpointLimit
|
||||
} = formData;
|
||||
|
||||
@@ -71,7 +68,6 @@ export const RateLimitPanel = () => {
|
||||
authRateLimit,
|
||||
inviteUserRateLimit,
|
||||
mfaRateLimit,
|
||||
creationLimit,
|
||||
publicEndpointLimit
|
||||
});
|
||||
createNotification({
|
||||
@@ -210,25 +206,6 @@ export const RateLimitPanel = () => {
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={300}
|
||||
name="creationLimit"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="New resource creation requests per minute"
|
||||
className="w-72"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={300}
|
||||
|
||||
Reference in New Issue
Block a user