Compare commits

..

2 Commits

Author SHA1 Message Date
a61e92c49c Update go.mdx 2024-06-11 04:26:35 +02:00
fa05639592 Docs: Go SDK 2024-06-10 05:18:39 +02:00
36 changed files with 577 additions and 351 deletions

View File

@ -63,7 +63,3 @@ CLIENT_SECRET_GITHUB_LOGIN=
CLIENT_ID_GITLAB_LOGIN= CLIENT_ID_GITLAB_LOGIN=
CLIENT_SECRET_GITLAB_LOGIN= CLIENT_SECRET_GITLAB_LOGIN=
CAPTCHA_SECRET=
NEXT_PUBLIC_CAPTCHA_SITE_KEY=

View File

@ -1,7 +1,6 @@
ARG POSTHOG_HOST=https://app.posthog.com ARG POSTHOG_HOST=https://app.posthog.com
ARG POSTHOG_API_KEY=posthog-api-key ARG POSTHOG_API_KEY=posthog-api-key
ARG INTERCOM_ID=intercom-id ARG INTERCOM_ID=intercom-id
ARG CAPTCHA_SITE_KEY=captcha-site-key
FROM node:20-alpine AS base FROM node:20-alpine AS base
@ -35,9 +34,7 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
ARG INTERCOM_ID ARG INTERCOM_ID
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
ARG INFISICAL_PLATFORM_VERSION ARG INFISICAL_PLATFORM_VERSION
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
ARG CAPTCHA_SITE_KEY
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
# Build # Build
RUN npm run build RUN npm run build
@ -113,9 +110,6 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
ARG INTERCOM_ID=intercom-id ARG INTERCOM_ID=intercom-id
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
ARG CAPTCHA_SITE_KEY
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY \
BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
WORKDIR / WORKDIR /

View File

@ -85,13 +85,13 @@ To set up and run Infisical locally, make sure you have Git and Docker installed
Linux/macOS: Linux/macOS:
```console ```console
git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker compose -f docker-compose.prod.yml up git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.prod.yml up
``` ```
Windows Command Prompt: Windows Command Prompt:
```console ```console
git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker compose -f docker-compose.prod.yml up git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.prod.yml up
``` ```
Create an account at `http://localhost:80` Create an account at `http://localhost:80`

View File

@ -1,29 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasConsecutiveFailedPasswordAttempts = await knex.schema.hasColumn(
TableName.Users,
"consecutiveFailedPasswordAttempts"
);
await knex.schema.alterTable(TableName.Users, (tb) => {
if (!hasConsecutiveFailedPasswordAttempts) {
tb.integer("consecutiveFailedPasswordAttempts").defaultTo(0);
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasConsecutiveFailedPasswordAttempts = await knex.schema.hasColumn(
TableName.Users,
"consecutiveFailedPasswordAttempts"
);
await knex.schema.alterTable(TableName.Users, (tb) => {
if (hasConsecutiveFailedPasswordAttempts) {
tb.dropColumn("consecutiveFailedPasswordAttempts");
}
});
}

View File

@ -25,8 +25,7 @@ export const UsersSchema = z.object({
isEmailVerified: z.boolean().default(false).nullable().optional(), isEmailVerified: z.boolean().default(false).nullable().optional(),
consecutiveFailedMfaAttempts: z.number().default(0).nullable().optional(), consecutiveFailedMfaAttempts: z.number().default(0).nullable().optional(),
isLocked: z.boolean().default(false).nullable().optional(), isLocked: z.boolean().default(false).nullable().optional(),
temporaryLockDateEnd: z.date().nullable().optional(), temporaryLockDateEnd: z.date().nullable().optional()
consecutiveFailedPasswordAttempts: z.number().default(0).nullable().optional()
}); });
export type TUsers = z.infer<typeof UsersSchema>; export type TUsers = z.infer<typeof UsersSchema>;

View File

@ -386,8 +386,6 @@ export const SECRET_IMPORTS = {
environment: "The slug of the environment to import into.", environment: "The slug of the environment to import into.",
path: "The path to import into.", path: "The path to import into.",
workspaceId: "The ID of the project you are working in.", workspaceId: "The ID of the project you are working in.",
isReplication:
"When true, secrets from the source will be automatically sent to the destination. If approval policies exist at the destination, the secrets will be sent as approval requests instead of being applied immediately.",
import: { import: {
environment: "The slug of the environment to import from.", environment: "The slug of the environment to import from.",
path: "The path to import from." path: "The path to import from."
@ -676,8 +674,7 @@ 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: {

View File

@ -120,8 +120,7 @@ const envSchema = z
.transform((val) => val === "true") .transform((val) => val === "true")
.optional(), .optional(),
INFISICAL_CLOUD: zodStrBool.default("false"), INFISICAL_CLOUD: zodStrBool.default("false"),
MAINTENANCE_MODE: zodStrBool.default("false"), MAINTENANCE_MODE: zodStrBool.default("false")
CAPTCHA_SECRET: zpStr(z.string().optional())
}) })
.transform((data) => ({ .transform((data) => ({
...data, ...data,

View File

@ -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 { IntegrationMetadataSchema } from "@app/services/integration/integration-schema"; import { IntegrationMappingBehavior } from "@app/services/integration-auth/integration-list";
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,7 +46,36 @@ 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: IntegrationMetadataSchema.default({}) 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)
})
.default({})
}), }),
response: { response: {
200: z.object({ 200: z.object({
@ -132,7 +161,33 @@ 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: IntegrationMetadataSchema.optional() 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()
}), }),
response: { response: {
200: z.object({ 200: z.object({

View File

@ -30,7 +30,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment), environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment),
path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path) path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path)
}), }),
isReplication: z.boolean().default(false).describe(SECRET_IMPORTS.CREATE.isReplication) isReplication: z.boolean().default(false)
}), }),
response: { response: {
200: z.object({ 200: z.object({

View File

@ -80,8 +80,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
body: z.object({ body: z.object({
email: z.string().trim(), email: z.string().trim(),
providerAuthToken: z.string().trim().optional(), providerAuthToken: z.string().trim().optional(),
clientProof: z.string().trim(), clientProof: z.string().trim()
captchaToken: z.string().trim().optional()
}), }),
response: { response: {
200: z.discriminatedUnion("mfaEnabled", [ 200: z.discriminatedUnion("mfaEnabled", [
@ -107,7 +106,6 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
const appCfg = getConfig(); const appCfg = getConfig();
const data = await server.services.login.loginExchangeClientProof({ const data = await server.services.login.loginExchangeClientProof({
captchaToken: req.body.captchaToken,
email: req.body.email, email: req.body.email,
ip: req.realIp, ip: req.realIp,
userAgent, userAgent,

View File

@ -3,7 +3,6 @@ import jwt from "jsonwebtoken";
import { TUsers, UserDeviceSchema } from "@app/db/schemas"; import { TUsers, UserDeviceSchema } from "@app/db/schemas";
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns"; import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto"; import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
import { BadRequestError, DatabaseError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, DatabaseError, UnauthorizedError } from "@app/lib/errors";
import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { getServerCfg } from "@app/services/super-admin/super-admin-service";
@ -177,16 +176,12 @@ export const authLoginServiceFactory = ({
clientProof, clientProof,
ip, ip,
userAgent, userAgent,
providerAuthToken, providerAuthToken
captchaToken
}: TLoginClientProofDTO) => { }: TLoginClientProofDTO) => {
const appCfg = getConfig();
const userEnc = await userDAL.findUserEncKeyByUsername({ const userEnc = await userDAL.findUserEncKeyByUsername({
username: email username: email
}); });
if (!userEnc) throw new Error("Failed to find user"); if (!userEnc) throw new Error("Failed to find user");
const user = await userDAL.findById(userEnc.userId);
const cfg = getConfig(); const cfg = getConfig();
let authMethod = AuthMethod.EMAIL; let authMethod = AuthMethod.EMAIL;
@ -201,31 +196,6 @@ export const authLoginServiceFactory = ({
} }
} }
if (
user.consecutiveFailedPasswordAttempts &&
user.consecutiveFailedPasswordAttempts >= 10 &&
Boolean(appCfg.CAPTCHA_SECRET)
) {
if (!captchaToken) {
throw new BadRequestError({
name: "Captcha Required",
message: "Accomplish the required captcha by logging in via Web"
});
}
// validate captcha token
const response = await request.postForm<{ success: boolean }>("https://api.hcaptcha.com/siteverify", {
response: captchaToken,
secret: appCfg.CAPTCHA_SECRET
});
if (!response.data.success) {
throw new BadRequestError({
name: "Invalid Captcha"
});
}
}
if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?"); if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?");
const isValidClientProof = await srpCheckClientProof( const isValidClientProof = await srpCheckClientProof(
userEnc.salt, userEnc.salt,
@ -234,31 +204,15 @@ export const authLoginServiceFactory = ({
userEnc.clientPublicKey, userEnc.clientPublicKey,
clientProof clientProof
); );
if (!isValidClientProof) throw new Error("Failed to authenticate. Try again?");
if (!isValidClientProof) {
await userDAL.update(
{ id: userEnc.userId },
{
$incr: {
consecutiveFailedPasswordAttempts: 1
}
}
);
throw new Error("Failed to authenticate. Try again?");
}
await userDAL.updateUserEncryptionByUserId(userEnc.userId, { await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
serverPrivateKey: null, serverPrivateKey: null,
clientPublicKey: null clientPublicKey: null
}); });
await userDAL.updateById(userEnc.userId, {
consecutiveFailedPasswordAttempts: 0
});
// send multi factor auth token if they it enabled // send multi factor auth token if they it enabled
if (userEnc.isMfaEnabled && userEnc.email) { if (userEnc.isMfaEnabled && userEnc.email) {
const user = await userDAL.findById(userEnc.userId);
enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd); enforceUserLockStatus(Boolean(user.isLocked), user.temporaryLockDateEnd);
const mfaToken = jwt.sign( const mfaToken = jwt.sign(

View File

@ -12,7 +12,6 @@ export type TLoginClientProofDTO = {
providerAuthToken?: string; providerAuthToken?: string;
ip: string; ip: string;
userAgent: string; userAgent: string;
captchaToken?: string;
}; };
export type TVerifyMfaTokenDTO = { export type TVerifyMfaTokenDTO = {

View File

@ -31,7 +31,6 @@ 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,
@ -1364,41 +1363,38 @@ const syncSecretsGitHub = async ({
} }
} }
const metadata = IntegrationMetadataSchema.parse(integration.metadata); for await (const encryptedSecret of encryptedSecrets) {
if (metadata.shouldEnableDelete) { if (
for await (const encryptedSecret of encryptedSecrets) { !(encryptedSecret.name in secrets) &&
if ( !(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
!(encryptedSecret.name in secrets) && !(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) && ) {
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix)) switch (integration.scope) {
) { case GithubScope.Org: {
switch (integration.scope) { await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
case GithubScope.Org: { org: integration.owner as string,
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", { secret_name: encryptedSecret.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: { }
await octokit.request( default: {
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}", await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
{ owner: integration.owner as string,
repository_id: Number(integration.appId), repo: integration.app as string,
environment_name: integration.targetEnvironmentId as string, secret_name: encryptedSecret.name
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;
}
} }
} }
} }

View File

@ -1,35 +0,0 @@
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)
});

View File

@ -29,7 +29,6 @@ export type TCreateIntegrationDTO = {
}[]; }[];
kmsKeyId?: string; kmsKeyId?: string;
shouldDisableDelete?: boolean; shouldDisableDelete?: boolean;
shouldEnableDelete?: boolean;
}; };
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
@ -55,7 +54,6 @@ export type TUpdateIntegrationDTO = {
}[]; }[];
kmsKeyId?: string; kmsKeyId?: string;
shouldDisableDelete?: boolean; shouldDisableDelete?: boolean;
shouldEnableDelete?: boolean;
}; };
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;

View File

@ -309,7 +309,7 @@ export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderD
}; };
const expandSecrets = async ( const expandSecrets = async (
secrets: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean | null }> secrets: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean }>
) => { ) => {
const expandedSec: Record<string, string> = {}; const expandedSec: Record<string, string> = {};
const interpolatedSec: Record<string, string> = {}; const interpolatedSec: Record<string, string> = {};
@ -329,8 +329,8 @@ export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderD
// should not do multi line encoding if user has set it to skip // should not do multi line encoding if user has set it to skip
// eslint-disable-next-line // eslint-disable-next-line
secrets[key].value = secrets[key].skipMultilineEncoding secrets[key].value = secrets[key].skipMultilineEncoding
? formatMultiValueEnv(expandedSec[key]) ? expandedSec[key]
: expandedSec[key]; : formatMultiValueEnv(expandedSec[key]);
// eslint-disable-next-line // eslint-disable-next-line
continue; continue;
} }
@ -347,7 +347,7 @@ export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderD
); );
// eslint-disable-next-line // eslint-disable-next-line
secrets[key].value = secrets[key].skipMultilineEncoding ? formatMultiValueEnv(expandedVal) : expandedVal; secrets[key].value = secrets[key].skipMultilineEncoding ? expandedVal : formatMultiValueEnv(expandedVal);
} }
return secrets; return secrets;
@ -395,8 +395,7 @@ export const decryptSecretRaw = (
type: secret.type, type: secret.type,
_id: secret.id, _id: secret.id,
id: secret.id, id: secret.id,
user: secret.userId, user: secret.userId
skipMultilineEncoding: secret.skipMultilineEncoding
}; };
}; };

View File

@ -67,10 +67,7 @@ const MAX_SYNC_SECRET_DEPTH = 5;
export const uniqueSecretQueueKey = (environment: string, secretPath: string) => export const uniqueSecretQueueKey = (environment: string, secretPath: string) =>
`secret-queue-dedupe-${environment}-${secretPath}`; `secret-queue-dedupe-${environment}-${secretPath}`;
type TIntegrationSecret = Record< type TIntegrationSecret = Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean }>;
string,
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
>;
export const secretQueueFactory = ({ export const secretQueueFactory = ({
queueService, queueService,
integrationDAL, integrationDAL,

View File

@ -971,24 +971,10 @@ export const secretServiceFactory = ({
}); });
const batchSecretsExpand = async ( const batchSecretsExpand = async (
secretBatch: { secretBatch: { secretKey: string; secretValue: string; secretComment?: string; secretPath: string }[]
secretKey: string;
secretValue: string;
secretComment?: string;
secretPath: string;
skipMultilineEncoding: boolean | null | undefined;
}[]
) => { ) => {
// Group secrets by secretPath // Group secrets by secretPath
const secretsByPath: Record< const secretsByPath: Record<string, { secretKey: string; secretValue: string; secretComment?: string }[]> = {};
string,
{
secretKey: string;
secretValue: string;
secretComment?: string;
skipMultilineEncoding: boolean | null | undefined;
}[]
> = {};
secretBatch.forEach((secret) => { secretBatch.forEach((secret) => {
if (!secretsByPath[secret.secretPath]) { if (!secretsByPath[secret.secretPath]) {
@ -1004,15 +990,11 @@ export const secretServiceFactory = ({
continue; continue;
} }
const secretRecord: Record< const secretRecord: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean }> = {};
string,
{ value: string; comment?: string; skipMultilineEncoding: boolean | null | undefined }
> = {};
secretsByPath[secPath].forEach((decryptedSecret) => { secretsByPath[secPath].forEach((decryptedSecret) => {
secretRecord[decryptedSecret.secretKey] = { secretRecord[decryptedSecret.secretKey] = {
value: decryptedSecret.secretValue, value: decryptedSecret.secretValue,
comment: decryptedSecret.secretComment, comment: decryptedSecret.secretComment
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
}; };
}); });

View File

@ -384,6 +384,7 @@
"pages": [ "pages": [
"sdks/languages/node", "sdks/languages/node",
"sdks/languages/python", "sdks/languages/python",
"sdks/languages/go",
"sdks/languages/java", "sdks/languages/java",
"sdks/languages/csharp" "sdks/languages/csharp"
] ]

438
docs/sdks/languages/go.mdx Normal file
View File

@ -0,0 +1,438 @@
---
title: "Infisical Go SDK"
sidebarTitle: "Go"
icon: "golang"
---
If you're working with Go Lang, the official [Infisical Go SDK](https://github.com/infisical/go-sdk) package is the easiest way to fetch and work with secrets for your application.
- [Package](https://pkg.go.dev/github.com/infisical/go-sdk)
- [Github Repository](https://github.com/infiscial/go-sdk)
## Basic Usage
```go
package main
import (
"fmt"
"os"
infisical "github.com/infisical/go-sdk"
)
func main() {
client, err := infisical.NewInfisicalClient(infisical.Config{
SiteUrl: "https://app.infisical.com", // Optional, default is https://app.infisical.com
})
if err != nil {
fmt.Printf("Error: %v", err)
os.Exit(1)
}
_, err = client.Auth().UniversalAuthLogin("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
if err != nil {
fmt.Printf("Authentication failed: %v", err)
os.Exit(1)
}
apiKeySecret, err := client.Secrets().Retrieve(infisical.RetrieveSecretOptions{
SecretKey: "API_KEY",
Environment: "dev",
ProjectID: "YOUR_PROJECT_ID",
SecretPath: "/",
})
if err != nil {
fmt.Printf("Error: %v", err)
os.Exit(1)
}
fmt.Printf("API Key Secret: %v", apiKeySecret)
}
```
This example demonstrates how to use the Infisical Go SDK in a simple Go application. The application retrieves a secret named `API_KEY` from the `dev` environment of the `YOUR_PROJECT_ID` project.
<Warning>
We do not recommend hardcoding your [Machine Identity Tokens](/platform/identities/overview). Setting it as an environment variable would be best.
</Warning>
# Installation
```console
$ go get github.com/infisical/go-sdk
```
# Configuration
Import the SDK and create a client instance.
```go
client, err := infisical.NewInfisicalClient(infisical.Config{
SiteUrl: "https://app.infisical.com", // Optional, default is https://api.infisical.com
})
if err != nil {
fmt.Printf("Error: %v", err)
os.Exit(1)
}
```
### ClientSettings methods
<ParamField query="options" type="object">
<Expandable title="properties">
<ParamField query="SiteUrl" type="string" optional>
The URL of the Infisical API. Default is `https://api.infisical.com`.
</ParamField>
<ParamField query="UserAgent" type="string" required>
Optionally set the user agent that will be used for HTTP requests. _(Not recommended)_
</ParamField>
</Expandable>
</ParamField>
### Authentication
The SDK supports a variety of authentication methods. The most common authentication method is Universal Auth, which uses a client ID and client secret to authenticate.
#### Universal Auth
**Using environment variables**
Call `.Auth().UniversalAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_UNIVERSAL_AUTH_CLIENT_ID` - Your machine identity client ID.
- `INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET` - Your machine identity client secret.
**Using the SDK directly**
```go
_, err := client.Auth().UniversalAuthLogin("CLIENT_ID", "CLIENT_SECRET")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
#### GCP ID Token Auth
<Info>
Please note that this authentication method will only work if you're running your application on Google Cloud Platform.
Please [read more](/documentation/platform/identities/gcp-auth) about this authentication method.
</Info>
**Using environment variables**
Call `.Auth().GcpIdTokenAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_GCP_AUTH_IDENTITY_ID` - Your Infisical Machine Identity ID.
**Using the SDK directly**
```go
_, err := client.Auth().GcpIdTokenAuthLogin("YOUR_MACHINE_IDENTITY_ID")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
#### GCP IAM Auth
**Using environment variables**
Call `.Auth().GcpIamAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_GCP_IAM_AUTH_IDENTITY_ID` - Your Infisical Machine Identity ID.
- `INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH` - The path to your GCP service account key file.
**Using the SDK directly**
```go
_, err = client.Auth().GcpIamAuthLogin("MACHINE_IDENTITY_ID", "SERVICE_ACCOUNT_KEY_FILE_PATH")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
#### AWS IAM Auth
<Info>
Please note that this authentication method will only work if you're running your application on AWS.
Please [read more](/documentation/platform/identities/aws-auth) about this authentication method.
</Info>
**Using environment variables**
Call `.Auth().AwsIamAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_AWS_IAM_AUTH_IDENTITY_ID` - Your Infisical Machine Identity ID.
**Using the SDK directly**
```go
_, err = client.Auth().AwsIamAuthLogin("MACHINE_IDENTITY_ID")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
#### Azure Auth
<Info>
Please note that this authentication method will only work if you're running your application on Azure.
Please [read more](/documentation/platform/identities/azure-auth) about this authentication method.
</Info>
**Using environment variables**
Call `.Auth().AzureAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_AZURE_AUTH_IDENTITY_ID` - Your Infisical Machine Identity ID.
**Using the SDK directly**
```go
_, err = client.Auth().AzureAuthLogin("MACHINE_IDENTITY_ID")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
#### Kubernetes Auth
<Info>
Please note that this authentication method will only work if you're running your application on Kubernetes.
Please [read more](/documentation/platform/identities/kubernetes-auth) about this authentication method.
</Info>
**Using environment variables**
Call `.Auth().KubernetesAuthLogin()` with empty arguments to use the following environment variables:
- `INFISICAL_KUBERNETES_IDENTITY_ID` - Your Infisical Machine Identity ID.
- `INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH_ENV_NAME` - The environment variable name that contains the path to the service account token. This is optional and will default to `/var/run/secrets/kubernetes.io/serviceaccount/token`.
**Using the SDK directly**
```go
// Service account token path will default to /var/run/secrets/kubernetes.io/serviceaccount/token if empty value is passed
_, err = client.Auth().KubernetesAuthLogin("MACHINE_IDENTITY_ID", "SERVICE_ACCOUNT_TOKEN_PATH")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
```
## Working with Secrets
### client.Secrets().List(options)
```go
secrets, err := client.Secrets().List(infisical.ListSecretsOptions{
ProjectID: "PROJECT_ID",
Environment: "dev",
SecretPath: "/foo/bar",
AttachToProcessEnv: false,
})
```
Retrieve all secrets within the Infisical project and environment that client is connected to
#### Parameters
<ParamField query="Parameters" type="object">
<Expandable title="properties">
<ParamField query="Environment" type="string" required>
The slug name (dev, prod, etc) of the environment from where secrets should be fetched from.
</ParamField>
<ParamField query="ProjectID" type="string">
The project ID where the secret lives in.
</ParamField>
<ParamField query="SecretPath" type="string" optional>
The path from where secrets should be fetched from.
</ParamField>
<ParamField query="AttachToProcessEnv" type="boolean" default="false" optional>
Whether or not to set the fetched secrets to the process environment. If true, you can access the secrets like so `System.getenv("SECRET_NAME")`.
</ParamField>
<ParamField query="IncludeImports" type="boolean" default="false" optional>
Whether or not to include imported secrets from the current path. Read about [secret import](/documentation/platform/secret-reference)
</ParamField>
<ParamField query="Recursive" type="boolean" default="false" optional>
Whether or not to fetch secrets recursively from the specified path. Please note that there's a 20-depth limit for recursive fetching.
</ParamField>
<ParamField query="ExpandSecretReferences" type="boolean" default="true" optional>
Whether or not to expand secret references in the fetched secrets. Read about [secret reference](/documentation/platform/secret-reference)
</ParamField>
</Expandable>
</ParamField>
### client.Secrets().Get(options)
```go
secret, err := client.Secrets().Retrieve(infisical.RetrieveSecretOptions{
SecretKey: "API_KEY",
ProjectID: "PROJECT_ID",
Environment: "dev",
})
```
Retrieve a secret from Infisical.
By default, `Secrets().Get()` fetches and returns a shared secret.
#### Parameters
<ParamField query="Parameters" type="object" optional>
<Expandable title="properties">
<ParamField query="SecretKey" type="string" required>
The key of the secret to retrieve.
</ParamField>
<ParamField query="ProjectID" type="string" required>
The project ID where the secret lives in.
</ParamField>
<ParamField query="Environment" type="string" required>
The slug name (dev, prod, etc) of the environment from where secrets should be fetched from.
</ParamField>
<ParamField query="SecretPath" type="string" optional>
The path from where secret should be fetched from.
</ParamField>
<ParamField query="Type" type="string" optional>
The type of the secret. Valid options are "shared" or "personal". If not specified, the default value is "shared".
</ParamField>
</Expandable>
</ParamField>
### client.Secrets().Create(options)
```go
secret, err := client.Secrets().Create(infisical.CreateSecretOptions{
ProjectID: "PROJECT_ID",
Environment: "dev",
SecretKey: "NEW_SECRET_KEY",
SecretValue: "NEW_SECRET_VALUE",
SecretComment: "This is a new secret",
})
```
Create a new secret in Infisical.
#### Parameters
<ParamField query="Parameters" type="object" optional>
<Expandable title="properties">
<ParamField query="SecretKey" type="string" required>
The key of the secret to create.
</ParamField>
<ParamField query="SecretValue" type="string" required>
The value of the secret.
</ParamField>
<ParamField query="SecretComment" type="string" optional>
A comment for the secret.
</ParamField>
<ParamField query="ProjectID" type="string" required>
The project ID where the secret lives in.
</ParamField>
<ParamField query="Environment" type="string" required>
The slug name (dev, prod, etc) of the environment from where secrets should be fetched from.
</ParamField>
<ParamField query="SecretPath" type="string" optional>
The path from where secret should be created.
</ParamField>
<ParamField query="Type" type="string" optional>
The type of the secret. Valid options are "shared" or "personal". If not specified, the default value is "shared".
</ParamField>
</Expandable>
</ParamField>
### client.Secrets().Update(options)
```go
secret, err := client.Secrets().Update(infisical.UpdateSecretOptions{
ProjectID: "PROJECT_ID",
Environment: "dev",
SecretKey: "NEW_SECRET_KEY",
NewSecretValue: "NEW_SECRET_VALUE",
NewSkipMultilineEncoding: false,
})
```
Update an existing secret in Infisical.
#### Parameters
<ParamField query="Parameters" type="object" optional>
<Expandable title="properties">
<ParamField query="SecretKey" type="string" required>
The key of the secret to update.
</ParamField>
<ParamField query="NewSecretValue" type="string" required>
The new value of the secret.
</ParamField>
<ParamField query="NewSkipMultilineEncoding" type="boolean" default="false" optional>
Whether or not to skip multiline encoding for the new secret value.
</ParamField>
<ParamField query="ProjectID" type="string" required>
The project ID where the secret lives in.
</ParamField>
<ParamField query="Environment" type="string" required>
The slug name (dev, prod, etc) of the environment from where secrets should be fetched from.
</ParamField>
<ParamField query="SecretPath" type="string" optional>
The path from where secret should be updated.
</ParamField>
<ParamField query="Type" type="string" optional>
The type of the secret. Valid options are "shared" or "personal". If not specified, the default value is "shared".
</ParamField>
</Expandable>
</ParamField>
### client.Secrets().Delete(options)
```go
secret, err := client.Secrets().Delete(infisical.DeleteSecretOptions{
ProjectID: "PROJECT_ID",
Environment: "dev",
SecretKey: "SECRET_KEY",
})
```
Delete a secret in Infisical.
#### Parameters
<ParamField query="Parameters" type="object" optional>
<Expandable title="properties">
<ParamField query="SecretKey" type="string">
The key of the secret to update.
</ParamField>
<ParamField query="ProjectID" type="string" required>
The project ID where the secret lives in.
</ParamField>
<ParamField query="Environment" type="string" required>
The slug name (dev, prod, etc) of the environment from where secrets should be fetched from.
</ParamField>
<ParamField query="SecretPath" type="string" optional>
The path from where secret should be deleted.
</ParamField>
<ParamField query="Type" type="string" optional>
The type of the secret. Valid options are "shared" or "personal". If not specified, the default value is "shared".
</ParamField>
</Expandable>
</ParamField>

View File

@ -2,7 +2,6 @@ ARG POSTHOG_HOST=https://app.posthog.com
ARG POSTHOG_API_KEY=posthog-api-key ARG POSTHOG_API_KEY=posthog-api-key
ARG INTERCOM_ID=intercom-id ARG INTERCOM_ID=intercom-id
ARG NEXT_INFISICAL_PLATFORM_VERSION=next-infisical-platform-version ARG NEXT_INFISICAL_PLATFORM_VERSION=next-infisical-platform-version
ARG CAPTCHA_SITE_KEY=captcha-site-key
FROM node:16-alpine AS deps FROM node:16-alpine AS deps
# Install dependencies only when needed. Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. # Install dependencies only when needed. Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
@ -32,8 +31,6 @@ ARG POSTHOG_API_KEY
ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
ARG INTERCOM_ID ARG INTERCOM_ID
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
ARG CAPTCHA_SITE_KEY
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY $CAPTCHA_SITE_KEY
# Build # Build
RUN npm run build RUN npm run build
@ -60,9 +57,7 @@ ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
ARG NEXT_INFISICAL_PLATFORM_VERSION ARG NEXT_INFISICAL_PLATFORM_VERSION
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION=$NEXT_INFISICAL_PLATFORM_VERSION ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION=$NEXT_INFISICAL_PLATFORM_VERSION
ARG CAPTCHA_SITE_KEY
ENV NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY \
BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY
COPY --chown=nextjs:nodejs --chmod=555 scripts ./scripts COPY --chown=nextjs:nodejs --chmod=555 scripts ./scripts
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
RUN chown nextjs:nodejs ./public/data RUN chown nextjs:nodejs ./public/data

View File

@ -1,12 +1,13 @@
const path = require("path"); const path = require("path");
const ContentSecurityPolicy = ` const ContentSecurityPolicy = `
default-src 'self'; default-src 'self';
script-src 'self' https://app.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' 'unsafe-eval'; script-src 'self' https://app.posthog.com https://js.stripe.com https://api.stripe.com https://widget.intercom.io https://js.intercomcdn.com 'unsafe-inline' 'unsafe-eval';
style-src 'self' https://rsms.me 'unsafe-inline' https://hcaptcha.com https://*.hcaptcha.com; style-src 'self' https://rsms.me 'unsafe-inline';
child-src https://api.stripe.com; child-src https://api.stripe.com;
frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/ https://hcaptcha.com https://*.hcaptcha.com; frame-src https://js.stripe.com/ https://api.stripe.com https://www.youtube.com/;
connect-src 'self' wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:* https://hcaptcha.com https://*.hcaptcha.com; connect-src 'self' wss://nexus-websocket-a.intercom.io https://api-iam.intercom.io https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com https://api.pwnedpasswords.com http://127.0.0.1:*;
img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:; img-src 'self' https://static.intercomassets.com https://js.intercomcdn.com https://downloads.intercomcdn.com https://*.stripe.com https://i.ytimg.com/ data:;
media-src https://js.intercomcdn.com; media-src https://js.intercomcdn.com;
font-src 'self' https://fonts.intercomcdn.com/ https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com; font-src 'self' https://fonts.intercomcdn.com/ https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com;

View File

@ -4,6 +4,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "frontend",
"dependencies": { "dependencies": {
"@casl/ability": "^6.5.0", "@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0", "@casl/react": "^3.1.0",
@ -18,7 +19,6 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@hcaptcha/react-hcaptcha": "^1.10.1",
"@headlessui/react": "^1.7.7", "@headlessui/react": "^1.7.7",
"@hookform/resolvers": "^2.9.10", "@hookform/resolvers": "^2.9.10",
"@octokit/rest": "^19.0.7", "@octokit/rest": "^19.0.7",
@ -3200,24 +3200,6 @@
"react": ">=16.3" "react": ">=16.3"
} }
}, },
"node_modules/@hcaptcha/loader": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@hcaptcha/loader/-/loader-1.2.4.tgz",
"integrity": "sha512-3MNrIy/nWBfyVVvMPBKdKrX7BeadgiimW0AL/a/8TohNtJqxoySKgTJEXOQvYwlHemQpUzFrIsK74ody7JiMYw=="
},
"node_modules/@hcaptcha/react-hcaptcha": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@hcaptcha/react-hcaptcha/-/react-hcaptcha-1.10.1.tgz",
"integrity": "sha512-P0en4gEZAecah7Pt3WIaJO2gFlaLZKkI0+Tfdg8fNqsDxqT9VytZWSkH4WAkiPRULK1QcGgUZK+J56MXYmPifw==",
"dependencies": {
"@babel/runtime": "^7.17.9",
"@hcaptcha/loader": "^1.2.1"
},
"peerDependencies": {
"react": ">= 16.3.0",
"react-dom": ">= 16.3.0"
}
},
"node_modules/@headlessui/react": { "node_modules/@headlessui/react": {
"version": "1.7.18", "version": "1.7.18",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz",

View File

@ -26,7 +26,6 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@hcaptcha/react-hcaptcha": "^1.10.1",
"@headlessui/react": "^1.7.7", "@headlessui/react": "^1.7.7",
"@hookform/resolvers": "^2.9.10", "@hookform/resolvers": "^2.9.10",
"@octokit/rest": "^19.0.7", "@octokit/rest": "^19.0.7",

View File

@ -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_INTERCOM_ID" "$NEXT_PUBLIC_INTERCOM_ID"
scripts/replace-standalone-build-variable.sh "$BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY" "$NEXT_PUBLIC_CAPTCHA_SITE_KEY"
if [ "$TELEMETRY_ENABLED" != "false" ]; then if [ "$TELEMETRY_ENABLED" != "false" ]; then
echo "Telemetry is enabled" echo "Telemetry is enabled"
scripts/set-standalone-build-telemetry.sh true scripts/set-standalone-build-telemetry.sh true

View File

@ -6,8 +6,6 @@ scripts/replace-variable.sh "$BAKED_NEXT_PUBLIC_INTERCOM_ID" "$NEXT_PUBLIC_INTER
scripts/replace-variable.sh "$BAKED_NEXT_SAML_ORG_SLUG" "$NEXT_PUBLIC_SAML_ORG_SLUG" scripts/replace-variable.sh "$BAKED_NEXT_SAML_ORG_SLUG" "$NEXT_PUBLIC_SAML_ORG_SLUG"
scripts/replace-variable.sh "$BAKED_NEXT_PUBLIC_CAPTCHA_SITE_KEY" "$NEXT_PUBLIC_CAPTCHA_SITE_KEY"
if [ "$TELEMETRY_ENABLED" != "false" ]; then if [ "$TELEMETRY_ENABLED" != "false" ]; then
echo "Telemetry is enabled" echo "Telemetry is enabled"
scripts/set-telemetry.sh true scripts/set-telemetry.sh true

View File

@ -30,13 +30,11 @@ export interface IsCliLoginSuccessful {
const attemptLogin = async ({ const attemptLogin = async ({
email, email,
password, password,
providerAuthToken, providerAuthToken
captchaToken
}: { }: {
email: string; email: string;
password: string; password: string;
providerAuthToken?: string; providerAuthToken?: string;
captchaToken?: string;
}): Promise<IsCliLoginSuccessful> => { }): Promise<IsCliLoginSuccessful> => {
const telemetry = new Telemetry().getInstance(); const telemetry = new Telemetry().getInstance();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -72,8 +70,7 @@ const attemptLogin = async ({
} = await login2({ } = await login2({
email, email,
clientProof, clientProof,
providerAuthToken, providerAuthToken
captchaToken
}); });
if (mfaEnabled) { if (mfaEnabled) {
// case: MFA is enabled // case: MFA is enabled

View File

@ -22,13 +22,11 @@ interface IsLoginSuccessful {
const attemptLogin = async ({ const attemptLogin = async ({
email, email,
password, password,
providerAuthToken, providerAuthToken
captchaToken
}: { }: {
email: string; email: string;
password: string; password: string;
providerAuthToken?: string; providerAuthToken?: string;
captchaToken?: string;
}): Promise<IsLoginSuccessful> => { }): Promise<IsLoginSuccessful> => {
const telemetry = new Telemetry().getInstance(); const telemetry = new Telemetry().getInstance();
// eslint-disable-next-line new-cap // eslint-disable-next-line new-cap
@ -60,7 +58,6 @@ const attemptLogin = async ({
iv, iv,
tag tag
} = await login2({ } = await login2({
captchaToken,
email, email,
clientProof, clientProof,
providerAuthToken providerAuthToken

View File

@ -2,6 +2,5 @@ const ENV = process.env.NEXT_PUBLIC_ENV! || "development"; // investigate
const POSTHOG_API_KEY = process.env.NEXT_PUBLIC_POSTHOG_API_KEY!; const POSTHOG_API_KEY = process.env.NEXT_PUBLIC_POSTHOG_API_KEY!;
const POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com"; const POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const INTERCOMid = process.env.NEXT_PUBLIC_INTERCOMid!; const INTERCOMid = process.env.NEXT_PUBLIC_INTERCOMid!;
const CAPTCHA_SITE_KEY = process.env.NEXT_PUBLIC_CAPTCHA_SITE_KEY!;
export { CAPTCHA_SITE_KEY, ENV, INTERCOMid, POSTHOG_API_KEY, POSTHOG_HOST }; export { ENV, INTERCOMid, POSTHOG_API_KEY, POSTHOG_HOST };

View File

@ -30,7 +30,6 @@ export type Login1DTO = {
}; };
export type Login2DTO = { export type Login2DTO = {
captchaToken?: string;
email: string; email: string;
clientProof: string; clientProof: string;
providerAuthToken?: string; providerAuthToken?: string;

View File

@ -73,7 +73,6 @@ export const useCreateIntegration = () => {
}[]; }[];
kmsKeyId?: string; kmsKeyId?: string;
shouldDisableDelete?: boolean; shouldDisableDelete?: boolean;
shouldEnableDelete?: boolean;
}; };
}) => { }) => {
const { const {

View File

@ -33,7 +33,6 @@ import {
Input, Input,
Select, Select,
SelectItem, SelectItem,
Switch,
Tab, Tab,
TabList, TabList,
TabPanel, TabPanel,
@ -60,7 +59,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", {
@ -99,6 +98,7 @@ 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,8 +120,7 @@ export default function GitHubCreateIntegrationPage() {
defaultValues: { defaultValues: {
secretPath: "/", secretPath: "/",
scope: "github-repo", scope: "github-repo",
repoIds: [], repoIds: []
shouldEnableDelete: false
} }
}); });
@ -178,8 +177,7 @@ 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
} }
}); });
}) })
@ -196,8 +194,7 @@ 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;
@ -214,8 +211,7 @@ 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;
@ -550,21 +546,6 @@ 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"

View File

@ -1,17 +1,15 @@
import { FormEvent, useEffect, useRef, useState } from "react"; import { FormEvent, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faLock } from "@fortawesome/free-solid-svg-icons"; import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import HCaptcha from "@hcaptcha/react-hcaptcha";
import Error from "@app/components/basic/Error"; import Error from "@app/components/basic/Error";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import attemptCliLogin from "@app/components/utilities/attemptCliLogin"; import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
import attemptLogin from "@app/components/utilities/attemptLogin"; import attemptLogin from "@app/components/utilities/attemptLogin";
import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
import { Button, Input } from "@app/components/v2"; import { Button, Input } from "@app/components/v2";
import { useServerConfig } from "@app/context"; import { useServerConfig } from "@app/context";
import { useFetchServerStatus } from "@app/hooks/api"; import { useFetchServerStatus } from "@app/hooks/api";
@ -34,9 +32,6 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
const [loginError, setLoginError] = useState(false); const [loginError, setLoginError] = useState(false);
const { config } = useServerConfig(); const { config } = useServerConfig();
const queryParams = new URLSearchParams(window.location.search); const queryParams = new URLSearchParams(window.location.search);
const [captchaToken, setCaptchaToken] = useState("");
const [shouldShowCaptcha, setShouldShowCaptcha] = useState(false);
const captchaRef = useRef<HCaptcha>(null);
const { data: serverDetails } = useFetchServerStatus(); const { data: serverDetails } = useFetchServerStatus();
useEffect(() => { useEffect(() => {
@ -61,8 +56,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
// attemptCliLogin // attemptCliLogin
const isCliLoginSuccessful = await attemptCliLogin({ const isCliLoginSuccessful = await attemptCliLogin({
email: email.toLowerCase(), email: email.toLowerCase(),
password, password
captchaToken
}); });
if (isCliLoginSuccessful && isCliLoginSuccessful.success) { if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
@ -84,8 +78,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
} else { } else {
const isLoginSuccessful = await attemptLogin({ const isLoginSuccessful = await attemptLogin({
email: email.toLowerCase(), email: email.toLowerCase(),
password, password
captchaToken
}); });
if (isLoginSuccessful && isLoginSuccessful.success) { if (isLoginSuccessful && isLoginSuccessful.success) {
@ -119,12 +112,6 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
return; return;
} }
if (err.response.data.error === "Captcha Required") {
setShouldShowCaptcha(true);
setIsLoading(false);
return;
}
setLoginError(true); setLoginError(true);
createNotification({ createNotification({
text: "Login unsuccessful. Double-check your credentials and try again.", text: "Login unsuccessful. Double-check your credentials and try again.",
@ -132,11 +119,6 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
}); });
} }
if (captchaRef.current) {
captchaRef.current.resetCaptcha();
}
setCaptchaToken("");
setIsLoading(false); setIsLoading(false);
}; };
@ -258,19 +240,8 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
className="select:-webkit-autofill:focus h-10" className="select:-webkit-autofill:focus h-10"
/> />
</div> </div>
{shouldShowCaptcha && (
<div className="mt-4">
<HCaptcha
theme="dark"
sitekey={CAPTCHA_SITE_KEY}
onVerify={(token) => setCaptchaToken(token)}
ref={captchaRef}
/>
</div>
)}
<div className="mt-3 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6"> <div className="mt-3 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button <Button
disabled={shouldShowCaptcha && captchaToken === ""}
type="submit" type="submit"
size="sm" size="sm"
isFullWidth isFullWidth

View File

@ -1,15 +1,13 @@
import { useRef, useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import HCaptcha from "@hcaptcha/react-hcaptcha";
import axios from "axios"; import axios from "axios";
import jwt_decode from "jwt-decode"; import jwt_decode from "jwt-decode";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import attemptCliLogin from "@app/components/utilities/attemptCliLogin"; import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
import attemptLogin from "@app/components/utilities/attemptLogin"; import attemptLogin from "@app/components/utilities/attemptLogin";
import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
import { Button, Input } from "@app/components/v2"; import { Button, Input } from "@app/components/v2";
import { useUpdateUserAuthMethods } from "@app/hooks/api"; import { useUpdateUserAuthMethods } from "@app/hooks/api";
import { useSelectOrganization } from "@app/hooks/api/auth/queries"; import { useSelectOrganization } from "@app/hooks/api/auth/queries";
@ -43,10 +41,6 @@ export const PasswordStep = ({
providerAuthToken providerAuthToken
) as any; ) as any;
const [captchaToken, setCaptchaToken] = useState("");
const [shouldShowCaptcha, setShouldShowCaptcha] = useState(false);
const captchaRef = useRef<HCaptcha>(null);
const handleLogin = async (e: React.FormEvent) => { const handleLogin = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
try { try {
@ -57,8 +51,7 @@ export const PasswordStep = ({
const isCliLoginSuccessful = await attemptCliLogin({ const isCliLoginSuccessful = await attemptCliLogin({
email, email,
password, password,
providerAuthToken, providerAuthToken
captchaToken
}); });
if (isCliLoginSuccessful && isCliLoginSuccessful.success) { if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
@ -106,8 +99,7 @@ export const PasswordStep = ({
const loginAttempt = await attemptLogin({ const loginAttempt = await attemptLogin({
email, email,
password, password,
providerAuthToken, providerAuthToken
captchaToken
}); });
if (loginAttempt && loginAttempt.success) { if (loginAttempt && loginAttempt.success) {
@ -166,21 +158,11 @@ export const PasswordStep = ({
return; return;
} }
if (err.response.data.error === "Captcha Required") {
setShouldShowCaptcha(true);
return;
}
createNotification({ createNotification({
text: "Login unsuccessful. Double-check your master password and try again.", text: "Login unsuccessful. Double-check your master password and try again.",
type: "error" type: "error"
}); });
} }
if (captchaRef.current) {
captchaRef.current.resetCaptcha();
}
setCaptchaToken("");
}; };
return ( return (
@ -212,19 +194,8 @@ export const PasswordStep = ({
/> />
</div> </div>
</div> </div>
{shouldShowCaptcha && (
<div className="mx-auto mt-4 flex w-full min-w-[22rem] items-center justify-center lg:w-1/6">
<HCaptcha
theme="dark"
sitekey={CAPTCHA_SITE_KEY}
onVerify={(token) => setCaptchaToken(token)}
ref={captchaRef}
/>
</div>
)}
<div className="mx-auto mt-4 flex w-1/4 w-full min-w-[22rem] items-center justify-center rounded-md text-center lg:w-1/6"> <div className="mx-auto mt-4 flex w-1/4 w-full min-w-[22rem] items-center justify-center rounded-md text-center lg:w-1/6">
<Button <Button
disabled={shouldShowCaptcha && captchaToken === ""}
type="submit" type="submit"
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"

View File

@ -393,15 +393,15 @@ export const SecretDetailSidebar = ({
{(isAllowed) => ( {(isAllowed) => (
<Switch <Switch
id="skipmultiencoding-option" id="skipmultiencoding-option"
onCheckedChange={(isChecked) => onChange(isChecked)} onCheckedChange={(isChecked) => onChange(!isChecked)}
isChecked={value} isChecked={!value}
onBlur={onBlur} onBlur={onBlur}
isDisabled={!isAllowed} isDisabled={!isAllowed}
className="items-center" className="items-center"
> >
Multi line encoding Enable multi line encoding
<Tooltip <Tooltip
content="When enabled, multiline secrets will be handled by escaping newlines and enclosing the entire value in double quotes." content="Infisical encodes multiline secrets by escaping newlines and wrapping in quotes. To disable, enable this option"
className="z-[100]" className="z-[100]"
> >
<FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" /> <FontAwesomeIcon icon={faCircleQuestion} className="ml-1" size="sm" />

View File

@ -232,6 +232,7 @@ func (r *InfisicalSecretReconciler) UpdateInfisicalManagedKubeSecret(ctx context
} }
managedKubeSecret.Data = plainProcessedSecrets managedKubeSecret.Data = plainProcessedSecrets
managedKubeSecret.ObjectMeta.Annotations = map[string]string{}
managedKubeSecret.ObjectMeta.Annotations[SECRET_VERSION_ANNOTATION] = ETag managedKubeSecret.ObjectMeta.Annotations[SECRET_VERSION_ANNOTATION] = ETag
err := r.Client.Update(ctx, &managedKubeSecret) err := r.Client.Update(ctx, &managedKubeSecret)