mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 14:27:59 +00:00
Update GCP SM and GitLab integrations to have overwrite popup warning
This commit is contained in:
@@ -187,7 +187,8 @@ const integrationSchema = new Schema<IIntegration>(
|
|||||||
default: "/",
|
default: "/",
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
type: Schema.Types.Mixed
|
type: Schema.Types.Mixed,
|
||||||
|
default: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -8,6 +9,8 @@ import { useRouter } from "next/router";
|
|||||||
import { faGoogle } from "@fortawesome/free-brands-svg-icons";
|
import { faGoogle } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useGetCloudIntegrations,
|
useGetCloudIntegrations,
|
||||||
@@ -15,39 +18,27 @@ import {
|
|||||||
|
|
||||||
import { Button, Card, CardTitle, FormControl, TextArea } from "../../../components/v2";
|
import { Button, Card, CardTitle, FormControl, TextArea } from "../../../components/v2";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
accessToken: yup.string().required("Service Account JSON cannot be empty")
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema)
|
||||||
|
});
|
||||||
|
|
||||||
const { data: cloudIntegrations } = useGetCloudIntegrations();
|
const { data: cloudIntegrations } = useGetCloudIntegrations();
|
||||||
|
|
||||||
const { mutateAsync } = useSaveIntegrationAccessToken();
|
const { mutateAsync } = useSaveIntegrationAccessToken();
|
||||||
|
|
||||||
const [accessToken, setAccessToken] = useState("");
|
|
||||||
const [accessTokenErrorText, setAccessTokenErrorText] = useState("");
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleIntegrateWithPAT = async () => {
|
|
||||||
try {
|
|
||||||
setAccessTokenErrorText("");
|
|
||||||
if (accessToken.length === 0) {
|
|
||||||
setAccessTokenErrorText("Service account JSON cannot be blank");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
const integrationAuth = await mutateAsync({
|
|
||||||
workspaceId: localStorage.getItem("projectData.id"),
|
|
||||||
integration: "gcp-secret-manager",
|
|
||||||
refreshToken: accessToken
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsLoading(false);
|
|
||||||
|
|
||||||
router.push(`/integrations/gcp-secret-manager/create?integrationAuthId=${integrationAuth._id}`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIntegrateWithOAuth = () => {
|
const handleIntegrateWithOAuth = () => {
|
||||||
if (!cloudIntegrations) return;
|
if (!cloudIntegrations) return;
|
||||||
@@ -61,6 +52,26 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
|||||||
const link = `https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/cloud-platform&response_type=code&access_type=offline&state=${state}&redirect_uri=${window.location.origin}/integrations/gcp-secret-manager/oauth2/callback&client_id=${integrationOption.clientId}`;
|
const link = `https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/cloud-platform&response_type=code&access_type=offline&state=${state}&redirect_uri=${window.location.origin}/integrations/gcp-secret-manager/oauth2/callback&client_id=${integrationOption.clientId}`;
|
||||||
window.location.assign(link);
|
window.location.assign(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onFormSubmit = async ({
|
||||||
|
accessToken
|
||||||
|
}: FormData) => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const integrationAuth = await mutateAsync({
|
||||||
|
workspaceId: localStorage.getItem("projectData.id"),
|
||||||
|
integration: "gcp-secret-manager",
|
||||||
|
refreshToken: accessToken
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
router.push(`/integrations/gcp-secret-manager/create?integrationAuthId=${integrationAuth._id}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
@@ -94,7 +105,7 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="px-7">
|
<div className="px-6">
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
@@ -104,35 +115,43 @@ export default function GCPSecretManagerAuthorizeIntegrationPage() {
|
|||||||
>
|
>
|
||||||
Continue with OAuth
|
Continue with OAuth
|
||||||
</Button>
|
</Button>
|
||||||
|
<div className='w-full flex flex-row items-center my-4 py-2'>
|
||||||
|
<div className='w-full border-t border-mineshaft-400/40' />
|
||||||
|
<span className="mx-2 text-mineshaft-400 text-xs">or</span>
|
||||||
|
<div className='w-full border-t border-mineshaft-400/40' />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full flex flex-row items-center my-4 py-2 px-7'>
|
<form
|
||||||
<div className='w-full border-t border-mineshaft-400/40' />
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
<span className="mx-2 text-mineshaft-400 text-xs">or</span>
|
className="px-6 text-right pb-8"
|
||||||
<div className='w-full border-t border-mineshaft-400/40' />
|
|
||||||
</div>
|
|
||||||
<FormControl
|
|
||||||
label="GCP Service Account JSON"
|
|
||||||
errorText={accessTokenErrorText}
|
|
||||||
isError={accessTokenErrorText !== "" ?? false}
|
|
||||||
className="px-6"
|
|
||||||
>
|
>
|
||||||
<TextArea
|
<Controller
|
||||||
value={accessToken}
|
control={control}
|
||||||
onChange={(e) => setAccessToken(e.target.value)}
|
name="accessToken"
|
||||||
placeholder=""
|
render={({ field, fieldState: { error } }) => (
|
||||||
className="h-48 border border-mineshaft-600 bg-bunker-900/80"
|
<FormControl
|
||||||
|
label="GCP Service Account JSON"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
{...field}
|
||||||
|
className="h-48 border border-mineshaft-600 bg-bunker-900/80"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Button
|
||||||
<Button
|
colorSchema="primary"
|
||||||
onClick={handleIntegrateWithPAT}
|
variant="outline_bg"
|
||||||
colorSchema="primary"
|
className="mt-2 w-min"
|
||||||
variant="outline_bg"
|
size="sm"
|
||||||
className="mb-8 mt-2 ml-auto mr-6 w-min"
|
type="submit"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
Connect to GCP Secret Manager
|
Connect to GCP Secret Manager
|
||||||
</Button>
|
</Button>
|
||||||
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import Head from "next/head";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen, faBugs, faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { usePopUp } from "@app/hooks";
|
||||||
import {
|
import {
|
||||||
useCreateIntegration
|
useCreateIntegration
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
@@ -21,14 +22,15 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Switch,
|
Switch,
|
||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Tabs
|
Tabs} from "../../../components/v2";
|
||||||
} from "../../../components/v2";
|
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthById
|
useGetIntegrationAuthById
|
||||||
@@ -44,30 +46,36 @@ const schema = yup.object({
|
|||||||
selectedSourceEnvironment: yup.string().required("Source environment is required"),
|
selectedSourceEnvironment: yup.string().required("Source environment is required"),
|
||||||
secretPath: yup.string().required("Secret path is required"),
|
secretPath: yup.string().required("Secret path is required"),
|
||||||
targetAppId: yup.string().required("GCP project is required"),
|
targetAppId: yup.string().required("GCP project is required"),
|
||||||
secretPrefix: yup.string(),
|
secretPrefix: yup.string().trim(),
|
||||||
secretSuffix: yup.string(),
|
secretSuffix: yup.string().trim(),
|
||||||
shouldLabel: yup.boolean(),
|
shouldLabel: yup.boolean(),
|
||||||
labelName: yup.string(),
|
labelName: yup.string().trim(),
|
||||||
labelValue: yup.string()
|
labelValue: yup.string().trim()
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormData = yup.InferType<typeof schema>;
|
type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export default function GCPSecretManagerCreateIntegrationPage() {
|
export default function GCPSecretManagerCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||||
|
"confirmIntegration"
|
||||||
|
] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setValue,
|
setValue,
|
||||||
watch
|
watch
|
||||||
} = useForm<FormData>({
|
} = useForm<FormData>({
|
||||||
resolver: yupResolver(schema),
|
resolver: yupResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
secretPath: "/",
|
secretPath: "/",
|
||||||
shouldLabel: false,
|
secretPrefix: "",
|
||||||
labelName: "managed-by",
|
secretSuffix: "",
|
||||||
labelValue: "infisical"
|
shouldLabel: false,
|
||||||
}
|
labelName: "managed-by",
|
||||||
|
labelValue: "infisical"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const shouldLabel = watch("shouldLabel");
|
const shouldLabel = watch("shouldLabel");
|
||||||
@@ -92,8 +100,8 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue("labelName", undefined);
|
setValue("labelName", "");
|
||||||
setValue("labelValue", undefined);
|
setValue("labelValue", "");
|
||||||
}, [shouldLabel]);
|
}, [shouldLabel]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -135,8 +143,8 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
sourceEnvironment: sce,
|
sourceEnvironment: sce,
|
||||||
secretPath,
|
secretPath,
|
||||||
metadata: {
|
metadata: {
|
||||||
secretPrefix,
|
...(secretPrefix ? { secretPrefix } : {}),
|
||||||
secretSuffix,
|
...(secretSuffix ? { secretSuffix } : {}),
|
||||||
...(sl ? {
|
...(sl ? {
|
||||||
secretGCPLabel: {
|
secretGCPLabel: {
|
||||||
labelName,
|
labelName,
|
||||||
@@ -159,7 +167,14 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps ? (
|
integrationAuthApps ? (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit((data: FormData) => {
|
||||||
|
if (!data.secretPrefix && !data.secretSuffix && !data.shouldLabel) {
|
||||||
|
handlePopUpOpen("confirmIntegration", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFormSubmit(data);
|
||||||
|
})}
|
||||||
className="flex flex-col h-full w-full items-center justify-center"
|
className="flex flex-col h-full w-full items-center justify-center"
|
||||||
>
|
>
|
||||||
<Card className="max-w-lg rounded-md border border-mineshaft-600">
|
<Card className="max-w-lg rounded-md border border-mineshaft-600">
|
||||||
@@ -169,7 +184,7 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
</Head>
|
</Head>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
className="text-left px-6 text-xl mb-2"
|
className="text-left px-6 text-xl mb-2"
|
||||||
subTitle="Select which environment or folder in Infisical you want to sync to GCP Secret Manager. Optionally, you can add suffixes/prefixes."
|
subTitle="Select which environment or folder in Infisical you want to sync to GCP Secret Manager."
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<div className="inline flex items-center pb-0.5">
|
<div className="inline flex items-center pb-0.5">
|
||||||
@@ -207,89 +222,87 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
<div>
|
<Controller
|
||||||
<Controller
|
control={control}
|
||||||
|
name="selectedSourceEnvironment"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Project Environment"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
|
<SelectItem
|
||||||
|
value={sourceEnvironment.slug}
|
||||||
|
key={`source-environment-${sourceEnvironment.slug}`}
|
||||||
|
>
|
||||||
|
{sourceEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="selectedSourceEnvironment"
|
defaultValue=""
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
name="secretPath"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secrets Path"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="/"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="targetAppId"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Project Environment"
|
label="GCP Project"
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
{...field}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => {
|
||||||
|
if (e === "") return;
|
||||||
|
onChange(e)
|
||||||
|
}}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
{integrationAuthApps.length > 0 ? (
|
||||||
<SelectItem
|
integrationAuthApps.map((integrationAuthApp) => (
|
||||||
value={sourceEnvironment.slug}
|
<SelectItem
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
value={String(integrationAuthApp.appId as string)}
|
||||||
>
|
key={`target-app-${String(integrationAuthApp.appId)}`}
|
||||||
{sourceEnvironment.name}
|
>
|
||||||
|
{integrationAuthApp.name}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem value="none" key="target-app-none">
|
||||||
|
No projects found
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)
|
||||||
/>
|
}}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="secretPath"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Secrets Path"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
placeholder="/"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="targetAppId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
label="GCP Project"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => {
|
|
||||||
if (e === "") return;
|
|
||||||
onChange(e)
|
|
||||||
}}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{integrationAuthApps.length > 0 ? (
|
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
|
||||||
<SelectItem
|
|
||||||
value={String(integrationAuthApp.appId as string)}
|
|
||||||
key={`target-app-${String(integrationAuthApp.appId)}`}
|
|
||||||
>
|
|
||||||
{integrationAuthApp.name}
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<SelectItem value="none" key="target-app-none">
|
|
||||||
No projects found
|
|
||||||
</SelectItem>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={TabSections.Options}>
|
<TabPanel value={TabSections.Options}>
|
||||||
@@ -398,11 +411,47 @@ export default function GCPSecretManagerCreateIntegrationPage() {
|
|||||||
Create Integration
|
Create Integration
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
|
{/* <div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
|
||||||
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
|
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
|
||||||
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
|
<div className="flex flex-row items-center">
|
||||||
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GCP Secret Manager with secrets from Infisical.</span>
|
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/>
|
||||||
</div>
|
<span className="ml-3 text-md text-mineshaft-100">Pro Tip</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-mineshaft-300 text-sm mt-4">
|
||||||
|
After creating an integration, your secrets will start syncing immediately.
|
||||||
|
|
||||||
|
To avoid overwriting existing secrets in GCP Secret Manager, you may consider adding a secret prefix/suffix and/or enabling labeling in the options tab.
|
||||||
|
</span>
|
||||||
|
</div> */}
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp.confirmIntegration?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("confirmIntegration", isOpen)}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
title="Heads Up"
|
||||||
|
footerContent={
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button onClick={() => onFormSubmit(popUp.confirmIntegration?.data as FormData)}>
|
||||||
|
Continue Anyway
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => handlePopUpClose("confirmIntegration")}
|
||||||
|
variant="outline_bg"
|
||||||
|
colorSchema="secondary"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
You're about to overwrite any existing secrets in GCP Secret Manager.
|
||||||
|
</p>
|
||||||
|
<p className="mt-4">
|
||||||
|
To avoid this behavior, you may consider adding a secret prefix/suffix or enabling labeling in the options tab.
|
||||||
|
</p>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
<div className="flex justify-center items-center w-full h-full">
|
||||||
|
|||||||
@@ -1,37 +1,55 @@
|
|||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
import { useGetCloudIntegrations } from "@app/hooks/api";
|
import { useGetCloudIntegrations } from "@app/hooks/api";
|
||||||
|
|
||||||
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
|
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
gitLabURL: yup.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
export default function GitLabAuthorizeIntegrationPage() {
|
export default function GitLabAuthorizeIntegrationPage() {
|
||||||
const { data: cloudIntegrations } = useGetCloudIntegrations();
|
const {
|
||||||
|
control,
|
||||||
const [gitLabURL, setGitLabURL] = useState("");
|
handleSubmit
|
||||||
|
} = useForm<FormData>({
|
||||||
const handleIntegrateWithOAuth = () => {
|
resolver: yupResolver(schema),
|
||||||
if (!cloudIntegrations) return;
|
defaultValues: {
|
||||||
const integrationOption = cloudIntegrations.find((integration) => integration.slug === "gitlab");
|
gitLabURL: ""
|
||||||
|
|
||||||
if (!integrationOption) return;
|
|
||||||
|
|
||||||
const baseURL = gitLabURL.trim() === "" ? "https://gitlab.com" : gitLabURL.trim();
|
|
||||||
|
|
||||||
const csrfToken = crypto.randomBytes(16).toString("hex");
|
|
||||||
localStorage.setItem("latestCSRFToken", csrfToken);
|
|
||||||
|
|
||||||
const state = `${csrfToken}|${gitLabURL.trim() === "" ? "" : gitLabURL.trim()}`;
|
|
||||||
const link = `${baseURL}/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
|
|
||||||
|
|
||||||
window.location.assign(link);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: cloudIntegrations } = useGetCloudIntegrations();
|
||||||
|
|
||||||
|
const onFormSubmit = ({
|
||||||
|
gitLabURL
|
||||||
|
}: FormData) => {
|
||||||
|
if (!cloudIntegrations) return;
|
||||||
|
const integrationOption = cloudIntegrations.find((integration) => integration.slug === "gitlab");
|
||||||
|
|
||||||
|
if (!integrationOption) return;
|
||||||
|
|
||||||
|
const baseURL = gitLabURL.trim() === "" ? "https://gitlab.com" : gitLabURL.trim();
|
||||||
|
|
||||||
|
const csrfToken = crypto.randomBytes(16).toString("hex");
|
||||||
|
localStorage.setItem("latestCSRFToken", csrfToken);
|
||||||
|
|
||||||
|
const state = `${csrfToken}|${gitLabURL.trim() === "" ? "" : gitLabURL.trim()}`;
|
||||||
|
const link = `${baseURL}/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
|
||||||
|
|
||||||
|
window.location.assign(link);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
@@ -42,7 +60,7 @@ export default function GitLabAuthorizeIntegrationPage() {
|
|||||||
<Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12">
|
<Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12">
|
||||||
<CardTitle
|
<CardTitle
|
||||||
className="text-left px-6 text-xl"
|
className="text-left px-6 text-xl"
|
||||||
subTitle="Authorize this integration to be able to sync secrets from Infisical to GitLab. If needed, specify the self-hosted GitLab URL."
|
subTitle="Authorize this integration to sync secrets from Infisical to GitLab. If no self-hosted GitLab URL is specified, then Infisical will connect you to GitLab Cloud."
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<div className="inline flex items-center pb-0.5">
|
<div className="inline flex items-center pb-0.5">
|
||||||
@@ -65,20 +83,36 @@ export default function GitLabAuthorizeIntegrationPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<FormControl label="Self-hosted URL (optional)" className="px-6">
|
<form
|
||||||
<Input
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
placeholder="https://self-hosted-gitlab.com"
|
className="px-6 text-right pb-8"
|
||||||
value={gitLabURL} onChange={(e) => setGitLabURL(e.target.value)}
|
>
|
||||||
/>
|
<Controller
|
||||||
</FormControl>
|
control={control}
|
||||||
<Button
|
name="gitLabURL"
|
||||||
onClick={handleIntegrateWithOAuth}
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Self-hosted URL (optional)"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="https://self-hosted-gitlab.com"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="mb-6 mt-2 ml-auto mr-6 w-min"
|
className="mt-2 w-min"
|
||||||
>
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
Continue with OAuth
|
Continue with OAuth
|
||||||
</Button>
|
</Button>
|
||||||
|
</form>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import Head from "next/head";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen, faBugs, faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare, faBookOpen, faBugs } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { usePopUp } from "@app/hooks";
|
||||||
import {
|
import {
|
||||||
useCreateIntegration
|
useCreateIntegration
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
@@ -21,13 +22,14 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Tabs
|
Tabs} from "../../../components/v2";
|
||||||
} from "../../../components/v2";
|
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById,
|
||||||
@@ -60,6 +62,9 @@ type FormData = yup.InferType<typeof schema>;
|
|||||||
|
|
||||||
export default function GitLabCreateIntegrationPage() {
|
export default function GitLabCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||||
|
"confirmIntegration"
|
||||||
|
] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
@@ -70,7 +75,9 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
resolver: yupResolver(schema),
|
resolver: yupResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
targetEntity: "individual",
|
targetEntity: "individual",
|
||||||
secretPath: "/"
|
secretPath: "/",
|
||||||
|
secretPrefix: "",
|
||||||
|
secretSuffix: ""
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
|
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
|
||||||
@@ -167,7 +174,14 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
integrationAuthApps &&
|
integrationAuthApps &&
|
||||||
integrationAuthTeams ? (
|
integrationAuthTeams ? (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit((data: FormData) => {
|
||||||
|
if (!data.secretPrefix && !data.secretSuffix) {
|
||||||
|
handlePopUpOpen("confirmIntegration", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFormSubmit(data);
|
||||||
|
})}
|
||||||
className="flex flex-col h-full w-full items-center justify-center"
|
className="flex flex-col h-full w-full items-center justify-center"
|
||||||
>
|
>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -431,11 +445,40 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
Create Integration
|
Create Integration
|
||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
|
{/* <div className="border-t border-mineshaft-800 w-full max-w-md mt-6"/>
|
||||||
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
|
<div className="flex flex-col bg-mineshaft-800 border border-mineshaft-600 w-full p-4 max-w-lg mt-6 rounded-md">
|
||||||
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
|
<div className="flex flex-row items-center"><FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-200 text-xl"/> <span className="ml-3 text-md text-mineshaft-100">Pro Tips</span></div>
|
||||||
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GitLab with secrets from Infisical.</span>
|
<span className="text-mineshaft-300 text-sm mt-4">After creating an integration, your secrets will start syncing immediately. This might cause an unexpected override of current secrets in GitLab with secrets from Infisical.</span>
|
||||||
</div>
|
</div> */}
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp.confirmIntegration?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("confirmIntegration", isOpen)}
|
||||||
|
>
|
||||||
|
<ModalContent
|
||||||
|
title="Heads Up"
|
||||||
|
footerContent={
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button onClick={() => onFormSubmit(popUp.confirmIntegration?.data as FormData)}>
|
||||||
|
Continue Anyway
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => handlePopUpClose("confirmIntegration")}
|
||||||
|
variant="outline_bg"
|
||||||
|
colorSchema="secondary"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
You're about to overwrite any existing secrets in GitLab.
|
||||||
|
</p>
|
||||||
|
<p className="mt-4">
|
||||||
|
To avoid this behavior, you may consider adding a secret prefix/suffix in the options tab.
|
||||||
|
</p>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
<div className="flex justify-center items-center w-full h-full">
|
||||||
|
|||||||
Reference in New Issue
Block a user