1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-21 19:34:05 +00:00

Compare commits

..

15 Commits

Author SHA1 Message Date
e6103d2d3f docs: update initial saml setup steps 2025-01-07 11:45:07 -08:00
0cefd6f837 Run linter 2025-01-08 01:41:22 +07:00
f632847dc6 Merge branch 'main', remote-tracking branch 'origin' into auth0-saml 2025-01-08 01:35:11 +07:00
faa6d1cf40 Add support for Auth0 SAML 2025-01-08 01:34:03 +07:00
7fb18870e3 Merge pull request from akhilmhdh/feat/updated-saml-error-message
Updated saml error message
2025-01-07 10:57:03 -05:00
ae841715e5 update saml error message 2025-01-07 10:56:44 -05:00
=
baac87c16a feat: updated saml error message on missing email or first name attribute 2025-01-07 21:17:25 +05:30
b726187ba3 Merge pull request from mr-ssd/patch-1
docs: add note for dynamic secrets as paid feature
2025-01-07 14:12:50 +05:30
d98ff32b07 Merge pull request from mr-ssd/patch-2
docs: add note Approval Workflows as paid feature
2025-01-07 14:12:12 +05:30
1fa510b32f Merge pull request from Infisical/feat/target-specific-azure-key-vault-tenant
feat: target specific azure key vault tenant
2025-01-07 14:05:14 +08:00
c57f0d8120 Merge pull request from Infisical/misc/made-secret-path-input-show-correct-folder
misc: made secret path input show correct folders
2025-01-06 23:28:36 -05:00
00490f2cff misc: made secret path input show correct folders based on env in integrations 2025-01-07 12:08:09 +08:00
ee58f538c0 feat: target specific azure key vault tenant 2025-01-07 11:53:17 +08:00
1190ca2d77 docs: add note Approval Workflows as paid feature 2024-12-25 15:27:38 +07:00
2fb60201bc add not for dynamic secrets as paid feature 2024-12-25 14:17:27 +07:00
78 changed files with 445 additions and 883 deletions
backend/src
docs
frontend/src
const
hooks/api
integrations
secretApprovalRequest
secrets
pages
organization/SettingsPage/components/OrgAuthTab
secret-manager
IntegrationsDetailsByIDPage/components
IntegrationsListPage
SecretApprovalsPage/components/SecretApprovalRequest/components
SecretDashboardPage/components/SecretListView
integrations
AwsSecretManagerConfigurePage
AzureAppConfigurationConfigurePage
AzureKeyVaultAuthorizePage
CircleCIConfigurePage
routeTree.gen.tsroutes.ts

@ -218,9 +218,6 @@ import {
TRateLimit,
TRateLimitInsert,
TRateLimitUpdate,
TResourceMetadata,
TResourceMetadataInsert,
TResourceMetadataUpdate,
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
@ -890,11 +887,6 @@ declare module "knex/types/tables" {
TProjectSplitBackfillIdsInsert,
TProjectSplitBackfillIdsUpdate
>;
[TableName.ResourceMetadata]: KnexOriginal.CompositeTableType<
TResourceMetadata,
TResourceMetadataInsert,
TResourceMetadataUpdate
>;
[TableName.AppConnection]: KnexOriginal.CompositeTableType<
TAppConnections,
TAppConnectionsInsert,

@ -1,40 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.ResourceMetadata))) {
await knex.schema.createTable(TableName.ResourceMetadata, (tb) => {
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
tb.string("key").notNullable();
tb.string("value", 1020).notNullable();
tb.uuid("orgId").notNullable();
tb.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
tb.uuid("userId");
tb.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
tb.uuid("identityId");
tb.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
tb.uuid("secretId");
tb.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
tb.timestamps(true, true, true);
});
}
const hasSecretMetadataField = await knex.schema.hasColumn(TableName.SecretApprovalRequestSecretV2, "secretMetadata");
if (!hasSecretMetadataField) {
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
t.jsonb("secretMetadata");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.ResourceMetadata);
const hasSecretMetadataField = await knex.schema.hasColumn(TableName.SecretApprovalRequestSecretV2, "secretMetadata");
if (hasSecretMetadataField) {
await knex.schema.alterTable(TableName.SecretApprovalRequestSecretV2, (t) => {
t.dropColumn("secretMetadata");
});
}
}

@ -71,7 +71,6 @@ export * from "./project-user-additional-privilege";
export * from "./project-user-membership-roles";
export * from "./projects";
export * from "./rate-limit";
export * from "./resource-metadata";
export * from "./saml-configs";
export * from "./scim-tokens";
export * from "./secret-approval-policies";

@ -80,7 +80,6 @@ export enum TableName {
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
// used by both identity and users
IdentityMetadata = "identity_metadata",
ResourceMetadata = "resource_metadata",
ScimToken = "scim_tokens",
AccessApprovalPolicy = "access_approval_policies",
AccessApprovalPolicyApprover = "access_approval_policies_approvers",

@ -1,24 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const ResourceMetadataSchema = z.object({
id: z.string().uuid(),
key: z.string(),
value: z.string(),
orgId: z.string().uuid(),
userId: z.string().uuid().nullable().optional(),
identityId: z.string().uuid().nullable().optional(),
secretId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TResourceMetadata = z.infer<typeof ResourceMetadataSchema>;
export type TResourceMetadataInsert = Omit<z.input<typeof ResourceMetadataSchema>, TImmutableDBKeys>;
export type TResourceMetadataUpdate = Partial<Omit<z.input<typeof ResourceMetadataSchema>, TImmutableDBKeys>>;

@ -24,8 +24,7 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({
requestId: z.string().uuid(),
op: z.string(),
secretId: z.string().uuid().nullable().optional(),
secretVersion: z.string().uuid().nullable().optional(),
secretMetadata: z.unknown().nullable().optional()
secretVersion: z.string().uuid().nullable().optional()
});
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;

@ -84,7 +84,10 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
samlConfig.audience = `spn:${ssoConfig.issuer}`;
}
}
if (ssoConfig.authProvider === SamlProviders.GOOGLE_SAML) {
if (
ssoConfig.authProvider === SamlProviders.GOOGLE_SAML ||
ssoConfig.authProvider === SamlProviders.AUTH0_SAML
) {
samlConfig.wantAssertionsSigned = false;
}
@ -123,7 +126,10 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
`email: ${email} firstName: ${profile.firstName as string}`
);
throw new Error("Invalid saml request. Missing email or first name");
throw new BadRequestError({
message:
"Missing email or first name. Please double check your SAML attribute mapping for the selected provider."
});
}
const userMetadata = Object.keys(profile.attributes || {})

@ -12,7 +12,6 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
const approvalRequestUser = z.object({ userId: z.string().nullable().optional() }).merge(
UsersSchema.pick({
@ -275,7 +274,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
.extend({
op: z.string(),
tags: tagSchema,
secretMetadata: ResourceMetadataSchema.nullish(),
secret: z
.object({
id: z.string(),
@ -293,8 +291,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
secretKey: z.string(),
secretValue: z.string().optional(),
secretComment: z.string().optional(),
tags: tagSchema,
secretMetadata: ResourceMetadataSchema.nullish()
tags: tagSchema
})
.optional()
})

@ -6,7 +6,8 @@ export enum SamlProviders {
AZURE_SAML = "azure-saml",
JUMPCLOUD_SAML = "jumpcloud-saml",
GOOGLE_SAML = "google-saml",
KEYCLOAK_SAML = "keycloak-saml"
KEYCLOAK_SAML = "keycloak-saml",
AUTH0_SAML = "auth0-saml"
}
export type TCreateSamlCfgDTO = {

@ -256,7 +256,6 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
db.ref("id").withSchema("secVerTag")
)
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
.select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2))
.select({
secVerTagId: "secVerTag.id",
@ -280,11 +279,6 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment")
)
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
);
const formatedDoc = sqlNestRelationships({
data: doc,
@ -344,19 +338,9 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
})
}
]
},
{
key: "metadataId",
label: "oldSecretMetadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
...el,
secret: secret?.[0],

@ -22,8 +22,6 @@ import { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import {
decryptSecretWithBot,
@ -93,7 +91,6 @@ type TSecretApprovalRequestServiceFactoryDep = {
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
smtpService: Pick<TSmtpService, "sendMail">;
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "findById">;
@ -141,8 +138,7 @@ export const secretApprovalRequestServiceFactory = ({
secretVersionV2BridgeDAL,
secretVersionTagV2BridgeDAL,
licenseService,
projectSlackConfigDAL,
resourceMetadataDAL
projectSlackConfigDAL
}: TSecretApprovalRequestServiceFactoryDep) => {
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
@ -245,7 +241,6 @@ export const secretApprovalRequestServiceFactory = ({
secretKey: el.key,
id: el.id,
version: el.version,
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
secretComment: el.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
@ -274,8 +269,7 @@ export const secretApprovalRequestServiceFactory = ({
secretComment: el.secretVersion.encryptedComment
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
: "",
tags: el.secretVersion.tags,
secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO
tags: el.secretVersion.tags
}
: undefined
}));
@ -549,7 +543,6 @@ export const secretApprovalRequestServiceFactory = ({
? await fnSecretV2BridgeBulkInsert({
tx,
folderId,
orgId: actorOrgId,
inputSecrets: secretCreationCommits.map((el) => ({
tagIds: el?.tags.map(({ id }) => id),
version: 1,
@ -557,7 +550,6 @@ export const secretApprovalRequestServiceFactory = ({
encryptedValue: el.encryptedValue,
skipMultilineEncoding: el.skipMultilineEncoding,
key: el.key,
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
references: el.encryptedValue
? getAllSecretReferencesV2Bridge(
secretManagerDecryptor({
@ -567,7 +559,6 @@ export const secretApprovalRequestServiceFactory = ({
: [],
type: SecretType.Shared
})),
resourceMetadataDAL,
secretDAL: secretV2BridgeDAL,
secretVersionDAL: secretVersionV2BridgeDAL,
secretTagDAL,
@ -577,7 +568,6 @@ export const secretApprovalRequestServiceFactory = ({
const updatedSecrets = secretUpdationCommits.length
? await fnSecretV2BridgeBulkUpdate({
folderId,
orgId: actorOrgId,
tx,
inputSecrets: secretUpdationCommits.map((el) => {
const encryptedValue =
@ -602,7 +592,6 @@ export const secretApprovalRequestServiceFactory = ({
skipMultilineEncoding: el.skipMultilineEncoding,
key: el.key,
tags: el?.tags.map(({ id }) => id),
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
...encryptedValue
}
};
@ -610,8 +599,7 @@ export const secretApprovalRequestServiceFactory = ({
secretDAL: secretV2BridgeDAL,
secretVersionDAL: secretVersionV2BridgeDAL,
secretTagDAL,
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
resourceMetadataDAL
secretVersionTagDAL: secretVersionTagV2BridgeDAL
})
: [];
const deletedSecret = secretDeletionCommits.length
@ -836,7 +824,6 @@ export const secretApprovalRequestServiceFactory = ({
}
await secretQueueService.syncSecrets({
projectId,
orgId: actorOrgId,
secretPath: folder.path,
environmentSlug: folder.environmentSlug,
actorId,
@ -1221,7 +1208,6 @@ export const secretApprovalRequestServiceFactory = ({
),
skipMultilineEncoding: createdSecret.skipMultilineEncoding,
key: createdSecret.secretKey,
secretMetadata: createdSecret.secretMetadata,
type: SecretType.Shared
}))
);
@ -1277,14 +1263,12 @@ export const secretApprovalRequestServiceFactory = ({
reminderNote,
secretComment,
metadata,
skipMultilineEncoding,
secretMetadata
skipMultilineEncoding
}) => {
const secretId = updatingSecretsGroupByKey[secretKey][0].id;
if (tagIds?.length) commitTagIds[secretKey] = tagIds;
return {
...latestSecretVersions[secretId],
secretMetadata,
key: newSecretName || secretKey,
encryptedComment: setKnexStringValue(
secretComment,
@ -1386,8 +1370,7 @@ export const secretApprovalRequestServiceFactory = ({
reminderRepeatDays,
encryptedValue,
secretId,
secretVersion,
secretMetadata
secretVersion
}) => ({
version,
requestId: doc.id,
@ -1400,8 +1383,7 @@ export const secretApprovalRequestServiceFactory = ({
reminderRepeatDays,
reminderNote,
encryptedComment,
key,
secretMetadata: JSON.stringify(secretMetadata)
key
})
),
tx

@ -1,6 +1,5 @@
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
import { TProjectPermission } from "@app/lib/types";
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
import { SecretOperations } from "@app/services/secret/secret-types";
export enum RequestState {
@ -35,7 +34,6 @@ export type TApprovalCreateSecretV2Bridge = {
reminderRepeatDays?: number | null;
skipMultilineEncoding?: boolean;
metadata?: Record<string, string>;
secretMetadata?: ResourceMetadataDTO;
tagIds?: string[];
};

@ -13,8 +13,6 @@ import { ActorType } from "@app/services/auth/auth-type";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
@ -58,7 +56,6 @@ type TSecretReplicationServiceFactoryDep = {
>;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "find" | "insertMany">;
secretVersionV2TagBridgeDAL: Pick<TSecretVersionV2TagDALFactory, "find" | "insertMany">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
queueService: Pick<TQueueServiceFactory, "start" | "listen" | "queue" | "stopJobById">;
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
@ -124,8 +121,7 @@ export const secretReplicationServiceFactory = ({
secretVersionV2TagBridgeDAL,
secretVersionV2BridgeDAL,
secretV2BridgeDAL,
kmsService,
resourceMetadataDAL
kmsService
}: TSecretReplicationServiceFactoryDep) => {
const $getReplicatedSecrets = (
botKey: string,
@ -155,10 +151,8 @@ export const secretReplicationServiceFactory = ({
};
const $getReplicatedSecretsV2 = (
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[],
importedSecrets: {
secrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[];
}[]
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[],
importedSecrets: { secrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[] }[]
) => {
const deDupe = new Set<string>();
const secrets = [...localSecrets];
@ -184,7 +178,6 @@ export const secretReplicationServiceFactory = ({
secretPath,
environmentSlug,
projectId,
orgId,
actorId,
actor,
pickOnlyImportIds,
@ -229,7 +222,6 @@ export const secretReplicationServiceFactory = ({
.map(({ folderId }) =>
secretQueueService.replicateSecrets({
projectId,
orgId,
secretPath: foldersGroupedById[folderId][0]?.path as string,
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
actorId,
@ -275,7 +267,6 @@ export const secretReplicationServiceFactory = ({
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
: undefined
}));
const sourceSecrets = $getReplicatedSecretsV2(sourceDecryptedLocalSecrets, sourceImportedSecrets);
const sourceSecretsGroupByKey = groupBy(sourceSecrets, (i) => i.key);
@ -342,29 +333,13 @@ export const secretReplicationServiceFactory = ({
.map((el) => ({ ...el, operation: SecretOperations.Create })); // rewrite update ops to create
const locallyUpdatedSecrets = sourceSecrets
.filter(({ key, secretKey, secretValue, secretMetadata }) => {
const sourceSecretMetadataJson = JSON.stringify(
(secretMetadata ?? []).map((entry) => ({
key: entry.key,
value: entry.value
}))
);
const destinationSecretMetadataJson = JSON.stringify(
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretMetadata ?? []).map((entry) => ({
key: entry.key,
value: entry.value
}))
);
return (
.filter(
({ key, secretKey, secretValue }) =>
destinationLocalSecretsGroupedByKey[key]?.[0] &&
// if key or value changed
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretKey !== secretKey ||
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue ||
sourceSecretMetadataJson !== destinationSecretMetadataJson)
);
})
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue)
)
.map((el) => ({ ...el, operation: SecretOperations.Update })); // rewrite update ops to create
const locallyDeletedSecrets = destinationLocalSecrets
@ -412,7 +387,6 @@ export const secretReplicationServiceFactory = ({
op: operation,
requestId: approvalRequestDoc.id,
metadata: doc.metadata,
secretMetadata: JSON.stringify(doc.secretMetadata),
key: doc.key,
encryptedValue: doc.encryptedValue,
encryptedComment: doc.encryptedComment,
@ -432,12 +406,10 @@ export const secretReplicationServiceFactory = ({
if (locallyCreatedSecrets.length) {
await fnSecretV2BridgeBulkInsert({
folderId: destinationReplicationFolderId,
orgId,
secretVersionDAL: secretVersionV2BridgeDAL,
secretDAL: secretV2BridgeDAL,
tx,
secretTagDAL,
resourceMetadataDAL,
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
inputSecrets: locallyCreatedSecrets.map((doc) => {
return {
@ -447,7 +419,6 @@ export const secretReplicationServiceFactory = ({
encryptedValue: doc.encryptedValue,
encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding,
secretMetadata: doc.secretMetadata,
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
};
})
@ -455,12 +426,10 @@ export const secretReplicationServiceFactory = ({
}
if (locallyUpdatedSecrets.length) {
await fnSecretV2BridgeBulkUpdate({
orgId,
folderId: destinationReplicationFolderId,
secretVersionDAL: secretVersionV2BridgeDAL,
secretDAL: secretV2BridgeDAL,
tx,
resourceMetadataDAL,
secretTagDAL,
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
inputSecrets: locallyUpdatedSecrets.map((doc) => {
@ -476,7 +445,6 @@ export const secretReplicationServiceFactory = ({
encryptedValue: doc.encryptedValue as Buffer,
encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding,
secretMetadata: doc.secretMetadata,
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
}
};
@ -498,7 +466,6 @@ export const secretReplicationServiceFactory = ({
await secretQueueService.syncSecrets({
projectId,
orgId,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environmentSlug,
actorId,
@ -784,7 +751,6 @@ export const secretReplicationServiceFactory = ({
await secretQueueService.syncSecrets({
projectId,
orgId,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environmentSlug,
actorId,

@ -1150,8 +1150,7 @@ export const INTEGRATION = {
shouldMaskSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Masked'.",
shouldProtectSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Protected'.",
shouldEnableDelete: "The flag to enable deletion of secrets.",
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy.",
metadataSyncMode: "The mode for syncing metadata to external system"
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy."
}
},
UPDATE: {

@ -181,7 +181,6 @@ import { projectUserMembershipRoleDALFactory } from "@app/services/project-membe
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
import { secretDALFactory } from "@app/services/secret/secret-dal";
import { secretQueueFactory } from "@app/services/secret/secret-queue";
import { secretServiceFactory } from "@app/services/secret/secret-service";
@ -375,7 +374,6 @@ export const registerRoutes = async (
const externalGroupOrgRoleMappingDAL = externalGroupOrgRoleMappingDALFactory(db);
const projectTemplateDAL = projectTemplateDALFactory(db);
const resourceMetadataDAL = resourceMetadataDALFactory(db);
const permissionService = permissionServiceFactory({
permissionDAL,
@ -856,8 +854,7 @@ export const registerRoutes = async (
secretApprovalRequestDAL,
projectKeyDAL,
projectUserMembershipRoleDAL,
orgService,
resourceMetadataDAL
orgService
});
const projectService = projectServiceFactory({
@ -983,8 +980,7 @@ export const registerRoutes = async (
secretApprovalPolicyService,
secretApprovalRequestSecretDAL,
kmsService,
snapshotService,
resourceMetadataDAL
snapshotService
});
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
@ -1011,8 +1007,7 @@ export const registerRoutes = async (
projectEnvDAL,
userDAL,
licenseService,
projectSlackConfigDAL,
resourceMetadataDAL
projectSlackConfigDAL
});
const secretService = secretServiceFactory({
@ -1091,10 +1086,8 @@ export const registerRoutes = async (
kmsService,
secretV2BridgeDAL,
secretVersionV2TagBridgeDAL: secretVersionTagV2BridgeDAL,
secretVersionV2BridgeDAL,
resourceMetadataDAL
secretVersionV2BridgeDAL
});
const secretRotationQueue = secretRotationQueueFactory({
telemetryService,
secretRotationDAL,
@ -1346,8 +1339,7 @@ export const registerRoutes = async (
folderDAL,
secretDAL: secretV2BridgeDAL,
queueService,
secretV2BridgeService,
resourceMetadataDAL
secretV2BridgeService
});
const migrationService = externalMigrationServiceFactory({

@ -17,7 +17,6 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
import { SecretsOrderBy } from "@app/services/secret/secret-types";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
@ -117,7 +116,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: secretRawSchema
.extend({
secretPath: z.string().optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
@ -410,7 +408,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: secretRawSchema
.extend({
secretPath: z.string().optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
@ -696,7 +693,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: secretRawSchema
.extend({
secretPath: z.string().optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
@ -868,7 +864,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
secrets: secretRawSchema
.extend({
secretPath: z.string().optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,

@ -18,7 +18,6 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
import { ProjectFilterType } from "@app/services/project/project-types";
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
import { SecretOperations, SecretProtectionType } from "@app/services/secret/secret-types";
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
@ -206,7 +205,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secrets: secretRawSchema
.extend({
secretPath: z.string().optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
@ -222,12 +220,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretPath: z.string(),
environment: z.string(),
folderId: z.string().optional(),
secrets: secretRawSchema
.omit({ createdAt: true, updatedAt: true })
.extend({
secretMetadata: ResourceMetadataSchema.optional()
})
.array()
secrets: secretRawSchema.omit({ createdAt: true, updatedAt: true }).array()
})
.array()
.optional()
@ -355,8 +348,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
})
.extend({ name: z.string() })
.array()
.optional(),
secretMetadata: ResourceMetadataSchema.optional()
.optional()
})
})
}
@ -458,7 +450,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
.describe(RAW_SECRETS.CREATE.secretValue),
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
secretMetadata: ResourceMetadataSchema.optional(),
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
@ -493,7 +484,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretValue: req.body.secretValue,
skipMultilineEncoding: req.body.skipMultilineEncoding,
secretComment: req.body.secretComment,
secretMetadata: req.body.secretMetadata,
tagIds: req.body.tagIds,
secretReminderNote: req.body.secretReminderNote,
secretReminderRepeatDays: req.body.secretReminderRepeatDays
@ -568,7 +558,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
metadata: z.record(z.string()).optional(),
secretMetadata: ResourceMetadataSchema.optional(),
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
secretReminderRepeatDays: z
.number()
@ -606,10 +595,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretReminderNote: req.body.secretReminderNote,
metadata: req.body.metadata,
newSecretName: req.body.newSecretName,
secretComment: req.body.secretComment,
secretMetadata: req.body.secretMetadata
secretComment: req.body.secretComment
});
if (secretOperation.type === SecretProtectionType.Approval) {
return { approval: secretOperation.approval };
}
@ -1863,7 +1850,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
metadata: z.record(z.string()).optional(),
secretMetadata: ResourceMetadataSchema.optional(),
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
})
.array()
@ -1966,7 +1952,6 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
secretMetadata: ResourceMetadataSchema.optional(),
secretReminderRepeatDays: z
.number()
.optional()

@ -16,7 +16,6 @@ import { TProjectDALFactory } from "../project/project-dal";
import { TProjectServiceFactory } from "../project/project-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
@ -36,8 +35,6 @@ export type TImportDataIntoInfisicalDTO = {
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath" | "findById">;
projectService: Pick<TProjectServiceFactory, "createProject">;
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
@ -506,7 +503,6 @@ export const importDataIntoInfisicalFn = async ({
secretTagDAL,
secretVersionTagDAL,
folderDAL,
resourceMetadataDAL,
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
}: TImportDataIntoInfisicalDTO) => {
// Import data to infisical
@ -766,8 +762,6 @@ export const importDataIntoInfisicalFn = async ({
};
}),
folderId: selectedFolder.id,
orgId: actorOrgId,
resourceMetadataDAL,
secretDAL,
secretVersionDAL,
secretTagDAL,

@ -8,7 +8,6 @@ import { TProjectDALFactory } from "../project/project-dal";
import { TProjectServiceFactory } from "../project/project-service";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
@ -36,8 +35,6 @@ export type TExternalMigrationQueueFactoryDep = {
projectService: Pick<TProjectServiceFactory, "createProject">;
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
@ -55,8 +52,7 @@ export const externalMigrationQueueFactory = ({
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
folderDAL,
resourceMetadataDAL
folderDAL
}: TExternalMigrationQueueFactoryDep) => {
const startImport = async (dto: {
actorEmail: string;
@ -113,8 +109,7 @@ export const externalMigrationQueueFactory = ({
kmsService,
projectService,
projectEnvService,
secretV2BridgeService,
resourceMetadataDAL
secretV2BridgeService
});
if (projectsNotImported.length) {

@ -427,8 +427,3 @@ export const getIntegrationOptions = async () => {
return INTEGRATION_OPTIONS;
};
export enum IntegrationMetadataSyncMode {
CUSTOM = "custom",
SECRET_METADATA = "secret-metadata"
}

@ -38,7 +38,6 @@ import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/
import { TIntegrationDALFactory } from "../integration/integration-dal";
import { IntegrationMetadataSchema } from "../integration/integration-schema";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { IntegrationAuthMetadataSchema } from "./integration-auth-schema";
import {
CircleCiScope,
@ -49,7 +48,6 @@ import {
import {
IntegrationInitialSyncBehavior,
IntegrationMappingBehavior,
IntegrationMetadataSyncMode,
Integrations,
IntegrationUrls
} from "./integration-list";
@ -1084,14 +1082,14 @@ const syncSecretsAWSSecretManager = async ({
projectId
}: {
integration: TIntegrations;
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
secrets: Record<string, { value: string; comment?: string }>;
accessId: string | null;
accessToken: string;
awsAssumeRoleArn: string | null;
projectId?: string;
}) => {
const appCfg = getConfig();
const metadata = IntegrationMetadataSchema.parse(integration.metadata || {});
const metadata = z.record(z.any()).parse(integration.metadata || {});
if (!accessId && !awsAssumeRoleArn) {
throw new Error("AWS access ID/AWS Assume Role is required");
@ -1139,25 +1137,8 @@ const syncSecretsAWSSecretManager = async ({
const processAwsSecret = async (
secretId: string,
secretValue: Record<string, string | null | undefined> | string,
secretMetadata?: ResourceMetadataDTO
secretValue: Record<string, string | null | undefined> | string
) => {
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
const shouldTag =
(secretAWSTag && secretAWSTag.length) ||
(metadata.metadataSyncMode === IntegrationMetadataSyncMode.SECRET_METADATA &&
metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE);
const tagArray =
(metadata.metadataSyncMode === IntegrationMetadataSyncMode.SECRET_METADATA ? secretMetadata : secretAWSTag) ?? [];
const integrationTagObj = tagArray.reduce(
(acc, item) => {
acc[item.key] = item.value;
return acc;
},
{} as Record<string, string>
);
try {
const awsSecretManagerSecret = await secretsManager.send(
new GetSecretValueCommand({
@ -1193,7 +1174,9 @@ const syncSecretsAWSSecretManager = async ({
}
}
if (shouldTag) {
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
if (secretAWSTag && secretAWSTag.length) {
const describedSecret = await secretsManager.send(
// requires secretsmanager:DescribeSecret policy
new DescribeSecretCommand({
@ -1203,6 +1186,14 @@ const syncSecretsAWSSecretManager = async ({
if (!describedSecret.Tags) return;
const integrationTagObj = secretAWSTag.reduce(
(acc, item) => {
acc[item.key] = item.value;
return acc;
},
{} as Record<string, string>
);
const awsTagObj = (describedSecret.Tags || []).reduce(
(acc, item) => {
if (item.Key && item.Value) {
@ -1234,7 +1225,7 @@ const syncSecretsAWSSecretManager = async ({
}
});
tagArray.forEach((tag) => {
secretAWSTag?.forEach((tag) => {
if (!(tag.key in awsTagObj)) {
// create tag in AWS secret manager
tagsToUpdate.push({
@ -1271,8 +1262,8 @@ const syncSecretsAWSSecretManager = async ({
Name: secretId,
SecretString: typeof secretValue === "string" ? secretValue : JSON.stringify(secretValue),
...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
Tags: shouldTag
? tagArray.map((tag: { key: string; value: string }) => ({
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
Key: tag.key,
Value: tag.value
}))
@ -1289,7 +1280,7 @@ const syncSecretsAWSSecretManager = async ({
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
for await (const [key, value] of Object.entries(secrets)) {
await processAwsSecret(key, value.value, value.secretMetadata);
await processAwsSecret(key, value.value);
}
} else {
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
@ -4449,7 +4440,7 @@ export const syncIntegrationSecrets = async ({
secretPath: string;
};
integrationAuth: TIntegrationAuths;
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
secrets: Record<string, { value: string; comment?: string }>;
accessId: string | null;
awsAssumeRoleArn: string | null;
accessToken: string;

@ -2,7 +2,7 @@ import { z } from "zod";
import { INTEGRATION } from "@app/lib/api-docs";
import { IntegrationMappingBehavior, IntegrationMetadataSyncMode } from "../integration-auth/integration-list";
import { IntegrationMappingBehavior } from "../integration-auth/integration-list";
export const IntegrationMetadataSchema = z.object({
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
@ -50,11 +50,6 @@ export const IntegrationMetadataSchema = z.object({
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets),
metadataSyncMode: z
.nativeEnum(IntegrationMetadataSyncMode)
.optional()
.describe(INTEGRATION.CREATE.metadata.metadataSyncMode),
octopusDeployScopeValues: z
.object({
// in Octopus Deploy Scope Value Format

@ -1,11 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TResourceMetadataDALFactory = ReturnType<typeof resourceMetadataDALFactory>;
export const resourceMetadataDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.ResourceMetadata);
return orm;
};

@ -1,10 +0,0 @@
import z from "zod";
export const ResourceMetadataSchema = z
.object({
key: z.string().trim().min(1),
value: z.string().trim().default("")
})
.array();
export type ResourceMetadataDTO = z.infer<typeof ResourceMetadataSchema>;

@ -1,7 +1,6 @@
import { SecretType, TSecretImports, TSecrets, TSecretsV2 } from "@app/db/schemas";
import { groupBy, unique } from "@app/lib/fn";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretDALFactory } from "../secret/secret-dal";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
@ -40,7 +39,6 @@ type TSecretImportSecretsV2 = {
// But for somereason ts consider ? and undefined explicit as different just ts things
secretValue: string;
secretComment: string;
secretMetadata?: ResourceMetadataDTO;
})[];
};

@ -160,7 +160,6 @@ export const secretImportServiceFactory = ({
if (secImport.isReplication && sourceFolder) {
await secretQueueService.replicateSecrets({
secretPath: secImport.importPath,
orgId: actorOrgId,
projectId,
environmentSlug: importEnv.slug,
pickOnlyImportIds: [secImport.id],
@ -170,7 +169,6 @@ export const secretImportServiceFactory = ({
} else {
await secretQueueService.syncSecrets({
secretPath,
orgId: actorOrgId,
projectId,
environmentSlug: environment,
actorId,
@ -342,7 +340,6 @@ export const secretImportServiceFactory = ({
await secretQueueService.syncSecrets({
secretPath,
orgId: actorOrgId,
projectId,
environmentSlug: environment,
actor,
@ -418,7 +415,6 @@ export const secretImportServiceFactory = ({
if (membership && sourceFolder) {
await secretQueueService.replicateSecrets({
orgId: actorOrgId,
secretPath: secretImportDoc.importPath,
projectId,
environmentSlug: secretImportDoc.importEnv.slug,

@ -78,12 +78,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
`${TableName.SecretTag}.id`
)
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
.select(selectAllTableCols(TableName.SecretV2))
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
@ -109,15 +103,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
slug,
name: slug
})
},
{
key: "metadataId",
label: "secretMetadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
@ -236,9 +221,7 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
.where({ folderId })
.where((bd) => {
void bd
.whereNull(`${TableName.SecretV2}.userId`)
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
void bd.whereNull("userId").orWhere({ userId: userId || null });
})
.leftJoin(
TableName.SecretV2JnTag,
@ -250,16 +233,10 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
`${TableName.SecretTag}.id`
)
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
.select(selectAllTableCols(TableName.SecretV2))
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
.orderBy("id", "asc");
const data = sqlNestRelationships({
@ -276,15 +253,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
slug,
name: slug
})
},
{
key: "metadataId",
label: "secretMetadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
@ -399,9 +367,7 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
}
})
.where((bd) => {
void bd
.whereNull(`${TableName.SecretV2}.userId`)
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
void bd.whereNull(`${TableName.SecretV2}.userId`).orWhere({ userId: userId || null });
})
.leftJoin(
TableName.SecretV2JnTag,
@ -413,23 +379,13 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
`${TableName.SecretTag}.id`
)
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
.select(
selectAllTableCols(TableName.SecretV2),
db.raw(
`DENSE_RANK() OVER (ORDER BY "${TableName.SecretV2}".key ${
filters?.orderDirection ?? OrderByDirection.ASC
}) as rank`
)
db.raw(`DENSE_RANK() OVER (ORDER BY "key" ${filters?.orderDirection ?? OrderByDirection.ASC}) as rank`)
)
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
)
.where((bd) => {
const slugs = filters?.tagSlugs?.filter(Boolean);
if (slugs && slugs.length > 0) {
@ -469,15 +425,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
slug,
name: slug
})
},
{
key: "metadataId",
label: "secretMetadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});
@ -598,17 +545,10 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
`${TableName.SecretTag}.id`
)
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
.select(selectAllTableCols(TableName.SecretV2))
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(
db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"),
db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"),
db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue")
);
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"));
const docs = sqlNestRelationships({
data: rawDocs,
key: "id",
@ -623,15 +563,6 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
slug,
name: slug
})
},
{
key: "metadataId",
label: "secretMetadata" as const,
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
id: metadataId,
key: metadataKey,
value: metadataValue
})
}
]
});

@ -6,7 +6,6 @@ import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
@ -55,11 +54,9 @@ export const getAllSecretReferences = (maybeSecretReference: string) => {
export const fnSecretBulkInsert = async ({
// TODO: Pick types here
folderId,
orgId,
inputSecrets,
secretDAL,
secretVersionDAL,
resourceMetadataDAL,
secretTagDAL,
secretVersionTagDAL,
tx
@ -94,7 +91,6 @@ export const fnSecretBulkInsert = async ({
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
tx
);
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
secretTags.map((tag) => ({
@ -110,7 +106,6 @@ export const fnSecretBulkInsert = async ({
})),
tx
);
await secretDAL.upsertSecretReferences(
inputSecrets.map(({ references = [], key }) => ({
secretId: newSecretGroupedByKeyName[key][0].id,
@ -118,22 +113,6 @@ export const fnSecretBulkInsert = async ({
})),
tx
);
await resourceMetadataDAL.insertMany(
inputSecrets.flatMap(({ key: secretKey, secretMetadata }) => {
if (secretMetadata) {
return secretMetadata.map(({ key, value }) => ({
key,
value,
secretId: newSecretGroupedByKeyName[secretKey][0].id,
orgId
}));
}
return [];
}),
tx
);
if (newSecretTags.length) {
const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx);
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);
@ -141,7 +120,6 @@ export const fnSecretBulkInsert = async ({
[`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id,
[`${TableName.SecretTag}Id` as const]: secret_tagsId
}));
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
}
@ -152,12 +130,10 @@ export const fnSecretBulkUpdate = async ({
tx,
inputSecrets,
folderId,
orgId,
secretDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
resourceMetadataDAL
secretVersionTagDAL
}: TFnSecretBulkUpdate) => {
const sanitizedInputSecrets = inputSecrets.map(
({
@ -255,34 +231,6 @@ export const fnSecretBulkUpdate = async ({
}
}
const inputSecretIdsWithMetadata = inputSecrets
.filter((sec) => Boolean(sec.data.secretMetadata))
.map((sec) => sec.filter.id);
await resourceMetadataDAL.delete(
{
$in: {
secretId: inputSecretIdsWithMetadata
}
},
tx
);
await resourceMetadataDAL.insertMany(
inputSecrets.flatMap(({ filter: { id }, data: { secretMetadata } }) => {
if (secretMetadata) {
return secretMetadata.map(({ key, value }) => ({
key,
value,
secretId: id,
orgId
}));
}
return [];
}),
tx
);
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
};
@ -622,7 +570,6 @@ export const reshapeBridgeSecret = (
color?: string | null;
name: string;
}[];
secretMetadata?: ResourceMetadataDTO;
}
) => ({
secretKey: secret.key,
@ -641,7 +588,6 @@ export const reshapeBridgeSecret = (
secretReminderRepeatDays: secret.reminderRepeatDays,
secretReminderNote: secret.reminderNote,
metadata: secret.metadata,
secretMetadata: secret.secretMetadata,
createdAt: secret.createdAt,
updatedAt: secret.updatedAt
});

@ -18,7 +18,6 @@ import { ActorType } from "../auth/auth-type";
import { TKmsServiceFactory } from "../kms/kms-service";
import { KmsDataKey } from "../kms/kms-types";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { TSecretQueueFactory } from "../secret/secret-queue";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
@ -75,7 +74,6 @@ type TSecretV2BridgeServiceFactoryDep = {
"insertV2Bridge" | "insertApprovalSecretV2Tags"
>;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
@ -97,8 +95,7 @@ export const secretV2BridgeServiceFactory = ({
secretApprovalPolicyService,
secretApprovalRequestDAL,
secretApprovalRequestSecretDAL,
kmsService,
resourceMetadataDAL
kmsService
}: TSecretV2BridgeServiceFactoryDep) => {
const $validateSecretReferences = async (
projectId: string,
@ -144,7 +141,7 @@ export const secretV2BridgeServiceFactory = ({
},
{
operator: "eq",
field: `${TableName.SecretV2}.key` as "key",
field: "key",
value: el.secretKey
}
]
@ -189,7 +186,6 @@ export const secretV2BridgeServiceFactory = ({
actorAuthMethod,
projectId,
secretPath,
secretMetadata,
...inputSecret
}: TCreateSecretDTO) => {
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
@ -259,7 +255,6 @@ export const secretV2BridgeServiceFactory = ({
const secret = await secretDAL.transaction((tx) =>
fnSecretBulkInsert({
folderId,
orgId: actorOrgId,
inputSecrets: [
{
version: 1,
@ -277,11 +272,9 @@ export const secretV2BridgeServiceFactory = ({
key: secretName,
userId: inputSecret.type === SecretType.Personal ? actorId : null,
tagIds: inputSecret.tagIds,
references: nestedReferences,
secretMetadata
references: nestedReferences
}
],
resourceMetadataDAL,
secretDAL,
secretVersionDAL,
secretTagDAL,
@ -294,7 +287,6 @@ export const secretV2BridgeServiceFactory = ({
await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({
secretPath,
orgId: actorOrgId,
actorId,
actor,
projectId,
@ -317,7 +309,6 @@ export const secretV2BridgeServiceFactory = ({
actorAuthMethod,
projectId,
secretPath,
secretMetadata,
...inputSecret
}: TUpdateSecretDTO) => {
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
@ -444,8 +435,6 @@ export const secretV2BridgeServiceFactory = ({
const updatedSecret = await secretDAL.transaction(async (tx) =>
fnSecretBulkUpdate({
folderId,
orgId: actorOrgId,
resourceMetadataDAL,
inputSecrets: [
{
filter: { id: secretId },
@ -459,7 +448,6 @@ export const secretV2BridgeServiceFactory = ({
skipMultilineEncoding: inputSecret.skipMultilineEncoding,
key: inputSecret.newSecretName || secretName,
tags: inputSecret.tagIds,
secretMetadata,
...encryptedValue
}
}
@ -487,7 +475,6 @@ export const secretV2BridgeServiceFactory = ({
actorId,
actor,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
}
@ -575,7 +562,6 @@ export const secretV2BridgeServiceFactory = ({
actorId,
actor,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
}
@ -975,8 +961,8 @@ export const secretV2BridgeServiceFactory = ({
? secretDAL.findOneWithTags({
folderId,
type: secretType,
[`${TableName.SecretV2}.key` as "key"]: secretName,
[`${TableName.SecretV2}.userId` as "userId"]: secretType === SecretType.Personal ? actorId : null
key: secretName,
userId: secretType === SecretType.Personal ? actorId : null
})
: secretVersionDAL
.findOne({
@ -1127,7 +1113,7 @@ export const secretV2BridgeServiceFactory = ({
value: [
{
operator: "eq",
field: `${TableName.SecretV2}.key` as "key",
field: "key",
value: el.secretKey
},
{
@ -1199,14 +1185,11 @@ export const secretV2BridgeServiceFactory = ({
key: el.secretKey,
tagIds: el.tagIds,
references,
secretMetadata: el.secretMetadata,
type: SecretType.Shared
};
}),
folderId,
orgId: actorOrgId,
secretDAL,
resourceMetadataDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
@ -1220,7 +1203,6 @@ export const secretV2BridgeServiceFactory = ({
actorId,
secretPath,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -1272,7 +1254,7 @@ export const secretV2BridgeServiceFactory = ({
value: [
{
operator: "eq",
field: `${TableName.SecretV2}.key` as "key",
field: "key",
value: el.secretKey
},
{
@ -1337,7 +1319,7 @@ export const secretV2BridgeServiceFactory = ({
value: [
{
operator: "eq",
field: `${TableName.SecretV2}.key` as "key",
field: "key",
value: el.secretKey
},
{
@ -1389,7 +1371,6 @@ export const secretV2BridgeServiceFactory = ({
const secrets = await secretDAL.transaction(async (tx) =>
fnSecretBulkUpdate({
folderId,
orgId: actorOrgId,
tx,
inputSecrets: inputSecrets.map((el) => {
const originalSecret = secretsToUpdateInDBGroupedByKey[el.secretKey][0];
@ -1413,7 +1394,6 @@ export const secretV2BridgeServiceFactory = ({
skipMultilineEncoding: el.skipMultilineEncoding,
key: el.newSecretName || el.secretKey,
tags: el.tagIds,
secretMetadata: el.secretMetadata,
...encryptedValue
}
};
@ -1421,8 +1401,7 @@ export const secretV2BridgeServiceFactory = ({
secretDAL,
secretVersionDAL,
secretTagDAL,
secretVersionTagDAL,
resourceMetadataDAL
secretVersionTagDAL
})
);
await snapshotService.performSnapshot(folderId);
@ -1431,7 +1410,6 @@ export const secretV2BridgeServiceFactory = ({
actorId,
secretPath,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -1483,7 +1461,7 @@ export const secretV2BridgeServiceFactory = ({
value: [
{
operator: "eq",
field: `${TableName.SecretV2}.key` as "key",
field: "key",
value: el.secretKey
},
{
@ -1534,7 +1512,6 @@ export const secretV2BridgeServiceFactory = ({
actorId,
secretPath,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -1838,12 +1815,10 @@ export const secretV2BridgeServiceFactory = ({
if (locallyCreatedSecrets.length) {
await fnSecretBulkInsert({
folderId: destinationFolder.id,
orgId: actorOrgId,
secretVersionDAL,
secretDAL,
tx,
secretTagDAL,
resourceMetadataDAL,
secretVersionTagDAL,
inputSecrets: locallyCreatedSecrets.map((doc) => {
return {
@ -1855,7 +1830,6 @@ export const secretV2BridgeServiceFactory = ({
skipMultilineEncoding: doc.skipMultilineEncoding,
reminderNote: doc.reminderNote,
reminderRepeatDays: doc.reminderRepeatDays,
secretMetadata: doc.secretMetadata,
references: doc.value ? getAllSecretReferences(doc.value).nestedReferences : []
};
})
@ -1864,8 +1838,6 @@ export const secretV2BridgeServiceFactory = ({
if (locallyUpdatedSecrets.length) {
await fnSecretBulkUpdate({
folderId: destinationFolder.id,
orgId: actorOrgId,
resourceMetadataDAL,
secretVersionDAL,
secretDAL,
tx,
@ -1883,7 +1855,6 @@ export const secretV2BridgeServiceFactory = ({
encryptedComment: doc.encryptedComment,
skipMultilineEncoding: doc.skipMultilineEncoding,
reminderNote: doc.reminderNote,
secretMetadata: doc.secretMetadata,
reminderRepeatDays: doc.reminderRepeatDays,
...(doc.encryptedValue
? {
@ -1967,7 +1938,6 @@ export const secretV2BridgeServiceFactory = ({
await snapshotService.performSnapshot(destinationFolder.id);
await secretQueueService.syncSecrets({
projectId,
orgId: actorOrgId,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environment.slug,
actorId,
@ -1979,7 +1949,6 @@ export const secretV2BridgeServiceFactory = ({
await snapshotService.performSnapshot(sourceFolder.id);
await secretQueueService.syncSecrets({
projectId,
orgId: actorOrgId,
secretPath: sourceFolder.path,
environmentSlug: sourceFolder.environment.slug,
actorId,

@ -7,8 +7,6 @@ import { SecretsOrderBy } from "@app/services/secret/secret-types";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
@ -60,7 +58,6 @@ export type TCreateSecretDTO = TProjectPermission & {
skipMultilineEncoding?: boolean;
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
secretMetadata?: ResourceMetadataDTO;
};
export type TUpdateSecretDTO = TProjectPermission & {
@ -78,7 +75,6 @@ export type TUpdateSecretDTO = TProjectPermission & {
metadata?: {
source?: string;
};
secretMetadata?: ResourceMetadataDTO;
};
export type TDeleteSecretDTO = TProjectPermission & {
@ -98,7 +94,6 @@ export type TCreateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
secretComment?: string;
skipMultilineEncoding?: boolean;
tagIds?: string[];
secretMetadata?: ResourceMetadataDTO;
metadata?: {
source?: string;
};
@ -118,7 +113,6 @@ export type TUpdateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
tagIds?: string[];
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
secretMetadata?: ResourceMetadataDTO;
}[];
};
@ -142,16 +136,8 @@ export type TSecretReference = { environment: string; secretPath: string; secret
export type TFnSecretBulkInsert = {
folderId: string;
orgId: string;
tx?: Knex;
inputSecrets: Array<
Omit<TSecretsV2Insert, "folderId"> & {
tagIds?: string[];
references: TSecretReference[];
secretMetadata?: ResourceMetadataDTO;
}
>;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
inputSecrets: Array<Omit<TSecretsV2Insert, "folderId"> & { tagIds?: string[]; references: TSecretReference[] }>;
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
@ -170,12 +156,10 @@ type TRequireReferenceIfValue =
export type TFnSecretBulkUpdate = {
folderId: string;
orgId: string;
inputSecrets: {
filter: Partial<TSecretsV2>;
data: TRequireReferenceIfValue & { tags?: string[]; secretMetadata?: ResourceMetadataDTO };
data: TRequireReferenceIfValue & { tags?: string[] };
}[];
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;

@ -749,8 +749,7 @@ export const createManySecretsRawFnFactory = ({
secretVersionV2BridgeDAL,
secretV2BridgeDAL,
secretVersionTagV2BridgeDAL,
kmsService,
resourceMetadataDAL
kmsService
}: TCreateManySecretsRawFnFactory) => {
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
const createManySecretsRawFn = async ({
@ -761,7 +760,7 @@ export const createManySecretsRawFnFactory = ({
userId
}: TCreateManySecretsRawFn) => {
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
const project = await projectDAL.findById(projectId);
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder)
throw new NotFoundError({
@ -815,9 +814,7 @@ export const createManySecretsRawFnFactory = ({
tagIds: el.tags
})),
folderId,
orgId: project.orgId,
secretDAL: secretV2BridgeDAL,
resourceMetadataDAL,
secretVersionDAL: secretVersionV2BridgeDAL,
secretTagDAL,
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
@ -912,7 +909,6 @@ export const updateManySecretsRawFnFactory = ({
secretVersionTagV2BridgeDAL,
secretVersionV2BridgeDAL,
secretV2BridgeDAL,
resourceMetadataDAL,
kmsService
}: TUpdateManySecretsRawFnFactory) => {
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
@ -924,7 +920,6 @@ export const updateManySecretsRawFnFactory = ({
userId
}: TUpdateManySecretsRawFn): Promise<Array<{ id: string }>> => {
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
const project = await projectDAL.findById(projectId);
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder)
@ -993,13 +988,11 @@ export const updateManySecretsRawFnFactory = ({
const updatedSecrets = await secretDAL.transaction(async (tx) =>
fnSecretV2BridgeBulkUpdate({
folderId,
orgId: project.orgId,
tx,
inputSecrets: inputSecrets.map((el) => ({
filter: { id: secretsToUpdateInDBGroupedByKey[el.key][0].id, type: SecretType.Shared },
data: el
})),
resourceMetadataDAL,
secretDAL: secretV2BridgeDAL,
secretVersionDAL: secretVersionV2BridgeDAL,
secretTagDAL,

@ -47,8 +47,6 @@ import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
@ -106,7 +104,6 @@ type TSecretQueueFactoryDep = {
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
export type TGetSecrets = {
@ -123,12 +120,7 @@ export const uniqueSecretQueueKey = (environment: string, secretPath: string) =>
type TIntegrationSecret = Record<
string,
{
value: string;
comment?: string;
skipMultilineEncoding?: boolean | null | undefined;
secretMetadata?: ResourceMetadataDTO;
}
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
>;
// TODO(akhilmhdh): split this into multiple queue
@ -165,8 +157,7 @@ export const secretQueueFactory = ({
auditLogService,
orgService,
projectUserMembershipRoleDAL,
projectKeyDAL,
resourceMetadataDAL
projectKeyDAL
}: TSecretQueueFactoryDep) => {
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
@ -315,8 +306,7 @@ export const secretQueueFactory = ({
kmsService,
secretVersionV2BridgeDAL,
secretV2BridgeDAL,
secretVersionTagV2BridgeDAL,
resourceMetadataDAL
secretVersionTagV2BridgeDAL
});
const updateManySecretsRawFn = updateManySecretsRawFnFactory({
@ -331,8 +321,7 @@ export const secretQueueFactory = ({
kmsService,
secretVersionV2BridgeDAL,
secretV2BridgeDAL,
secretVersionTagV2BridgeDAL,
resourceMetadataDAL
secretVersionTagV2BridgeDAL
});
/**
@ -383,7 +372,6 @@ export const secretQueueFactory = ({
}
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
content[secretKey].secretMetadata = secret.secretMetadata;
})
);
@ -409,8 +397,7 @@ export const secretQueueFactory = ({
content[importedSecret.key] = {
skipMultilineEncoding: importedSecret.skipMultilineEncoding,
comment: importedSecret.secretComment,
value: importedSecret.secretValue || "",
secretMetadata: importedSecret.secretMetadata
value: importedSecret.secretValue || ""
};
}
}
@ -610,7 +597,6 @@ export const secretQueueFactory = ({
_depth: depth,
secretPath,
projectId,
orgId,
environmentSlug: environment,
excludeReplication,
actorId,
@ -639,7 +625,6 @@ export const secretQueueFactory = ({
_deDupeReplicationQueue: deDupeReplicationQueue,
_depth: depth,
projectId,
orgId,
secretPath,
actorId,
actor,
@ -696,7 +681,6 @@ export const secretQueueFactory = ({
if (!folder) {
throw new Error("Secret path not found");
}
const project = await projectDAL.findById(projectId);
// find all imports made with the given environment and secret path
const linkSourceDto = {
@ -731,7 +715,6 @@ export const secretQueueFactory = ({
.map(({ folderId }) =>
syncSecrets({
projectId,
orgId: project.orgId,
secretPath: foldersGroupedById[folderId][0]?.path as string,
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
_deDupeQueue: deDupeQueue,
@ -784,7 +767,6 @@ export const secretQueueFactory = ({
.map((folderId) =>
syncSecrets({
projectId,
orgId: project.orgId,
secretPath: referencedFoldersGroupedById[folderId][0]?.path as string,
environmentSlug: referencedFoldersGroupedById[folderId][0]?.environmentSlug as string,
_deDupeQueue: deDupeQueue,

@ -288,7 +288,6 @@ export const secretServiceFactory = ({
actorId,
actor,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
}
@ -430,7 +429,6 @@ export const secretServiceFactory = ({
await snapshotService.performSnapshot(folderId);
await secretQueueService.syncSecrets({
secretPath: path,
orgId: actorOrgId,
actorId,
actor,
projectId,
@ -528,7 +526,6 @@ export const secretServiceFactory = ({
actorId,
actor,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
}
@ -823,7 +820,6 @@ export const secretServiceFactory = ({
actorId,
secretPath: path,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -932,7 +928,6 @@ export const secretServiceFactory = ({
actorId,
secretPath: path,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -1019,7 +1014,6 @@ export const secretServiceFactory = ({
actorId,
secretPath: path,
projectId,
orgId: actorOrgId,
environmentSlug: folder.environment.slug
});
@ -1391,8 +1385,7 @@ export const secretServiceFactory = ({
skipMultilineEncoding,
tagIds,
secretReminderNote,
secretReminderRepeatDays,
secretMetadata
secretReminderRepeatDays
}: TCreateSecretRawDTO) => {
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
const policy =
@ -1419,8 +1412,7 @@ export const secretServiceFactory = ({
secretValue,
tagIds,
reminderNote: secretReminderNote,
reminderRepeatDays: secretReminderRepeatDays,
secretMetadata
reminderRepeatDays: secretReminderRepeatDays
}
]
}
@ -1443,8 +1435,7 @@ export const secretServiceFactory = ({
tagIds,
secretReminderNote,
skipMultilineEncoding,
secretReminderRepeatDays,
secretMetadata
secretReminderRepeatDays
});
return { secret, type: SecretProtectionType.Direct as const };
}
@ -1534,8 +1525,7 @@ export const secretServiceFactory = ({
secretReminderRepeatDays,
metadata,
secretComment,
newSecretName,
secretMetadata
newSecretName
}: TUpdateSecretRawDTO) => {
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
const policy =
@ -1563,8 +1553,7 @@ export const secretServiceFactory = ({
secretValue,
tagIds,
reminderNote: secretReminderNote,
reminderRepeatDays: secretReminderRepeatDays,
secretMetadata
reminderRepeatDays: secretReminderRepeatDays
}
]
}
@ -1588,8 +1577,7 @@ export const secretServiceFactory = ({
secretName,
newSecretName,
metadata,
secretValue,
secretMetadata
secretValue
});
return { type: SecretProtectionType.Direct as const, secret };
}
@ -1805,8 +1793,7 @@ export const secretServiceFactory = ({
secretComment: el.secretComment,
metadata: el.metadata,
skipMultilineEncoding: el.skipMultilineEncoding,
secretKey: el.secretKey,
secretMetadata: el.secretMetadata
secretKey: el.secretKey
}))
}
});
@ -1932,8 +1919,7 @@ export const secretServiceFactory = ({
secretValue: el.secretValue,
secretComment: el.secretComment,
skipMultilineEncoding: el.skipMultilineEncoding,
secretKey: el.secretKey,
secretMetadata: el.secretMetadata
secretKey: el.secretKey
}))
}
});
@ -2276,7 +2262,6 @@ export const secretServiceFactory = ({
await secretQueueService.syncSecrets({
secretPath,
projectId: project.id,
orgId: project.orgId,
environmentSlug: environment,
excludeReplication: true
});
@ -2385,7 +2370,6 @@ export const secretServiceFactory = ({
await secretQueueService.syncSecrets({
secretPath,
projectId: project.id,
orgId: project.orgId,
environmentSlug: environment,
excludeReplication: true
});
@ -2844,7 +2828,6 @@ export const secretServiceFactory = ({
await snapshotService.performSnapshot(destinationFolder.id);
await secretQueueService.syncSecrets({
projectId: project.id,
orgId: project.orgId,
secretPath: destinationFolder.path,
environmentSlug: destinationFolder.environment.slug,
actorId,
@ -2856,7 +2839,6 @@ export const secretServiceFactory = ({
await snapshotService.performSnapshot(sourceFolder.id);
await secretQueueService.syncSecrets({
projectId: project.id,
orgId: project.orgId,
secretPath: sourceFolder.path,
environmentSlug: sourceFolder.environment.slug,
actorId,

@ -14,8 +14,6 @@ import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
import { ActorType } from "../auth/auth-type";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
@ -213,7 +211,6 @@ export type TCreateSecretRawDTO = TProjectPermission & {
skipMultilineEncoding?: boolean;
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
secretMetadata?: ResourceMetadataDTO;
};
export type TUpdateSecretRawDTO = TProjectPermission & {
@ -231,7 +228,6 @@ export type TUpdateSecretRawDTO = TProjectPermission & {
metadata?: {
source?: string;
};
secretMetadata?: ResourceMetadataDTO;
};
export type TDeleteSecretRawDTO = TProjectPermission & {
@ -252,7 +248,6 @@ export type TCreateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
secretComment?: string;
skipMultilineEncoding?: boolean;
tagIds?: string[];
secretMetadata?: ResourceMetadataDTO;
metadata?: {
source?: string;
};
@ -271,7 +266,6 @@ export type TUpdateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
secretComment?: string;
skipMultilineEncoding?: boolean;
tagIds?: string[];
secretMetadata?: ResourceMetadataDTO;
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
}[];
@ -299,13 +293,7 @@ export type TSecretReference = { environment: string; secretPath: string };
export type TFnSecretBulkInsert = {
folderId: string;
tx?: Knex;
inputSecrets: Array<
Omit<TSecretsInsert, "folderId"> & {
tags?: string[];
references?: TSecretReference[];
secretMetadata?: ResourceMetadataDTO;
}
>;
inputSecrets: Array<Omit<TSecretsInsert, "folderId"> & { tags?: string[]; references?: TSecretReference[] }>;
secretDAL: Pick<TSecretDALFactory, "insertMany" | "upsertSecretReferences">;
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
@ -401,7 +389,6 @@ export type TCreateManySecretsRawFnFactory = {
>;
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
};
export type TCreateManySecretsRawFn = {
@ -438,7 +425,6 @@ export type TUpdateManySecretsRawFnFactory = {
>;
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
export type TUpdateManySecretsRawFn = {
@ -474,7 +460,6 @@ export type TSyncSecretsDTO<T extends boolean = false> = {
_depth?: number;
secretPath: string;
projectId: string;
orgId: string;
environmentSlug: string;
// cases for just doing sync integration and webhook
excludeReplication?: T;

@ -4,6 +4,13 @@ sidebarTitle: "Overview"
description: "Learn how to generate secrets dynamically on-demand."
---
<Info>
Note that Dynamic Secrets is a paid feature.
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**
If you're self-hosting Infisical, then you should contact sales@infisical.com to purchase an enterprise license to use it.
</Info>
## Introduction
Contrary to static key-value secrets, which require manual input of data into the secure Infisical storage, **dynamic secrets are generated on-demand upon access**.

@ -3,6 +3,13 @@ title: "Approval Workflows"
description: "Learn how to enable a set of policies to manage changes to sensitive secrets and environments."
---
<Info>
Approval Workflows is a paid feature.
If you're using Infisical Cloud, then it is available under the **Pro Tier** and **Enterprise Tire**.
If you're self-hosting Infisical, then you should contact sales@infisical.com to purchase an enterprise license to use it.
</Info>
## Problem at hand
Updating secrets in high-stakes environments (e.g., production) can have a number of problematic issues:
@ -40,4 +47,4 @@ When a user submits a change to an enviropnment that is under a particular polic
Approvers are notified by email and/or Slack as soon as the request is initiated. In the Infisical Dashboard, they will be able to `approve` and `merge` (or `deny`) a request for a change in a particular environment. After that, depending on the workflows setup, the change will be automatically propagated to the right applications (e.g., using [Infisical Kubernetes Operator](https://infisical.com/docs/integrations/platforms/kubernetes)).
![secrets update pull request](../../images/platform/pr-workflows/secret-update-pr.png)
![secrets update pull request](../../images/platform/pr-workflows/secret-update-pr.png)

@ -15,7 +15,7 @@ Prerequisites:
<Steps>
<Step title="Create a SCIM token in Infisical">
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
press the **Enable SCIM provisioning** toggle to allow Azure to provision/deprovision users for your organization.
![SCIM enable provisioning](/images/platform/scim/scim-enable-provisioning.png)

@ -15,7 +15,7 @@ Prerequisites:
<Steps>
<Step title="Create a SCIM token in Infisical">
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
press the **Enable SCIM provisioning** toggle to allow JumpCloud to provision/deprovision users and user groups for your organization.
![SCIM enable provisioning](/images/platform/scim/scim-enable-provisioning.png)

@ -15,7 +15,7 @@ Prerequisites:
<Steps>
<Step title="Create a SCIM token in Infisical">
In Infisical, head to your Organization Settings > Authentication > SCIM Configuration and
In Infisical, head to your Organization Settings > Security > SCIM Configuration and
press the **Enable SCIM provisioning** toggle to allow Okta to provision/deprovision users and user groups for your organization.
![SCIM enable provisioning](/images/platform/scim/scim-enable-provisioning.png)

@ -0,0 +1,93 @@
---
title: "Auth0 SAML"
description: "Learn how to configure Auth0 SAML for Infisical SSO."
---
<Info>
Auth0 SAML SSO feature is a paid feature. If you're using Infisical Cloud,
then it is available under the **Pro Tier**. If you're self-hosting Infisical,
then you should contact sales@infisical.com to purchase an enterprise license
to use it.
</Info>
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Auth0, then click **Connect** again.
Next, note the **Application Callback URL** and **Audience** to use when configuring the Auth0 SAML application.
![Auth0 SAML initial configuration](../../../images/sso/auth0-saml/init-config.png)
</Step>
<Step title="Create a SAML application in Auth0">
2.1. In your Auth0 account, head to Applications and create an application.
![Auth0 SAML app creation](../../../images/sso/auth0-saml/create-application.png)
Select **Regular Web Application** and press **Create**.
![Auth0 SAML app creation](../../../images/sso/auth0-saml/create-application-2.png)
2.2. In the Application head to Settings > Application URIs and add the **Application Callback URL** from step 1 into the **Allowed Callback URLs** field.
![Auth0 SAML allowed callback URLs](../../../images/sso/auth0-saml/auth0-config.png)
2.3. In the Application head to Addons > SAML2 Web App and copy the **Issuer**, **Identity Provider Login URL**, and **Identity Provider Certificate** from the **Usage** tab.
![Auth0 SAML config](../../../images/sso/auth0-saml/auth0-config-2.png)
2.4. Back in Infisical, set **Issuer**, **Identity Provider Login URL**, and **Certificate** to the corresponding items from step 2.3.
![Auth0 SAML Infisical config](../../../images/sso/auth0-saml/infisical-config.png)
2.5. Back in Auth0, in the **Settings** tab, set the **Application Callback URL** to the **Application Callback URL** from step 1
and update the **Settings** field with the JSON under the picture below (replacing `<audience-from-infisical>` with the **Audience** from step 1).
![Auth0 SAML config](../../../images/sso/auth0-saml/auth0-config-3.png)
```json
{
"audience": "<audience-from-infisical>",
"mappings": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email",
"given_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/firstName",
"family_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/lastName"
},
"signatureAlgorithm": "rsa-sha256",
"digestAlgorithm": "sha256",
"signResponse": true
}
```
Click **Save**.
</Step>
<Step title="Enable SAML SSO in Infisical">
Enabling SAML SSO allows members in your organization to log into Infisical via Auth0.
![Auth0 SAML enable](../../../images/sso/auth0-saml/enable-saml.png)
</Step>
<Step title="Enforce SAML SSO in Infisical">
Enforcing SAML SSO ensures that members in your organization can only access Infisical
by logging into the organization via Auth0.
To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Auth0 user with Infisical;
Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO.
</Step>
</Steps>
<Tip>
If you are only using one organization on your Infisical instance, you can configure a default organization in the [Server Admin Console](../admin-panel/server-admin#default-organization) to expedite SAML login.
</Tip>
<Note>
If you're configuring SAML SSO on a self-hosted instance of Infisical, make
sure to set the `AUTH_SECRET` and `SITE_URL` environment variable for it to
work:
<div class="height:1px;"/>
- `AUTH_SECRET`: A secret key used for signing and verifying JWT. This
can be a random 32-byte base64 string generated with `openssl rand -base64
32`.
<div class="height:1px;"/>
- `SITE_URL`: The absolute URL of your self-hosted instance of Infisical including the protocol (e.g. https://app.infisical.com)
</Note>

@ -12,7 +12,7 @@ description: "Learn how to configure Microsoft Entra ID for Infisical SSO."
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Azure / Entra, then click **Connect** again.
Next, copy the **Reply URL (Assertion Consumer Service URL)** and **Identifier (Entity ID)** to use when configuring the Azure SAML application.

@ -12,7 +12,7 @@ description: "Learn how to configure Google SAML for Infisical SSO."
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Google, then click **Connect** again.
Next, note the **ACS URL** and **SP Entity ID** to use when configuring the Google SAML application.

@ -12,7 +12,7 @@ description: "Learn how to configure JumpCloud SAML for Infisical SSO."
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select JumpCloud, then click **Connect** again.
Next, copy the **ACS URL** and **SP Entity ID** to use when configuring the JumpCloud SAML application.

@ -12,7 +12,7 @@ description: "Learn how to configure Keycloak SAML for Infisical SSO."
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Manage**.
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Keycloak, then click **Connect** again.
![Keycloak SAML organization security section](../../../images/sso/keycloak/org-security-section.png)

@ -12,7 +12,7 @@ description: "Learn how to configure Okta SAML 2.0 for Infisical SSO."
<Steps>
<Step title="Prepare the SAML SSO configuration in Infisical">
In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Set up SAML SSO**.
In Infisical, head to Organization Settings > Security and click **Connect** for SAML under the Connect to an Identity Provider section. Select Okta, then click **Connect** again.
Next, copy the **Single sign-on URL** and **Audience URI (SP Entity ID)** to use when configuring the Okta SAML 2.0 application.
![Okta SAML initial configuration](../../../images/sso/okta/init-config.png)

@ -28,6 +28,7 @@ Infisical supports these and many other identity providers:
- [JumpCloud SAML](/documentation/platform/sso/jumpcloud)
- [Keycloak SAML](/documentation/platform/sso/keycloak-saml)
- [Google SAML](/documentation/platform/sso/google-saml)
- [Auth0 SAML](/documentation/platform/sso/auth0-saml)
- [Keycloak OIDC](/documentation/platform/sso/keycloak-oidc)
- [Auth0 OIDC](/documentation/platform/sso/auth0-oidc)
- [General OIDC](/documentation/platform/sso/general-oidc)

Binary file not shown.

Before

(image error) Size: 580 KiB

After

(image error) Size: 74 KiB

Binary file not shown.

After

(image error) Size: 612 KiB

Binary file not shown.

After

(image error) Size: 366 KiB

Binary file not shown.

After

(image error) Size: 431 KiB

Binary file not shown.

After

(image error) Size: 352 KiB

Binary file not shown.

After

(image error) Size: 253 KiB

Binary file not shown.

After

(image error) Size: 605 KiB

Binary file not shown.

After

(image error) Size: 605 KiB

Binary file not shown.

After

(image error) Size: 539 KiB

@ -17,7 +17,6 @@ Prerequisites:
If your instance is deployed on AWS, the aws-sdk will automatically retrieve the credentials. Ensure that you assign the provided permission policy to your deployed instance, such as ECS or EC2.
The following steps are for instances not deployed on AWS
<Steps>
<Step title="Create an IAM User">
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
@ -41,10 +40,9 @@ The following steps are for instances not deployed on AWS
<Step title="Obtain the IAM User Credentials">
Obtain the AWS access key ID and secret access key for your IAM User by navigating to IAM > Users > [Your User] > Security credentials > Access keys.
![Access Key Step 1](../../images/integrations/aws/integrations-aws-access-key-1.png)
![Access Key Step 2](../../images/integrations/aws/integrations-aws-access-key-2.png)
![Access Key Step 3](../../images/integrations/aws/integrations-aws-access-key-3.png)
![Access Key Step 1](../../images/integrations/aws/integrations-aws-access-key-1.png)
![Access Key Step 2](../../images/integrations/aws/integrations-aws-access-key-2.png)
![Access Key Step 3](../../images/integrations/aws/integrations-aws-access-key-3.png)
</Step>
<Step title="Set Up Integration Keys">
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
@ -61,7 +59,6 @@ The following steps are for instances not deployed on AWS
2. Select **AWS Account** as the **Trusted Entity Type**.
3. Choose **Another AWS Account** and enter **381492033652** (Infisical AWS Account ID). This restricts the role to be assumed only by Infisical. If self-hosting, provide your AWS account number instead.
4. Optionally, enable **Require external ID** and enter your **project ID** to further enhance security.
</Step>
<Step title="Add Required Permissions for the IAM Role">
@ -92,13 +89,11 @@ The following steps are for instances not deployed on AWS
]
}
```
</Step>
<Step title="Copy the AWS IAM Role ARN">
![Copy IAM Role
ARN](../../images/integrations/aws/integration-aws-iam-assume-arn.png)
</Step>
<Step title="Copy the AWS IAM Role ARN">
![Copy IAM Role ARN](../../images/integrations/aws/integration-aws-iam-assume-arn.png)
</Step>
<Step title="Authorize Infisical for AWS Secrets Manager">
1. Navigate to your project's integrations tab in Infisical.
@ -109,7 +104,6 @@ The following steps are for instances not deployed on AWS
![Select Assume Role](../../images/integrations/aws/integration-aws-iam-assume-select.png)
4. Provide the **AWS IAM Role ARN** obtained from the previous step.
</Step> <Step title="Start integration">
Select how you want to integration to work by specifying a number of parameters:
@ -133,12 +127,6 @@ The following steps are for instances not deployed on AWS
Optionally, you can add tags or specify the encryption key of all the secrets created via this integration:
<ParamField path="Tag Sync Mode" type="string" optional>
The sync mode for AWS tags. The supported options are `Secret Metadata` and `Custom`. If `Secret Metadata` is selected,
the metadata of the Infisical secrets are used as tags in AWS. If custom is selected, then the key/value of the **Secret Tag** field is used. `Secret Metadata` mode
is only supported for one-to-one integrations.
</ParamField>
<ParamField path="Secret Tag" type="string" optional>
The Key/Value of a tag that will be added to secrets in AWS. Please note that it is possible to add multiple tags via API.
</ParamField>

@ -248,6 +248,7 @@
"documentation/platform/sso/jumpcloud",
"documentation/platform/sso/keycloak-saml",
"documentation/platform/sso/google-saml",
"documentation/platform/sso/auth0-saml",
"documentation/platform/sso/keycloak-oidc",
"documentation/platform/sso/auth0-oidc",
"documentation/platform/sso/general-oidc"

@ -101,6 +101,10 @@ export const ROUTE_PATHS = Object.freeze({
"/secret-manager/$projectId/integrations/azure-devops/create",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/create"
),
AzureKeyVaultAuthorizePage: setRoute(
"/secret-manager/$projectId/integrations/azure-key-vault/authorize",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize"
),
AzureKeyVaultOauthCallbackPage: setRoute(
"/secret-manager/$projectId/integrations/azure-key-vault/oauth2/callback",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/oauth2/callback"

@ -4,12 +4,7 @@ import { createNotification } from "@app/components/notifications";
import { apiRequest } from "@app/config/request";
import { workspaceKeys } from "../workspace";
import {
IntegrationMetadataSyncMode,
TCloudIntegration,
TIntegrationWithEnv,
TOctopusDeployScopeValues
} from "./types";
import { TCloudIntegration, TIntegrationWithEnv, TOctopusDeployScopeValues } from "./types";
export const integrationQueryKeys = {
getIntegrations: () => ["integrations"] as const,
@ -93,7 +88,6 @@ export const useCreateIntegration = () => {
shouldProtectSecrets?: boolean;
shouldEnableDelete?: boolean;
octopusDeployScopeValues?: TOctopusDeployScopeValues;
metadataSyncMode?: IntegrationMetadataSyncMode;
};
}) => {
const {

@ -62,7 +62,6 @@ export type TIntegration = {
octopusDeployScopeValues?: TOctopusDeployScopeValues;
awsIamRole?: string;
region?: string;
metadataSyncMode?: IntegrationMetadataSyncMode;
};
};
@ -93,8 +92,3 @@ export enum IntegrationMappingBehavior {
ONE_TO_ONE = "one-to-one",
MANY_TO_ONE = "many-to-one"
}
export enum IntegrationMetadataSyncMode {
CUSTOM = "custom",
SECRET_METADATA = "secret-metadata"
}

@ -82,7 +82,6 @@ export type TSecretApprovalRequest = {
conflicts: Array<{ secretId: string; op: CommitType.UPDATE }>;
commits: ({
// if there is no secret means it was creation
secretMetadata?: { key: string; value: string }[];
secret?: { version: number };
secretVersion: SecretV3Raw;
// if there is no new version its for Delete

@ -84,8 +84,7 @@ export const useUpdateSecretV3 = ({
secretReminderRepeatDays,
secretReminderNote,
newSecretName,
skipMultilineEncoding,
secretMetadata
skipMultilineEncoding
}) => {
const { data } = await apiRequest.patch(`/api/v3/secrets/raw/${secretKey}`, {
workspaceId,
@ -98,8 +97,7 @@ export const useUpdateSecretV3 = ({
newSecretName,
secretComment,
tagIds,
secretValue,
secretMetadata
secretValue
});
return data;
},

@ -67,8 +67,7 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => {
updatedAt: el.updatedAt,
version: el.version,
skipMultilineEncoding: el.skipMultilineEncoding,
path: el.secretPath,
secretMetadata: el.secretMetadata
path: el.secretPath
};
if (el.type === SecretType.Personal) {

@ -48,7 +48,6 @@ export type SecretV3RawSanitized = {
overrideAction?: string;
folderId?: string;
skipMultilineEncoding?: boolean;
secretMetadata?: { key: string; value: string }[];
};
export type SecretV3Raw = {
@ -64,7 +63,6 @@ export type SecretV3Raw = {
secretComment?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
secretMetadata?: { key: string; value: string }[];
skipMultilineEncoding?: boolean;
metadata?: Record<string, string>;
tags?: WsTag[];
@ -150,7 +148,6 @@ export type TUpdateSecretsV3DTO = {
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
tagIds?: string[];
secretMetadata?: { key: string; value: string }[];
};
export type TDeleteSecretsV3DTO = {

@ -25,7 +25,8 @@ enum AuthProvider {
AZURE_SAML = "azure-saml",
JUMPCLOUD_SAML = "jumpcloud-saml",
KEYCLOAK_SAML = "keycloak-saml",
GOOGLE_SAML = "google-saml"
GOOGLE_SAML = "google-saml",
AUTH0_SAML = "auth0-saml"
}
const ssoAuthProviders = [
@ -33,7 +34,8 @@ const ssoAuthProviders = [
{ label: "Azure / Entra SAML", value: AuthProvider.AZURE_SAML },
{ label: "JumpCloud SAML", value: AuthProvider.JUMPCLOUD_SAML },
{ label: "Keycloak SAML", value: AuthProvider.KEYCLOAK_SAML },
{ label: "Google SAML", value: AuthProvider.GOOGLE_SAML }
{ label: "Google SAML", value: AuthProvider.GOOGLE_SAML },
{ label: "Auth0 SAML", value: AuthProvider.AUTH0_SAML }
];
const schema = z
@ -191,6 +193,15 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle, hideDelet
issuer: "Issuer",
issuerPlaceholder: window.origin
};
case AuthProvider.AUTH0_SAML:
return {
acsUrl: "Application Callback URL",
entityId: "Audience",
entryPoint: "Identity Provider Login URL",
entryPointPlaceholder: "https://xxx.auth0.com/samlp/xxx",
issuer: "Issuer",
issuerPlaceholder: "urn:xxx-xxx.us.auth0.com"
};
default:
return {
acsUrl: "ACS URL",

@ -32,8 +32,7 @@ const metadataMappings: Record<keyof NonNullable<TIntegrationWithEnv["metadata"]
shouldEnableDelete: "GitHub Secret Deletion Enabled",
octopusDeployScopeValues: "Octopus Deploy Scope Values",
awsIamRole: "AWS IAM Role",
region: "Region",
metadataSyncMode: "Metadata Sync Mode"
region: "Region"
} as const;
export const IntegrationSettingsSection = ({ integration }: Props) => {

@ -80,8 +80,16 @@ export const redirectForProviderAuth = (
createIntegrationMissingEnvVarsNotification(integrationOption.slug);
return;
}
const link = `https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
window.location.assign(link);
navigate({
to: "/secret-manager/$projectId/integrations/azure-key-vault/authorize",
params: {
projectId
},
search: {
clientId: integrationOption.clientId,
state,
}
});
break;
}
case "azure-app-configuration": {

@ -1,4 +1,4 @@
import { faExclamationTriangle, faInfo, faKey } from "@fortawesome/free-solid-svg-icons";
import { faExclamationTriangle, faInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
@ -18,10 +18,7 @@ import { CommitType, SecretV3Raw, TSecretApprovalSecChange, WsTag } from "@app/h
export type Props = {
op: CommitType;
secretVersion?: SecretV3Raw;
newVersion?: Omit<TSecretApprovalSecChange, "tags"> & {
tags?: WsTag[];
secretMetadata?: { key: string; value: string }[];
};
newVersion?: Omit<TSecretApprovalSecChange, "tags"> & { tags?: WsTag[] };
presentSecretVersionNumber: number;
hasMerged?: boolean;
conflicts: Array<{ secretId: string; op: CommitType }>;
@ -83,12 +80,11 @@ export const SecretApprovalRequestChangeItem = ({
<Table>
<THead>
<Tr>
{op === CommitType.UPDATE && <Th className="w-12 shrink-0" />}
<Th className="w-48 shrink-0">Secret</Th>
<Th className="min-w-0 flex-1">Value</Th>
<Th className="w-24 shrink-0">Comment</Th>
<Th className="w-24 shrink-0">Tags</Th>
<Th className="w-40 shrink-0">Metadata</Th>
{op === CommitType.UPDATE && <Th className="w-12" />}
<Th className="min-table-row">Secret</Th>
<Th>Value</Th>
<Th className="min-table-row">Comment</Th>
<Th className="min-table-row">Tags</Th>
</Tr>
</THead>
{op === CommitType.UPDATE ? (
@ -114,33 +110,6 @@ export const SecretApprovalRequestChangeItem = ({
</Tag>
))}
</Td>
<Td>
{secretVersion?.secretMetadata?.length ? (
<div className="mt-1 flex flex-wrap gap-2 text-sm text-mineshaft-300">
{secretVersion.secretMetadata?.map((el) => (
<div key={el.key} className="flex items-center">
<Tag
size="xs"
className="mr-0 flex items-center rounded-r-none border border-mineshaft-500"
>
<FontAwesomeIcon icon={faKey} size="xs" className="mr-1" />
<div>{el.key}</div>
</Tag>
<Tag
size="xs"
className="flex items-center rounded-l-none border border-mineshaft-500 bg-mineshaft-900 pl-1"
>
<div className="max-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap">
{el.value}
</div>
</Tag>
</div>
))}
</div>
) : (
<p className="text-sm text-mineshaft-300">-</p>
)}
</Td>
</Tr>
<Tr>
<Td className="text-green-600">NEW</Td>
@ -163,33 +132,6 @@ export const SecretApprovalRequestChangeItem = ({
</Tag>
))}
</Td>
<Td>
{newVersion?.secretMetadata?.length ? (
<div className="mt-1 flex flex-wrap gap-2 text-sm text-mineshaft-300">
{newVersion.secretMetadata?.map((el) => (
<div key={el.key} className="flex items-center">
<Tag
size="xs"
className="mr-0 flex items-center rounded-r-none border border-mineshaft-500"
>
<FontAwesomeIcon icon={faKey} size="xs" className="mr-1" />
<div>{el.key}</div>
</Tag>
<Tag
size="xs"
className="flex items-center rounded-l-none border border-mineshaft-500 bg-mineshaft-900 pl-1"
>
<div className="max-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap">
{el.value}
</div>
</Tag>
</div>
))}
</div>
) : (
<p className="text-sm text-mineshaft-300">-</p>
)}
</Td>
</Tr>
</TBody>
) : (
@ -231,33 +173,6 @@ export const SecretApprovalRequestChangeItem = ({
)
)}
</Td>
<Td>
{newVersion?.secretMetadata?.length ? (
<div className="mt-1 flex flex-wrap gap-2 text-sm text-mineshaft-300">
{newVersion.secretMetadata?.map((el) => (
<div key={el.key} className="flex items-center">
<Tag
size="xs"
className="mr-0 flex items-center rounded-r-none border border-mineshaft-500"
>
<FontAwesomeIcon icon={faKey} size="xs" className="mr-1" />
<div>{el.key}</div>
</Tag>
<Tag
size="xs"
className="flex items-center rounded-l-none border border-mineshaft-500 bg-mineshaft-900 pl-1"
>
<div className="max-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap">
{el.value}
</div>
</Tag>
</div>
))}
</div>
) : (
<p className="text-sm text-mineshaft-300">-</p>
)}
</Td>
</Tr>
</TBody>
)}

@ -8,8 +8,7 @@ import {
faClock,
faPlus,
faShare,
faTag,
faTrash
faTag
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
@ -27,7 +26,6 @@ import {
DropdownMenuLabel,
DropdownMenuTrigger,
FormControl,
FormLabel,
IconButton,
Input,
Switch,
@ -89,16 +87,11 @@ export const SecretDetailSidebar = ({
const { permission } = useProjectPermission();
const tagFields = useFieldArray({
const { fields, append, remove } = useFieldArray({
control,
name: "tags"
});
const metadataFormFields = useFieldArray({
control,
name: "secretMetadata"
});
const secretKey = secret?.key || "";
const selectedTags = watch("tags", []) || [];
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
@ -160,10 +153,10 @@ export const SecretDetailSidebar = ({
if (selectedTagsGroupById?.[tag.id]) {
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
if (tagPos !== -1) {
tagFields.remove(tagPos);
remove(tagPos);
}
} else {
tagFields.append(tag);
append(tag);
}
};
@ -284,73 +277,9 @@ export const SecretDetailSidebar = ({
)}
/>
)}
<FormControl label="Metadata">
<div className="flex flex-col space-y-2">
{metadataFormFields.fields.map(({ id: metadataFieldId }, i) => (
<div key={metadataFieldId} className="flex items-end space-x-2">
<div className="flex-grow">
{i === 0 && <span className="text-xs text-mineshaft-400">Key</span>}
<Controller
control={control}
name={`secretMetadata.${i}.key`}
render={({ field, fieldState: { error } }) => (
<FormControl
isError={Boolean(error?.message)}
errorText={error?.message}
className="mb-0"
>
<Input {...field} className="max-h-8" />
</FormControl>
)}
/>
</div>
<div className="flex-grow">
{i === 0 && (
<FormLabel
label="Value"
className="text-xs text-mineshaft-400"
isOptional
/>
)}
<Controller
control={control}
name={`secretMetadata.${i}.value`}
render={({ field, fieldState: { error } }) => (
<FormControl
isError={Boolean(error?.message)}
errorText={error?.message}
className="mb-0"
>
<Input {...field} className="max-h-8" />
</FormControl>
)}
/>
</div>
<IconButton
ariaLabel="delete key"
className="bottom-0.5 max-h-8"
variant="outline_bg"
onClick={() => metadataFormFields.remove(i)}
>
<FontAwesomeIcon icon={faTrash} />
</IconButton>
</div>
))}
<div className="mt-2">
<Button
leftIcon={<FontAwesomeIcon icon={faPlus} />}
size="xs"
variant="outline_bg"
onClick={() => metadataFormFields.append({ key: "", value: "" })}
>
Add Key
</Button>
</div>
</div>
</FormControl>
<FormControl label="Tags" className="">
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
{tagFields.fields.map(({ tagColor, id: formId, slug, id }) => (
{fields.map(({ tagColor, id: formId, slug, id }) => (
<Tag
className="flex w-min items-center space-x-2"
key={formId}

@ -78,8 +78,7 @@ export const SecretListView = ({
tags,
skipMultilineEncoding,
newKey,
secretId,
secretMetadata
secretId
}: Partial<{
value: string;
comment: string;
@ -89,7 +88,6 @@ export const SecretListView = ({
skipMultilineEncoding: boolean;
newKey: string;
secretId: string;
secretMetadata?: { key: string; value: string }[];
}> = {}
) => {
if (operation === "delete") {
@ -117,8 +115,7 @@ export const SecretListView = ({
secretReminderRepeatDays: reminderRepeatDays,
secretReminderNote: reminderNote,
skipMultilineEncoding,
newSecretName: newKey,
secretMetadata
newSecretName: newKey
});
return;
}
@ -141,10 +138,7 @@ export const SecretListView = ({
const handleSaveSecret = useCallback(
async (
orgSecret: SecretV3RawSanitized,
modSecret: Omit<SecretV3RawSanitized, "tags"> & {
tags?: { id: string }[];
secretMetadata?: { key: string; value: string }[];
},
modSecret: Omit<SecretV3RawSanitized, "tags"> & { tags?: { id: string }[] },
cb?: () => void
) => {
const { key: oldKey } = orgSecret;
@ -157,8 +151,7 @@ export const SecretListView = ({
tags,
comment,
reminderRepeatDays,
reminderNote,
secretMetadata
reminderNote
} = modSecret;
const hasKeyChanged = oldKey !== key && key;
@ -173,8 +166,7 @@ export const SecretListView = ({
"comment",
"skipMultilineEncoding",
"reminderRepeatDays",
"reminderNote",
"secretMetadata"
"reminderNote"
] as const
).every((el) => orgSecret[el] === modSecret[el]) && isSameTags;
@ -207,8 +199,7 @@ export const SecretListView = ({
reminderNote,
secretId: orgSecret.id,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding,
secretMetadata
skipMultilineEncoding: modSecret.skipMultilineEncoding
});
if (cb) cb();
}

@ -47,13 +47,7 @@ export const formSchema = z.object({
.nullable()
.optional(),
reminderNote: z.string().trim().nullable().optional(),
secretMetadata: z
.object({
key: z.string().trim().min(1),
value: z.string().trim().default("")
})
.array()
.optional(),
tags: z
.object({
id: z.string(),

@ -34,10 +34,7 @@ import { useWorkspace } from "@app/context";
import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthById } from "@app/hooks/api/integrationAuth";
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
import {
IntegrationMappingBehavior,
IntegrationMetadataSyncMode
} from "@app/hooks/api/integrations/types";
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
enum TabSections {
Connection = "connection",
@ -97,7 +94,6 @@ const schema = z
mappingBehavior: z.nativeEnum(IntegrationMappingBehavior),
kmsKeyId: z.string().optional(),
shouldTag: z.boolean().optional(),
metadataSyncMode: z.nativeEnum(IntegrationMetadataSyncMode).optional(),
tags: z
.object({
key: z.string(),
@ -140,7 +136,6 @@ export const AwsSecretManagerConfigurePage = () => {
});
const shouldTagState = watch("shouldTag");
const selectedMetadataSyncMode = watch("metadataSyncMode");
const selectedSourceEnvironment = watch("sourceEnvironment");
const selectedAWSRegion = watch("awsRegion");
const selectedMappingBehavior = watch("mappingBehavior");
@ -176,8 +171,7 @@ export const AwsSecretManagerConfigurePage = () => {
tags,
secretPrefix,
kmsKeyId,
mappingBehavior,
metadataSyncMode
mappingBehavior
}: TFormSchema) => {
try {
if (!integrationAuth?.id) return;
@ -192,8 +186,7 @@ export const AwsSecretManagerConfigurePage = () => {
metadata: {
...(shouldTag
? {
secretAWSTag: tags,
metadataSyncMode
secretAWSTag: tags
}
: {}),
...(secretPrefix && { secretPrefix }),
@ -303,7 +296,7 @@ export const AwsSecretManagerConfigurePage = () => {
errorText={error?.message}
isError={Boolean(error)}
>
<SecretPathInput {...field} />
<SecretPathInput {...field} environment={selectedSourceEnvironment} />
</FormControl>
)}
/>
@ -346,12 +339,7 @@ export const AwsSecretManagerConfigurePage = () => {
>
<Select
defaultValue={field.value}
onValueChange={(e) => {
if (e === IntegrationMappingBehavior.MANY_TO_ONE) {
setValue("metadataSyncMode", IntegrationMetadataSyncMode.CUSTOM);
}
onChange(e);
}}
onValueChange={(e) => onChange(e)}
className="w-full border border-mineshaft-500"
dropdownContainerClassName="max-w-full"
>
@ -398,25 +386,14 @@ export const AwsSecretManagerConfigurePage = () => {
animate={{ opacity: 1, translateX: 0 }}
exit={{ opacity: 0, translateX: 30 }}
>
<div className="mb-3 ml-1 mt-2">
<div className="ml-1 mt-2">
<Controller
control={control}
name="shouldTag"
render={({ field: { onChange, value } }) => (
<Switch
id="tag-aws"
onCheckedChange={(isChecked) => {
if (
isChecked &&
selectedMappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE
) {
setValue(
"metadataSyncMode",
IntegrationMetadataSyncMode.SECRET_METADATA
);
}
onChange(isChecked);
}}
onCheckedChange={(isChecked) => onChange(isChecked)}
isChecked={value}
>
Tag in AWS Secrets Manager
@ -424,76 +401,37 @@ export const AwsSecretManagerConfigurePage = () => {
)}
/>
</div>
{shouldTagState &&
selectedMappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE && (
{shouldTagState && (
<div className="mt-4 flex justify-between">
<Controller
control={control}
name="metadataSyncMode"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
name="tags.0.key"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Tag Sync Mode"
label="Tag Key"
errorText={error?.message}
isError={Boolean(error)}
>
<Select
defaultValue={field.value}
onValueChange={(e) => {
setValue("tags", []);
onChange(e);
}}
className="w-full border border-mineshaft-500"
dropdownContainerClassName="max-w-full"
>
<SelectItem
value={IntegrationMetadataSyncMode.SECRET_METADATA}
className="text-left"
key={`sync-mode-${IntegrationMetadataSyncMode.SECRET_METADATA}`}
>
Secret Metadata
</SelectItem>
<SelectItem
value={IntegrationMetadataSyncMode.CUSTOM}
className="text-left"
key={`sync-mode-${IntegrationMetadataSyncMode.CUSTOM}`}
>
Custom
</SelectItem>
</Select>
<Input placeholder="managed-by" {...field} />
</FormControl>
)}
/>
)}
{shouldTagState &&
selectedMetadataSyncMode === IntegrationMetadataSyncMode.CUSTOM && (
<div className="mt-4 flex justify-between">
<Controller
control={control}
name="tags.0.key"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Tag Key"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="managed-by" {...field} />
</FormControl>
)}
/>
<Controller
control={control}
name="tags.0.value"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Tag Value"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="infisical" {...field} />
</FormControl>
)}
/>
</div>
)}
<Controller
control={control}
name="tags.0.value"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Tag Value"
errorText={error?.message}
isError={Boolean(error)}
>
<Input placeholder="infisical" {...field} />
</FormControl>
)}
/>
</div>
)}
<Controller
control={control}
name="secretPrefix"

@ -76,6 +76,7 @@ export const AzureAppConfigurationConfigurePage = () => {
}
});
const selectedEnvironment = watch("sourceEnvironment");
const { mutateAsync } = useCreateIntegration();
const integrationAuthId = useSearch({
@ -248,7 +249,7 @@ export const AzureAppConfigurationConfigurePage = () => {
name="secretPath"
render={({ field, fieldState: { error } }) => (
<FormControl label="Secrets Path" errorText={error?.message} isError={Boolean(error)}>
<SecretPathInput {...field} />
<SecretPathInput {...field} environment={selectedEnvironment} />
</FormControl>
)}
/>

@ -0,0 +1,97 @@
import { Controller, useForm } from "react-hook-form";
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import z from "zod";
import { Button, Card, CardTitle, FormControl, Input } from "@app/components/v2";
import { Helmet } from "react-helmet";
import { useSearch } from "@tanstack/react-router";
import { ROUTE_PATHS } from "@app/const/routes";
const schema = z.object({
tenantId: z.string().trim().optional()
});
type FormData = z.infer<typeof schema>;
export function AzureKeyVaultAuthorizePage() {
const { state, clientId } = useSearch({
from: ROUTE_PATHS.SecretManager.Integratons.AzureKeyVaultAuthorizePage.id,
});
const { control, handleSubmit } = useForm<FormData>({
resolver: zodResolver(schema)
});
const onFormSubmit = async ({ tenantId }: FormData) => {
const link = `https://login.microsoftonline.com/${
tenantId ?? "common"
}/oauth2/v2.0/authorize?client_id=${clientId}&response_type=code&redirect_uri=${
window.location.origin
}/integrations/azure-key-vault/oauth2/callback&response_mode=query&scope=https://vault.azure.net/.default openid offline_access&state=${state}`;
window.location.assign(link);
};
return (
<div className="flex h-full w-full items-center justify-center">
<Helmet>
<title>Authorize Azure Key Vault Integration</title>
<link rel="icon" href="/infisical.ico" />
</Helmet>
<Card className="mb-12 max-w-lg rounded-md border border-mineshaft-600">
<CardTitle
className="px-6 text-left text-xl"
subTitle="Authenticate with a specific tenant ID or let OAuth handle it automatically."
>
<div className="flex flex-row items-center">
<div className="flex items-center pb-0.5">
<img src="/images/integrations/GitHub.png" height={30} width={30} alt="Github logo" />
</div>
<span className="ml-2.5">Azure Key Vault Integration </span>
<a
href="https://infisical.com/docs/integrations/cloud/azure-key-vault"
target="_blank"
rel="noopener noreferrer"
>
<div className="ml-2 mb-1 inline-block cursor-default rounded-md bg-yellow/20 px-1.5 pb-[0.03rem] pt-[0.04rem] text-sm text-yellow opacity-80 hover:opacity-100">
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
Docs
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
className="ml-1.5 mb-[0.07rem] text-xxs"
/>
</div>
</a>
</div>
</CardTitle>
<form onSubmit={handleSubmit(onFormSubmit)} className="px-6 pb-8 text-right">
<Controller
control={control}
name="tenantId"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Tenant ID (optional)"
errorText={error?.message}
isError={Boolean(error)}
>
<Input {...field} placeholder="2e39537c-9a01-4bd6-a7b8-c3b88cbb8db9" />
</FormControl>
)}
/>
<Button
colorSchema="primary"
variant="outline_bg"
className="mt-2 w-min"
size="sm"
type="submit"
>
Connect to Azure Key Vault
</Button>
</form>
</Card>
</div>
);
}

@ -0,0 +1,17 @@
import { createFileRoute } from "@tanstack/react-router";
import z from 'zod'
import { AzureKeyVaultAuthorizePage } from "./AzureKeyVaultAuthorizePage";
const PageQueryParamsSchema = z.object({
state: z.string(),
clientId: z.string().optional(),
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize"
)({
component: AzureKeyVaultAuthorizePage,
validateSearch: PageQueryParamsSchema
});

@ -62,6 +62,7 @@ export const CircleCIConfigurePage = () => {
const selectedScope = watch("scope");
const selectedOrg = watch("targetOrg");
const selectedEnvironment = watch("sourceEnvironment");
const { data: circleCIOrganizations, isPending: isCircleCIOrganizationsLoading } =
useGetIntegrationAuthCircleCIOrganizations(integrationAuthId);
@ -186,7 +187,7 @@ export const CircleCIConfigurePage = () => {
name="secretPath"
render={({ field, fieldState: { error } }) => (
<FormControl label="Secrets Path" errorText={error?.message} isError={Boolean(error)}>
<SecretPathInput {...field} />
<SecretPathInput {...field} environment={selectedEnvironment.slug}/>
</FormControl>
)}
/>

@ -159,6 +159,7 @@ import { Route as secretManagerIntegrationsChecklyConfigurePageRouteImport } fro
import { Route as secretManagerIntegrationsChecklyAuthorizePageRouteImport } from './pages/secret-manager/integrations/ChecklyAuthorizePage/route'
import { Route as secretManagerIntegrationsBitbucketConfigurePageRouteImport } from './pages/secret-manager/integrations/BitbucketConfigurePage/route'
import { Route as secretManagerIntegrationsAzureKeyVaultConfigurePageRouteImport } from './pages/secret-manager/integrations/AzureKeyVaultConfigurePage/route'
import { Route as secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteImport } from './pages/secret-manager/integrations/AzureKeyVaultAuthorizePage/route'
import { Route as secretManagerIntegrationsAzureDevopsConfigurePageRouteImport } from './pages/secret-manager/integrations/AzureDevopsConfigurePage/route'
import { Route as secretManagerIntegrationsAzureDevopsAuthorizePageRouteImport } from './pages/secret-manager/integrations/AzureDevopsAuthorizePage/route'
import { Route as secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteImport } from './pages/secret-manager/integrations/AzureAppConfigurationConfigurePage/route'
@ -1352,6 +1353,14 @@ const secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute =
AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutIntegrationsRoute,
} as any)
const secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute =
secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteImport.update({
id: '/azure-key-vault/authorize',
path: '/azure-key-vault/authorize',
getParentRoute: () =>
AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutIntegrationsRoute,
} as any)
const secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute =
secretManagerIntegrationsAzureDevopsConfigurePageRouteImport.update({
id: '/azure-devops/create',
@ -2251,6 +2260,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof secretManagerIntegrationsAzureDevopsConfigurePageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutIntegrationsImport
}
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize': {
id: '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize'
path: '/azure-key-vault/authorize'
fullPath: '/secret-manager/$projectId/integrations/azure-key-vault/authorize'
preLoaderRoute: typeof secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutIntegrationsImport
}
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create': {
id: '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create'
path: '/azure-key-vault/create'
@ -2944,6 +2960,7 @@ interface AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutI
secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteRoute: typeof secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteRoute
secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute: typeof secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute
secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute: typeof secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute
secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute: typeof secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute
secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute: typeof secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute
secretManagerIntegrationsBitbucketConfigurePageRouteRoute: typeof secretManagerIntegrationsBitbucketConfigurePageRouteRoute
secretManagerIntegrationsChecklyAuthorizePageRouteRoute: typeof secretManagerIntegrationsChecklyAuthorizePageRouteRoute
@ -3034,6 +3051,8 @@ const AuthenticateInjectOrgDetailsSecretManagerProjectIdSecretManagerLayoutInteg
secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute,
secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute:
secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute,
secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute:
secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute,
secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute:
secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute,
secretManagerIntegrationsBitbucketConfigurePageRouteRoute:
@ -3513,6 +3532,7 @@ export interface FileRoutesByFullPath {
'/secret-manager/$projectId/integrations/azure-app-configuration/create': typeof secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/azure-devops/authorize': typeof secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/azure-devops/create': typeof secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/authorize': typeof secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/create': typeof secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/bitbucket/create': typeof secretManagerIntegrationsBitbucketConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/checkly/authorize': typeof secretManagerIntegrationsChecklyAuthorizePageRouteRoute
@ -3676,6 +3696,7 @@ export interface FileRoutesByTo {
'/secret-manager/$projectId/integrations/azure-app-configuration/create': typeof secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/azure-devops/authorize': typeof secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/azure-devops/create': typeof secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/authorize': typeof secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/create': typeof secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/bitbucket/create': typeof secretManagerIntegrationsBitbucketConfigurePageRouteRoute
'/secret-manager/$projectId/integrations/checkly/authorize': typeof secretManagerIntegrationsChecklyAuthorizePageRouteRoute
@ -3854,6 +3875,7 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/create': typeof secretManagerIntegrationsAzureAppConfigurationConfigurePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/authorize': typeof secretManagerIntegrationsAzureDevopsAuthorizePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/create': typeof secretManagerIntegrationsAzureDevopsConfigurePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize': typeof secretManagerIntegrationsAzureKeyVaultAuthorizePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create': typeof secretManagerIntegrationsAzureKeyVaultConfigurePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/bitbucket/create': typeof secretManagerIntegrationsBitbucketConfigurePageRouteRoute
'/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/checkly/authorize': typeof secretManagerIntegrationsChecklyAuthorizePageRouteRoute
@ -4024,6 +4046,7 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/azure-app-configuration/create'
| '/secret-manager/$projectId/integrations/azure-devops/authorize'
| '/secret-manager/$projectId/integrations/azure-devops/create'
| '/secret-manager/$projectId/integrations/azure-key-vault/authorize'
| '/secret-manager/$projectId/integrations/azure-key-vault/create'
| '/secret-manager/$projectId/integrations/bitbucket/create'
| '/secret-manager/$projectId/integrations/checkly/authorize'
@ -4186,6 +4209,7 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/azure-app-configuration/create'
| '/secret-manager/$projectId/integrations/azure-devops/authorize'
| '/secret-manager/$projectId/integrations/azure-devops/create'
| '/secret-manager/$projectId/integrations/azure-key-vault/authorize'
| '/secret-manager/$projectId/integrations/azure-key-vault/create'
| '/secret-manager/$projectId/integrations/bitbucket/create'
| '/secret-manager/$projectId/integrations/checkly/authorize'
@ -4362,6 +4386,7 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/create'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/authorize'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/create'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/bitbucket/create'
| '/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/checkly/authorize'
@ -4925,6 +4950,7 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/create",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/authorize",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-devops/create",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/bitbucket/create",
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/checkly/authorize",
@ -5105,6 +5131,10 @@ export const routeTree = rootRoute
"filePath": "secret-manager/integrations/AzureDevopsConfigurePage/route.tsx",
"parent": "/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations"
},
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/authorize": {
"filePath": "secret-manager/integrations/AzureKeyVaultAuthorizePage/route.tsx",
"parent": "/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations"
},
"/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/create": {
"filePath": "secret-manager/integrations/AzureKeyVaultConfigurePage/route.tsx",
"parent": "/_authenticate/_inject-org-details/secret-manager/$projectId/_secret-manager-layout/integrations"

@ -75,6 +75,10 @@ const secretManagerRoutes = route("/secret-manager/$projectId", [
"/azure-devops/create",
"secret-manager/integrations/AzureDevopsConfigurePage/route.tsx"
),
route(
"/azure-key-vault/authorize",
"secret-manager/integrations/AzureKeyVaultAuthorizePage/route.tsx"
),
route(
"/azure-key-vault/oauth2/callback",
"secret-manager/integrations/AzureKeyVaultOauthCallbackPage/route.tsx"