Compare commits

...

2 Commits

31 changed files with 893 additions and 149 deletions

View File

@ -1,4 +1,4 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
@ -66,6 +66,10 @@ export const integrationServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: sourceEnvironment, secretPath })
);
const folder = await folderDAL.findBySecretPath(integrationAuth.projectId, sourceEnvironment, secretPath); const folder = await folderDAL.findBySecretPath(integrationAuth.projectId, sourceEnvironment, secretPath);
if (!folder) throw new BadRequestError({ message: "Folder path not found" }); if (!folder) throw new BadRequestError({ message: "Folder path not found" });
@ -123,6 +127,11 @@ export const integrationServiceFactory = ({
); );
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
);
const folder = await folderDAL.findBySecretPath(integration.projectId, environment, secretPath); const folder = await folderDAL.findBySecretPath(integration.projectId, environment, secretPath);
if (!folder) throw new BadRequestError({ message: "Folder path not found" }); if (!folder) throw new BadRequestError({ message: "Folder path not found" });

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -13,6 +14,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries"; import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
@ -94,12 +97,35 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [tagValue, setTagValue] = useState(""); const [tagValue, setTagValue] = useState("");
const [kmsKeyId, setKmsKeyId] = useState(""); const [kmsKeyId, setKmsKeyId] = useState("");
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
}
setSelectedSourceEnvironment(availableEnvironments[0].slug);
setSelectedAWSRegion(awsRegions[0].slug); setSelectedAWSRegion(awsRegions[0].slug);
} }
}, [workspace]); }, [workspace, availableEnvironments]);
const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } =
useGetIntegrationAuthAwsKmsKeys({ useGetIntegrationAuthAwsKmsKeys({
@ -218,7 +244,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`} key={`flyio-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -13,6 +14,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries"; import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types"; import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
@ -104,6 +107,18 @@ export default function AWSSecretManagerCreateIntegrationPage() {
const [tagKey, setTagKey] = useState(""); const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState(""); const [tagValue, setTagValue] = useState("");
const [kmsKeyId, setKmsKeyId] = useState(""); const [kmsKeyId, setKmsKeyId] = useState("");
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
// const [path, setPath] = useState(''); // const [path, setPath] = useState('');
// const [pathErrorText, setPathErrorText] = useState(''); // const [pathErrorText, setPathErrorText] = useState('');
@ -118,11 +133,20 @@ export default function AWSSecretManagerCreateIntegrationPage() {
}); });
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
}
setSelectedSourceEnvironment(availableEnvironments[0].slug);
setSelectedAWSRegion(awsRegions[0].slug); setSelectedAWSRegion(awsRegions[0].slug);
} }
}, [workspace]); }, [workspace, availableEnvironments]);
// const isValidAWSPath = (path: string) => { // const isValidAWSPath = (path: string) => {
// const pattern = /^\/[\w./]+\/$/; // const pattern = /^\/[\w./]+\/$/;
@ -238,7 +262,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`flyio-environment-${sourceEnvironment.slug}`} key={`flyio-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -32,12 +35,33 @@ export default function AzureKeyVaultCreateIntegrationPage() {
const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState(""); const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
const handleButtonClick = async () => { const handleButtonClick = async () => {
try { try {
@ -79,7 +103,7 @@ export default function AzureKeyVaultCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`azure-key-vault-environment-${sourceEnvironment.slug}`} key={`azure-key-vault-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -42,11 +45,33 @@ export default function BitBucketCreateIntegrationPage() {
workspaceSlug: targetEnvironmentId workspaceSlug: targetEnvironmentId
}); });
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -116,7 +141,7 @@ export default function BitBucketCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,13 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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 { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { import {
Button, Button,
Card, Card,
@ -21,6 +23,7 @@ import {
TabPanel, TabPanel,
Tabs Tabs
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -62,11 +65,33 @@ export default function ChecklyCreateIntegrationPage() {
accountId: targetAppId accountId: targetAppId
}); });
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -176,7 +201,7 @@ export default function ChecklyCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -12,6 +13,8 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -50,12 +53,33 @@ export default function CircleCICreateIntegrationPage() {
const [targetApp, setTargetApp] = useState(""); const [targetApp, setTargetApp] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -137,7 +161,7 @@ export default function CircleCICreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -36,11 +39,33 @@ export default function Cloud66CreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -91,7 +116,7 @@ export default function Cloud66CreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,10 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import axios from "axios"; import axios from "axios";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration, useGetWorkspaceById } from "@app/hooks/api"; import { useCreateIntegration, useGetWorkspaceById } from "@app/hooks/api";
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2"; import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2";
@ -37,11 +39,33 @@ export default function CloudflarePagesIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -112,7 +136,7 @@ export default function CloudflarePagesIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,10 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import axios from "axios"; import axios from "axios";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration, useGetWorkspaceById } from "@app/hooks/api"; import { useCreateIntegration, useGetWorkspaceById } from "@app/hooks/api";
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2"; import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2";
@ -32,11 +34,33 @@ export default function CloudflareWorkersIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -103,7 +127,7 @@ export default function CloudflareWorkersIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -36,11 +39,33 @@ export default function CodefreshCreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -91,7 +116,7 @@ export default function CodefreshCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -36,11 +39,33 @@ export default function DigitalOceanAppPlatformCreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -91,7 +116,7 @@ export default function DigitalOceanAppPlatformCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -12,6 +13,8 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -52,11 +55,33 @@ export default function FlyioCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
// TODO: handle case where apps can be empty // TODO: handle case where apps can be empty
@ -136,7 +161,7 @@ export default function FlyioCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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";
@ -11,7 +12,9 @@ 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 { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
@ -100,11 +103,33 @@ export default function GCPSecretManagerCreateIntegrationPage() {
setValue("labelValue", ""); setValue("labelValue", "");
}, [shouldLabel]); }, [shouldLabel]);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setValue("selectedSourceEnvironment", workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setValue("selectedSourceEnvironment", availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -237,7 +262,7 @@ export default function GCPSecretManagerCreateIntegrationPage() {
onValueChange={(e) => onChange(e)} onValueChange={(e) => onChange(e)}
className="w-full" className="w-full"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faAngleDown, faAngleDown,
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
@ -38,6 +39,7 @@ import {
TabPanel, TabPanel,
Tabs Tabs
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { import {
useCreateIntegration, useCreateIntegration,
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
@ -99,7 +101,6 @@ export default function GitHubCreateIntegrationPage() {
const router = useRouter(); const router = useRouter();
const { mutateAsync } = useCreateIntegration(); const { mutateAsync } = useCreateIntegration();
const integrationAuthId = const integrationAuthId =
(queryString.parse(router.asPath.split("?")[1]).integrationAuthId as string) ?? ""; (queryString.parse(router.asPath.split("?")[1]).integrationAuthId as string) ?? "";
@ -138,11 +139,33 @@ export default function GitHubCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setValue("selectedSourceEnvironment", workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setValue("selectedSourceEnvironment", availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthGithubEnvs && integrationAuthGithubEnvs?.length > 0) { if (integrationAuthGithubEnvs && integrationAuthGithubEnvs?.length > 0) {
@ -303,7 +326,7 @@ export default function GitHubCreateIntegrationPage() {
onValueChange={onChange} onValueChange={onChange}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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";
@ -11,7 +12,9 @@ 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 { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
@ -100,11 +103,33 @@ export default function GitLabCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setValue("selectedSourceEnvironment", workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setValue("selectedSourceEnvironment", availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -247,7 +272,7 @@ export default function GitLabCreateIntegrationPage() {
onValueChange={(e) => onChange(e)} onValueChange={(e) => onChange(e)}
className="w-full" className="w-full"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,9 @@
import { useState } from "react"; import { useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -12,6 +13,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import queryString from "query-string"; import queryString from "query-string";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -47,6 +49,19 @@ export default function HashiCorpVaultCreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
const isValidVaultPath = (vaultPath: string) => { const isValidVaultPath = (vaultPath: string) => {
return !(vaultPath.length === 0 || vaultPath.startsWith("/") || vaultPath.endsWith("/")); return !(vaultPath.length === 0 || vaultPath.startsWith("/") || vaultPath.endsWith("/"));
}; };
@ -129,7 +144,7 @@ export default function HashiCorpVaultCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`vault-environment-${sourceEnvironment.slug}`} key={`vault-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,10 @@
import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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";
@ -11,6 +13,7 @@ import * as yup from "yup";
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "@app/components/v2"; import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "@app/components/v2";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
useGetIntegrationAuthApps, useGetIntegrationAuthApps,
@ -72,6 +75,19 @@ export default function HasuraCloudCreateIntegrationPage() {
} }
}; };
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
return integrationAuth && workspace && integrationAuthApps ? ( return integrationAuth && workspace && integrationAuthApps ? (
<div className="flex h-full w-full flex-col items-center justify-center"> <div className="flex h-full w-full flex-col items-center justify-center">
<Head> <Head>
@ -125,7 +141,7 @@ export default function HasuraCloudCreateIntegrationPage() {
field.onChange(val); field.onChange(val);
}} }}
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -17,7 +18,9 @@ import queryString from "query-string";
// import { App, Pipeline } from "@app/hooks/api/integrationAuth/types"; // import { App, Pipeline } from "@app/hooks/api/integrationAuth/types";
import * as yup from "yup"; import * as yup from "yup";
import { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
// import { RadioGroup } from "@app/components/v2/RadioGroup"; // import { RadioGroup } from "@app/components/v2/RadioGroup";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types"; import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types";
@ -92,11 +95,33 @@ export default function HerokuCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setValue("selectedSourceEnvironment", workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setValue("selectedSourceEnvironment", availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
// useEffect(() => { // useEffect(() => {
// if (integrationAuthPipelineCouplings) { // if (integrationAuthPipelineCouplings) {
@ -255,7 +280,7 @@ export default function HerokuCreateIntegrationPage() {
onValueChange={(e) => onChange(e)} onValueChange={(e) => onChange(e)}
className="w-full" className="w-full"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -36,11 +39,33 @@ export default function LaravelForgeCreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -92,7 +117,7 @@ export default function LaravelForgeCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -44,12 +47,34 @@ export default function NetlifyCreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
}
setSelectedSourceEnvironment(availableEnvironments[0].slug);
setTargetEnvironment(netlifyEnvironments[0].slug); setTargetEnvironment(netlifyEnvironments[0].slug);
} }
}, [workspace]); }, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -101,7 +126,7 @@ export default function NetlifyCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -43,11 +46,33 @@ export default function NorthflankCreateIntegrationPage() {
appId: targetAppId appId: targetAppId
}); });
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -113,7 +138,7 @@ export default function NorthflankCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,13 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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 { motion } from "framer-motion"; import { motion } from "framer-motion";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { import {
Button, Button,
Card, Card,
@ -21,6 +23,7 @@ import {
TabPanel, TabPanel,
Tabs Tabs
} from "@app/components/v2"; } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
useGetIntegrationAuthQoveryEnvironments, useGetIntegrationAuthQoveryEnvironments,
@ -94,11 +97,33 @@ export default function QoveryCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -239,7 +264,7 @@ export default function QoveryCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -48,11 +51,33 @@ export default function RailwayCreateIntegrationPage() {
appId: targetAppId appId: targetAppId
}); });
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -133,7 +158,7 @@ export default function RailwayCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form"; 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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -15,7 +16,9 @@ import { yupResolver } from "@hookform/resolvers/yup";
import queryString from "query-string"; import queryString from "query-string";
import * as yup from "yup"; import * as yup from "yup";
import { createNotification } from "@app/components/notifications";
import { SecretPathInput } from "@app/components/v2/SecretPathInput"; import { SecretPathInput } from "@app/components/v2/SecretPathInput";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -69,11 +72,33 @@ export default function RenderCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setValue("selectedSourceEnvironment", workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setValue("selectedSourceEnvironment", availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -167,7 +192,7 @@ export default function RenderCreateIntegrationPage() {
onValueChange={(e) => onChange(e)} onValueChange={(e) => onChange(e)}
className="w-full" className="w-full"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -37,11 +40,33 @@ export default function SupabaseCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -92,7 +117,7 @@ export default function SupabaseCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,12 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { faArrowUpRightFromSquare, faBookOpen, faBugs } 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 queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -51,11 +54,33 @@ export default function TeamCityCreateIntegrationPage() {
appId: targetAppId appId: targetAppId
}); });
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -149,7 +174,7 @@ export default function TeamCityCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,12 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
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 queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types"; import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types";
@ -30,8 +33,14 @@ const initialSyncBehaviors = [
label: "No Import - Overwrite all values in Terraform Cloud", label: "No Import - Overwrite all values in Terraform Cloud",
value: IntegrationSyncBehavior.OVERWRITE_TARGET value: IntegrationSyncBehavior.OVERWRITE_TARGET
}, },
{ label: "Import non-sensitive - Prefer values from Terraform Cloud", value: IntegrationSyncBehavior.PREFER_TARGET }, {
{ label: "Import non-sensitive - Prefer values from Infisical", value: IntegrationSyncBehavior.PREFER_SOURCE } label: "Import non-sensitive - Prefer values from Terraform Cloud",
value: IntegrationSyncBehavior.PREFER_TARGET
},
{
label: "Import non-sensitive - Prefer values from Infisical",
value: IntegrationSyncBehavior.PREFER_SOURCE
}
]; ];
const variableTypes = [{ name: "env" }, { name: "terraform" }]; const variableTypes = [{ name: "env" }, { name: "terraform" }];
@ -54,14 +63,36 @@ export default function TerraformCloudCreateIntegrationPage() {
const [variableType, setVariableType] = useState(""); const [variableType, setVariableType] = useState("");
const [variableTypeErrorText, setVariableTypeErrorText] = useState(""); const [variableTypeErrorText, setVariableTypeErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [initialSyncBehavior, setInitialSyncBehavior] = useState("prefer-source") const [initialSyncBehavior, setInitialSyncBehavior] = useState("prefer-source");
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
}
setSelectedSourceEnvironment(availableEnvironments[0].slug);
setVariableType(variableTypes[0].name); setVariableType(variableTypes[0].name);
} }
}, [workspace]); }, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -153,7 +184,7 @@ export default function TerraformCloudCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}
@ -212,7 +243,11 @@ export default function TerraformCloudCreateIntegrationPage() {
</Select> </Select>
</FormControl> </FormControl>
<FormControl label="Initial Sync Behavior" className="px-6"> <FormControl label="Initial Sync Behavior" className="px-6">
<Select value={initialSyncBehavior} onValueChange={(e) => setInitialSyncBehavior(e)} className="w-full border border-mineshaft-600"> <Select
value={initialSyncBehavior}
onValueChange={(e) => setInitialSyncBehavior(e)}
className="w-full border border-mineshaft-600"
>
{initialSyncBehaviors.map((b) => { {initialSyncBehaviors.map((b) => {
return ( return (
<SelectItem value={b.value} key={`sync-behavior-${b.value}`}> <SelectItem value={b.value} key={`sync-behavior-${b.value}`}>

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -36,11 +39,33 @@ export default function TravisCICreateIntegrationPage() {
const [secretPath, setSecretPath] = useState("/"); const [secretPath, setSecretPath] = useState("/");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -91,7 +116,7 @@ export default function TravisCICreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
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 { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import { import {
faArrowUpRightFromSquare, faArrowUpRightFromSquare,
faBookOpen, faBookOpen,
@ -12,6 +13,8 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -63,11 +66,33 @@ export default function VercelCreateIntegrationPage() {
const filteredBranches = branches?.filter((branchName) => branchName !== "main").concat(); const filteredBranches = branches?.filter((branchName) => branchName !== "main").concat();
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -160,7 +185,7 @@ export default function VercelCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`azure-key-vault-environment-${sourceEnvironment.slug}`} key={`azure-key-vault-environment-${sourceEnvironment.slug}`}

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { subject } from "@casl/ability";
import queryString from "query-string"; import queryString from "query-string";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api"; import { useCreateIntegration } from "@app/hooks/api";
import { import {
@ -37,11 +40,33 @@ export default function WindmillCreateIntegrationPage() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { permission } = useProjectPermission();
const availableEnvironments = useMemo(
() =>
workspace?.environments.filter((env) =>
permission.can(
ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment: env.slug, secretPath: "/" })
)
),
[workspace]
);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace && availableEnvironments) {
setSelectedSourceEnvironment(workspace.environments[0].slug); if (!availableEnvironments.length) {
createNotification({
title: "Insufficient Access",
text: "You do not have read access to any environment",
type: "error"
});
return;
} }
}, [workspace]); setSelectedSourceEnvironment(availableEnvironments[0].slug);
}
}, [workspace, availableEnvironments]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
@ -92,7 +117,7 @@ export default function WindmillCreateIntegrationPage() {
onValueChange={(val) => setSelectedSourceEnvironment(val)} onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
{workspace?.environments.map((sourceEnvironment) => ( {availableEnvironments?.map((sourceEnvironment) => (
<SelectItem <SelectItem
value={sourceEnvironment.slug} value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`} key={`source-environment-${sourceEnvironment.slug}`}