mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-10 07:58:15 -05:00
Merge remote-tracking branch 'origin/main' into feat/add-option-to-mask-and-protect-gitlab-secrets
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
ARG POSTHOG_HOST=https://app.posthog.com
|
||||
ARG POSTHOG_API_KEY=posthog-api-key
|
||||
ARG INTERCOM_ID=intercom-id
|
||||
ARG SAML_ORG_SLUG=saml-org-slug-default
|
||||
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
@@ -35,9 +34,7 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
||||
ARG INTERCOM_ID
|
||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
||||
ARG INFISICAL_PLATFORM_VERSION
|
||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||
ARG SAML_ORG_SLUG
|
||||
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||
|
||||
# Build
|
||||
RUN npm run build
|
||||
@@ -113,9 +110,6 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
||||
ARG INTERCOM_ID=intercom-id
|
||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
||||
ARG SAML_ORG_SLUG
|
||||
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
|
||||
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
@@ -676,7 +676,8 @@ export const INTEGRATION = {
|
||||
kmsKeyId: "The ID of the encryption key from AWS KMS.",
|
||||
shouldDisableDelete: "The flag to disable deletion of secrets in AWS Parameter Store.",
|
||||
shouldMaskSecrets: "The flag to determine the visibility of secrets to sync. Used by Gitlab",
|
||||
shouldProtectSecrets: "The flag to determine usage rules of secrets to sync. Used by Gitlab"
|
||||
shouldProtectSecrets: "The flag to determine usage rules of secrets to sync. Used by Gitlab",
|
||||
shouldEnableDelete: "The flag to enable deletion of secrets"
|
||||
}
|
||||
},
|
||||
UPDATE: {
|
||||
|
||||
@@ -75,6 +75,7 @@ const envSchema = z
|
||||
.optional()
|
||||
.default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL)
|
||||
), // fallback since URL_GITLAB_LOGIN has been renamed
|
||||
DEFAULT_SAML_ORG_SLUG: zpStr(z.string().optional()).default(process.env.NEXT_PUBLIC_SAML_ORG_SLUG),
|
||||
// integration client secrets
|
||||
// heroku
|
||||
CLIENT_ID_HEROKU: zpStr(z.string().optional()),
|
||||
@@ -131,7 +132,8 @@ const envSchema = z
|
||||
isSecretScanningConfigured:
|
||||
Boolean(data.SECRET_SCANNING_GIT_APP_ID) &&
|
||||
Boolean(data.SECRET_SCANNING_PRIVATE_KEY) &&
|
||||
Boolean(data.SECRET_SCANNING_WEBHOOK_SECRET)
|
||||
Boolean(data.SECRET_SCANNING_WEBHOOK_SECRET),
|
||||
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG
|
||||
}));
|
||||
|
||||
let envCfg: Readonly<z.infer<typeof envSchema>>;
|
||||
|
||||
@@ -919,7 +919,8 @@ export const registerRoutes = async (
|
||||
emailConfigured: z.boolean().optional(),
|
||||
inviteOnlySignup: z.boolean().optional(),
|
||||
redisConfigured: z.boolean().optional(),
|
||||
secretScanningConfigured: z.boolean().optional()
|
||||
secretScanningConfigured: z.boolean().optional(),
|
||||
samlDefaultOrgSlug: z.string().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -932,7 +933,8 @@ export const registerRoutes = async (
|
||||
emailConfigured: cfg.isSmtpConfigured,
|
||||
inviteOnlySignup: Boolean(serverCfg.allowSignUp),
|
||||
redisConfigured: cfg.isRedisConfigured,
|
||||
secretScanningConfigured: cfg.isSecretScanningConfigured
|
||||
secretScanningConfigured: cfg.isSecretScanningConfigured,
|
||||
samlDefaultOrgSlug: cfg.samlDefaultOrgSlug
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
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";
|
||||
|
||||
export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
@@ -46,38 +46,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
path: z.string().trim().optional().describe(INTEGRATION.CREATE.path),
|
||||
region: z.string().trim().optional().describe(INTEGRATION.CREATE.region),
|
||||
scope: z.string().trim().optional().describe(INTEGRATION.CREATE.scope),
|
||||
metadata: 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),
|
||||
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
||||
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets)
|
||||
})
|
||||
.default({})
|
||||
metadata: IntegrationMetadataSchema.default({})
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -163,33 +132,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
||||
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
||||
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
||||
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment),
|
||||
metadata: 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.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()
|
||||
metadata: IntegrationMetadataSchema.optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
|
||||
@@ -31,6 +31,7 @@ import { logger } from "@app/lib/logger";
|
||||
import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/secret/secret-types";
|
||||
|
||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
||||
import {
|
||||
IntegrationInitialSyncBehavior,
|
||||
IntegrationMappingBehavior,
|
||||
@@ -1363,38 +1364,41 @@ const syncSecretsGitHub = async ({
|
||||
}
|
||||
}
|
||||
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
if (
|
||||
!(encryptedSecret.name in secrets) &&
|
||||
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
|
||||
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
|
||||
) {
|
||||
switch (integration.scope) {
|
||||
case GithubScope.Org: {
|
||||
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
|
||||
org: integration.owner as string,
|
||||
secret_name: encryptedSecret.name
|
||||
});
|
||||
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,
|
||||
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
||||
if (metadata.shouldEnableDelete) {
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
if (
|
||||
!(encryptedSecret.name in secrets) &&
|
||||
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
|
||||
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
|
||||
) {
|
||||
switch (integration.scope) {
|
||||
case GithubScope.Org: {
|
||||
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
|
||||
org: integration.owner as string,
|
||||
secret_name: encryptedSecret.name
|
||||
}
|
||||
);
|
||||
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;
|
||||
});
|
||||
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
|
||||
}
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2752,6 +2756,20 @@ const syncSecretsCloudflarePages = async ({
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const metadata = z.record(z.any()).parse(integration.metadata);
|
||||
if (metadata.shouldAutoRedeploy) {
|
||||
await request.post(
|
||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}/deployments`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
37
backend/src/services/integration/integration-schema.ts
Normal file
37
backend/src/services/integration/integration-schema.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
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),
|
||||
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
||||
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets)
|
||||
});
|
||||
@@ -31,6 +31,7 @@ export type TCreateIntegrationDTO = {
|
||||
shouldDisableDelete?: boolean;
|
||||
shouldMaskSecrets?: boolean;
|
||||
shouldProtectSecrets?: boolean;
|
||||
shouldEnableDelete?: boolean;
|
||||
};
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
@@ -56,6 +57,7 @@ export type TUpdateIntegrationDTO = {
|
||||
}[];
|
||||
kmsKeyId?: string;
|
||||
shouldDisableDelete?: boolean;
|
||||
shouldEnableDelete?: boolean;
|
||||
};
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export const kmsServiceFactory = ({ kmsDAL, kmsRootConfigDAL, keyStore }: TKmsSe
|
||||
// This will switch to a seal process and HMS flow in future
|
||||
const encryptionKey = appCfg.ENCRYPTION_KEY || appCfg.ROOT_ENCRYPTION_KEY;
|
||||
// if root key its base64 encoded
|
||||
const isBase64 = Boolean(appCfg.ROOT_ENCRYPTION_KEY);
|
||||
const isBase64 = !appCfg.ENCRYPTION_KEY;
|
||||
if (!encryptionKey) throw new Error("Root encryption key not found for KMS service.");
|
||||
const encryptionKeyBuffer = Buffer.from(encryptionKey, isBase64 ? "base64" : "utf8");
|
||||
|
||||
|
||||
@@ -496,7 +496,6 @@ To enable auto redeployment you simply have to add the following annotation to t
|
||||
```yaml
|
||||
secrets.infisical.com/auto-reload: "true"
|
||||
```
|
||||
|
||||
<Accordion title="Deployment example with auto redeploy enabled">
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
@@ -527,7 +526,11 @@ spec:
|
||||
- containerPort: 80
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Info>
|
||||
#### How it works
|
||||
When a secret change occurs, the operator will check to see which deployments are using the operator-managed Kubernetes secret that received the update.
|
||||
Then, for each deployment that has this annotation present, a rolling update will be triggered.
|
||||
</Info>
|
||||
## Global configuration
|
||||
|
||||
To configure global settings that will apply to all instances of `InfisicalSecret`, you can define these configurations in a Kubernetes ConfigMap.
|
||||
|
||||
@@ -318,6 +318,11 @@ SMTP_FROM_NAME=Infisical
|
||||
By default, users can only login via email/password based login method.
|
||||
To login into Infisical with OAuth providers such as Google, configure the associated variables.
|
||||
|
||||
<ParamField query="DEFAULT_SAML_ORG_SLUG" type="string">
|
||||
|
||||
When set, all visits to the Infisical login page will automatically redirect users of your Infisical instance to the SAML identity provider associated with the specified organization slug.
|
||||
</ParamField>
|
||||
|
||||
<Accordion title="Google">
|
||||
Follow detailed guide to configure [Google SSO](/documentation/platform/sso/google)
|
||||
|
||||
@@ -369,11 +374,6 @@ To login into Infisical with OAuth providers such as Google, configure the assoc
|
||||
information.
|
||||
</Accordion>
|
||||
|
||||
<ParamField query="NEXT_PUBLIC_SAML_ORG_SLUG" type="string">
|
||||
Configure SAML organization slug to automatically redirect all users of your
|
||||
Infisical instance to the identity provider.
|
||||
</ParamField>
|
||||
|
||||
## Native secret integrations
|
||||
|
||||
To help you sync secrets from Infisical to services such as Github and Gitlab, Infisical provides native integrations out of the box.
|
||||
|
||||
@@ -4,8 +4,6 @@ scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_POSTHOG_API_KEY
|
||||
|
||||
scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_INTERCOM_ID" "$NEXT_PUBLIC_INTERCOM_ID"
|
||||
|
||||
scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_SAML_ORG_SLUG" "$NEXT_PUBLIC_SAML_ORG_SLUG"
|
||||
|
||||
if [ "$TELEMETRY_ENABLED" != "false" ]; then
|
||||
echo "Telemetry is enabled"
|
||||
scripts/set-standalone-build-telemetry.sh true
|
||||
|
||||
@@ -75,6 +75,7 @@ export const useCreateIntegration = () => {
|
||||
shouldDisableDelete?: boolean;
|
||||
shouldMaskSecrets?: boolean;
|
||||
shouldProtectSecrets?: boolean;
|
||||
shouldEnableDelete?: boolean;
|
||||
};
|
||||
}) => {
|
||||
const {
|
||||
|
||||
@@ -4,4 +4,5 @@ export type ServerStatus = {
|
||||
emailConfigured: boolean;
|
||||
secretScanningConfigured: boolean;
|
||||
redisConfigured: boolean;
|
||||
samlDefaultOrgSlug: boolean
|
||||
};
|
||||
|
||||
@@ -7,7 +7,15 @@ import { createNotification } from "@app/components/notifications";
|
||||
import { SecretPathInput } from "@app/components/v2/SecretPathInput";
|
||||
import { useCreateIntegration, useGetWorkspaceById } from "@app/hooks/api";
|
||||
|
||||
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardTitle,
|
||||
FormControl,
|
||||
Select,
|
||||
SelectItem,
|
||||
Switch
|
||||
} from "../../../components/v2";
|
||||
import {
|
||||
useGetIntegrationAuthApps,
|
||||
useGetIntegrationAuthById
|
||||
@@ -34,6 +42,7 @@ export default function CloudflarePagesIntegrationPage() {
|
||||
const [targetApp, setTargetApp] = useState("");
|
||||
const [targetAppId, setTargetAppId] = useState("");
|
||||
const [targetEnvironment, setTargetEnvironment] = useState("");
|
||||
const [shouldAutoRedeploy, setShouldAutoRedeploy] = useState(false);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -69,7 +78,10 @@ export default function CloudflarePagesIntegrationPage() {
|
||||
appId: targetAppId,
|
||||
sourceEnvironment: selectedSourceEnvironment,
|
||||
targetEnvironment,
|
||||
secretPath
|
||||
secretPath,
|
||||
metadata: {
|
||||
shouldAutoRedeploy
|
||||
}
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
@@ -169,6 +181,15 @@ export default function CloudflarePagesIntegrationPage() {
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<div className="mb-[2.36rem] ml-1 px-6">
|
||||
<Switch
|
||||
id="redeploy-cloudflare-pages"
|
||||
onCheckedChange={(isChecked: boolean) => setShouldAutoRedeploy(isChecked)}
|
||||
isChecked={shouldAutoRedeploy}
|
||||
>
|
||||
Auto-redeploy service upon secret change
|
||||
</Switch>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleButtonClick}
|
||||
color="mineshaft"
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
Input,
|
||||
Select,
|
||||
SelectItem,
|
||||
Switch,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
@@ -59,7 +60,7 @@ const schema = yup.object({
|
||||
selectedSourceEnvironment: yup.string().trim().required("Project Environment is required"),
|
||||
secretPath: yup.string().trim().required("Secrets Path is required"),
|
||||
secretSuffix: yup.string().trim().optional(),
|
||||
|
||||
shouldEnableDelete: yup.boolean().optional(),
|
||||
scope: yup.mixed<TargetEnv>().oneOf(targetEnv.slice()).required(),
|
||||
|
||||
repoIds: yup.mixed().when("scope", {
|
||||
@@ -98,7 +99,6 @@ type FormData = yup.InferType<typeof schema>;
|
||||
export default function GitHubCreateIntegrationPage() {
|
||||
const router = useRouter();
|
||||
const { mutateAsync } = useCreateIntegration();
|
||||
|
||||
|
||||
const integrationAuthId =
|
||||
(queryString.parse(router.asPath.split("?")[1]).integrationAuthId as string) ?? "";
|
||||
@@ -120,7 +120,8 @@ export default function GitHubCreateIntegrationPage() {
|
||||
defaultValues: {
|
||||
secretPath: "/",
|
||||
scope: "github-repo",
|
||||
repoIds: []
|
||||
repoIds: [],
|
||||
shouldEnableDelete: false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -177,7 +178,8 @@ export default function GitHubCreateIntegrationPage() {
|
||||
app: targetApp.name, // repo name
|
||||
owner: targetApp.owner, // repo owner
|
||||
metadata: {
|
||||
secretSuffix: data.secretSuffix
|
||||
secretSuffix: data.secretSuffix,
|
||||
shouldEnableDelete: data.shouldEnableDelete
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -194,7 +196,8 @@ export default function GitHubCreateIntegrationPage() {
|
||||
scope: data.scope,
|
||||
owner: integrationAuthOrgs?.find((e) => e.orgId === data.orgId)?.name,
|
||||
metadata: {
|
||||
secretSuffix: data.secretSuffix
|
||||
secretSuffix: data.secretSuffix,
|
||||
shouldEnableDelete: data.shouldEnableDelete
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -211,7 +214,8 @@ export default function GitHubCreateIntegrationPage() {
|
||||
owner: repoOwner,
|
||||
targetEnvironmentId: data.envId,
|
||||
metadata: {
|
||||
secretSuffix: data.secretSuffix
|
||||
secretSuffix: data.secretSuffix,
|
||||
shouldEnableDelete: data.shouldEnableDelete
|
||||
}
|
||||
});
|
||||
break;
|
||||
@@ -546,6 +550,21 @@ export default function GitHubCreateIntegrationPage() {
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
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
|
||||
control={control}
|
||||
name="secretSuffix"
|
||||
|
||||
@@ -12,6 +12,7 @@ import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
||||
import attemptLogin from "@app/components/utilities/attemptLogin";
|
||||
import { Button, Input } from "@app/components/v2";
|
||||
import { useServerConfig } from "@app/context";
|
||||
import { useFetchServerStatus } from "@app/hooks/api";
|
||||
|
||||
import { navigateUserToSelectOrg } from "../../Login.utils";
|
||||
|
||||
@@ -31,21 +32,15 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
||||
const [loginError, setLoginError] = useState(false);
|
||||
const { config } = useServerConfig();
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
process.env.NEXT_PUBLIC_SAML_ORG_SLUG &&
|
||||
process.env.NEXT_PUBLIC_SAML_ORG_SLUG !== "saml-org-slug-default"
|
||||
) {
|
||||
const callbackPort = queryParams.get("callback_port");
|
||||
window.open(
|
||||
`/api/v1/sso/redirect/saml2/organizations/${process.env.NEXT_PUBLIC_SAML_ORG_SLUG}${
|
||||
callbackPort ? `?callback_port=${callbackPort}` : ""
|
||||
}`
|
||||
);
|
||||
window.close();
|
||||
}
|
||||
}, []);
|
||||
if (serverDetails?.samlDefaultOrgSlug){
|
||||
const callbackPort = queryParams.get("callback_port");
|
||||
const redirectUrl = `/api/v1/sso/redirect/saml2/organizations/${serverDetails?.samlDefaultOrgSlug}${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||
router.push(redirectUrl);
|
||||
}
|
||||
}, [serverDetails?.samlDefaultOrgSlug]);
|
||||
|
||||
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -454,12 +454,12 @@ export const SecretOverviewPage = () => {
|
||||
const filteredSecretNames = secKeys
|
||||
?.filter((name) => name.toUpperCase().includes(searchFilter.toUpperCase()))
|
||||
.sort((a, b) => (sortDir === "asc" ? a.localeCompare(b) : b.localeCompare(a)));
|
||||
const filteredFolderNames = folderNames?.filter((name) =>
|
||||
name.toLowerCase().includes(searchFilter.toLowerCase())
|
||||
);
|
||||
const filteredDynamicSecrets = dynamicSecretNames?.filter((name) =>
|
||||
name.toLowerCase().includes(searchFilter.toLowerCase())
|
||||
);
|
||||
const filteredFolderNames = folderNames
|
||||
?.filter((name) => name.toLowerCase().includes(searchFilter.toLowerCase()))
|
||||
.sort((a, b) => (sortDir === "asc" ? a.localeCompare(b) : b.localeCompare(a)));
|
||||
const filteredDynamicSecrets = dynamicSecretNames
|
||||
?.filter((name) => name.toLowerCase().includes(searchFilter.toLowerCase()))
|
||||
.sort((a, b) => (sortDir === "asc" ? a.localeCompare(b) : b.localeCompare(a)));
|
||||
|
||||
const isTableEmpty =
|
||||
!(
|
||||
|
||||
@@ -178,7 +178,7 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
errorText={error?.message}
|
||||
>
|
||||
<SecretInput
|
||||
isVisible
|
||||
isVisible={false}
|
||||
{...field}
|
||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[100px]"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user