mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-02 16:55:02 +00:00
Compare commits
4 Commits
add-folder
...
feat/add-o
Author | SHA1 | Date | |
---|---|---|---|
f4a1a00b59 | |||
b9933d711c | |||
847c2c67ec | |||
76a424dcfb |
@ -674,7 +674,8 @@ export const INTEGRATION = {
|
|||||||
secretGCPLabel: "The label for GCP secrets.",
|
secretGCPLabel: "The label for GCP secrets.",
|
||||||
secretAWSTag: "The tags for AWS secrets.",
|
secretAWSTag: "The tags for AWS secrets.",
|
||||||
kmsKeyId: "The ID of the encryption key from AWS KMS.",
|
kmsKeyId: "The ID of the encryption key from AWS KMS.",
|
||||||
shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store."
|
shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store.",
|
||||||
|
shouldEnableDelete: "The flag to enable deletion of secrets"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
|
@ -8,7 +8,7 @@ import { writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { IntegrationMappingBehavior } from "@app/services/integration-auth/integration-list";
|
import { IntegrationMetadataSchema } from "@app/services/integration/integration-schema";
|
||||||
import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||||
@ -46,36 +46,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
||||||
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
||||||
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
||||||
metadata: z
|
metadata: IntegrationMetadataSchema.default({})
|
||||||
.object({
|
|
||||||
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
|
||||||
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
|
||||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
|
||||||
mappingBehavior: z
|
|
||||||
.nativeEnum(IntegrationMappingBehavior)
|
|
||||||
.optional()
|
|
||||||
.describe(INTEGRATION.CREATE.metadata.mappingBehavior),
|
|
||||||
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
|
||||||
secretGCPLabel: z
|
|
||||||
.object({
|
|
||||||
labelName: z.string(),
|
|
||||||
labelValue: z.string()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
|
|
||||||
secretAWSTag: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
key: z.string(),
|
|
||||||
value: z.string()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
|
||||||
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
|
|
||||||
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
|
|
||||||
})
|
|
||||||
.default({})
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -161,33 +132,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
||||||
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
||||||
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment),
|
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment),
|
||||||
metadata: z
|
metadata: IntegrationMetadataSchema.optional()
|
||||||
.object({
|
|
||||||
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
|
||||||
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
|
||||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
|
||||||
mappingBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.mappingBehavior),
|
|
||||||
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
|
||||||
secretGCPLabel: z
|
|
||||||
.object({
|
|
||||||
labelName: z.string(),
|
|
||||||
labelValue: z.string()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
|
|
||||||
secretAWSTag: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
key: z.string(),
|
|
||||||
value: z.string()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
|
||||||
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
|
|
||||||
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -31,6 +31,7 @@ import { logger } from "@app/lib/logger";
|
|||||||
import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/secret/secret-types";
|
import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/secret/secret-types";
|
||||||
|
|
||||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||||
|
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
||||||
import {
|
import {
|
||||||
IntegrationInitialSyncBehavior,
|
IntegrationInitialSyncBehavior,
|
||||||
IntegrationMappingBehavior,
|
IntegrationMappingBehavior,
|
||||||
@ -1363,38 +1364,41 @@ const syncSecretsGitHub = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
||||||
if (
|
if (metadata.shouldEnableDelete) {
|
||||||
!(encryptedSecret.name in secrets) &&
|
for await (const encryptedSecret of encryptedSecrets) {
|
||||||
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
|
if (
|
||||||
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
|
!(encryptedSecret.name in secrets) &&
|
||||||
) {
|
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
|
||||||
switch (integration.scope) {
|
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
|
||||||
case GithubScope.Org: {
|
) {
|
||||||
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
|
switch (integration.scope) {
|
||||||
org: integration.owner as string,
|
case GithubScope.Org: {
|
||||||
secret_name: encryptedSecret.name
|
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
|
||||||
});
|
org: integration.owner as string,
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GithubScope.Env: {
|
|
||||||
await octokit.request(
|
|
||||||
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
|
|
||||||
{
|
|
||||||
repository_id: Number(integration.appId),
|
|
||||||
environment_name: integration.targetEnvironmentId as string,
|
|
||||||
secret_name: encryptedSecret.name
|
secret_name: encryptedSecret.name
|
||||||
}
|
});
|
||||||
);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case GithubScope.Env: {
|
||||||
default: {
|
await octokit.request(
|
||||||
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
|
||||||
owner: integration.owner as string,
|
{
|
||||||
repo: integration.app as string,
|
repository_id: Number(integration.appId),
|
||||||
secret_name: encryptedSecret.name
|
environment_name: integration.targetEnvironmentId as string,
|
||||||
});
|
secret_name: encryptedSecret.name
|
||||||
break;
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||||
|
owner: integration.owner as string,
|
||||||
|
repo: integration.app as string,
|
||||||
|
secret_name: encryptedSecret.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
backend/src/services/integration/integration-schema.ts
Normal file
35
backend/src/services/integration/integration-schema.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { INTEGRATION } from "@app/lib/api-docs";
|
||||||
|
|
||||||
|
import { IntegrationMappingBehavior } from "../integration-auth/integration-list";
|
||||||
|
|
||||||
|
export const IntegrationMetadataSchema = z.object({
|
||||||
|
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
||||||
|
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
||||||
|
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||||
|
mappingBehavior: z
|
||||||
|
.nativeEnum(IntegrationMappingBehavior)
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.mappingBehavior),
|
||||||
|
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
||||||
|
secretGCPLabel: z
|
||||||
|
.object({
|
||||||
|
labelName: z.string(),
|
||||||
|
labelValue: z.string()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
|
||||||
|
secretAWSTag: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
||||||
|
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
|
||||||
|
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete),
|
||||||
|
shouldEnableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldEnableDelete)
|
||||||
|
});
|
@ -29,6 +29,7 @@ export type TCreateIntegrationDTO = {
|
|||||||
}[];
|
}[];
|
||||||
kmsKeyId?: string;
|
kmsKeyId?: string;
|
||||||
shouldDisableDelete?: boolean;
|
shouldDisableDelete?: boolean;
|
||||||
|
shouldEnableDelete?: boolean;
|
||||||
};
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ export type TUpdateIntegrationDTO = {
|
|||||||
}[];
|
}[];
|
||||||
kmsKeyId?: string;
|
kmsKeyId?: string;
|
||||||
shouldDisableDelete?: boolean;
|
shouldDisableDelete?: boolean;
|
||||||
|
shouldEnableDelete?: boolean;
|
||||||
};
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ export const useCreateIntegration = () => {
|
|||||||
}[];
|
}[];
|
||||||
kmsKeyId?: string;
|
kmsKeyId?: string;
|
||||||
shouldDisableDelete?: boolean;
|
shouldDisableDelete?: boolean;
|
||||||
|
shouldEnableDelete?: boolean;
|
||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
Switch,
|
||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
@ -59,7 +60,7 @@ const schema = yup.object({
|
|||||||
selectedSourceEnvironment: yup.string().trim().required("Project Environment is required"),
|
selectedSourceEnvironment: yup.string().trim().required("Project Environment is required"),
|
||||||
secretPath: yup.string().trim().required("Secrets Path is required"),
|
secretPath: yup.string().trim().required("Secrets Path is required"),
|
||||||
secretSuffix: yup.string().trim().optional(),
|
secretSuffix: yup.string().trim().optional(),
|
||||||
|
shouldEnableDelete: yup.boolean().optional(),
|
||||||
scope: yup.mixed<TargetEnv>().oneOf(targetEnv.slice()).required(),
|
scope: yup.mixed<TargetEnv>().oneOf(targetEnv.slice()).required(),
|
||||||
|
|
||||||
repoIds: yup.mixed().when("scope", {
|
repoIds: yup.mixed().when("scope", {
|
||||||
@ -98,7 +99,6 @@ type FormData = yup.InferType<typeof schema>;
|
|||||||
export default function GitHubCreateIntegrationPage() {
|
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) ?? "";
|
||||||
@ -120,7 +120,8 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
secretPath: "/",
|
secretPath: "/",
|
||||||
scope: "github-repo",
|
scope: "github-repo",
|
||||||
repoIds: []
|
repoIds: [],
|
||||||
|
shouldEnableDelete: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -177,7 +178,8 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
app: targetApp.name, // repo name
|
app: targetApp.name, // repo name
|
||||||
owner: targetApp.owner, // repo owner
|
owner: targetApp.owner, // repo owner
|
||||||
metadata: {
|
metadata: {
|
||||||
secretSuffix: data.secretSuffix
|
secretSuffix: data.secretSuffix,
|
||||||
|
shouldEnableDelete: data.shouldEnableDelete
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -194,7 +196,8 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
scope: data.scope,
|
scope: data.scope,
|
||||||
owner: integrationAuthOrgs?.find((e) => e.orgId === data.orgId)?.name,
|
owner: integrationAuthOrgs?.find((e) => e.orgId === data.orgId)?.name,
|
||||||
metadata: {
|
metadata: {
|
||||||
secretSuffix: data.secretSuffix
|
secretSuffix: data.secretSuffix,
|
||||||
|
shouldEnableDelete: data.shouldEnableDelete
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -211,7 +214,8 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
owner: repoOwner,
|
owner: repoOwner,
|
||||||
targetEnvironmentId: data.envId,
|
targetEnvironmentId: data.envId,
|
||||||
metadata: {
|
metadata: {
|
||||||
secretSuffix: data.secretSuffix
|
secretSuffix: data.secretSuffix,
|
||||||
|
shouldEnableDelete: data.shouldEnableDelete
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -546,6 +550,21 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
|
<div className="ml-1 mb-5">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="shouldEnableDelete"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Switch
|
||||||
|
id="delete-github-option"
|
||||||
|
onCheckedChange={(isChecked) => onChange(isChecked)}
|
||||||
|
isChecked={value}
|
||||||
|
>
|
||||||
|
Delete secrets in Github that are not in Infisical
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="secretSuffix"
|
name="secretSuffix"
|
||||||
|
Reference in New Issue
Block a user