mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 14:28:20 +00:00
Merge pull request #2897 from Infisical/feat/resource-metadata
feat: resource metadata
This commit is contained in:
Vendored
+8
@@ -218,6 +218,9 @@ import {
|
|||||||
TRateLimit,
|
TRateLimit,
|
||||||
TRateLimitInsert,
|
TRateLimitInsert,
|
||||||
TRateLimitUpdate,
|
TRateLimitUpdate,
|
||||||
|
TResourceMetadata,
|
||||||
|
TResourceMetadataInsert,
|
||||||
|
TResourceMetadataUpdate,
|
||||||
TSamlConfigs,
|
TSamlConfigs,
|
||||||
TSamlConfigsInsert,
|
TSamlConfigsInsert,
|
||||||
TSamlConfigsUpdate,
|
TSamlConfigsUpdate,
|
||||||
@@ -887,6 +890,11 @@ declare module "knex/types/tables" {
|
|||||||
TProjectSplitBackfillIdsInsert,
|
TProjectSplitBackfillIdsInsert,
|
||||||
TProjectSplitBackfillIdsUpdate
|
TProjectSplitBackfillIdsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.ResourceMetadata]: KnexOriginal.CompositeTableType<
|
||||||
|
TResourceMetadata,
|
||||||
|
TResourceMetadataInsert,
|
||||||
|
TResourceMetadataUpdate
|
||||||
|
>;
|
||||||
[TableName.AppConnection]: KnexOriginal.CompositeTableType<
|
[TableName.AppConnection]: KnexOriginal.CompositeTableType<
|
||||||
TAppConnections,
|
TAppConnections,
|
||||||
TAppConnectionsInsert,
|
TAppConnectionsInsert,
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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,6 +71,7 @@ export * from "./project-user-additional-privilege";
|
|||||||
export * from "./project-user-membership-roles";
|
export * from "./project-user-membership-roles";
|
||||||
export * from "./projects";
|
export * from "./projects";
|
||||||
export * from "./rate-limit";
|
export * from "./rate-limit";
|
||||||
|
export * from "./resource-metadata";
|
||||||
export * from "./saml-configs";
|
export * from "./saml-configs";
|
||||||
export * from "./scim-tokens";
|
export * from "./scim-tokens";
|
||||||
export * from "./secret-approval-policies";
|
export * from "./secret-approval-policies";
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export enum TableName {
|
|||||||
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||||
// used by both identity and users
|
// used by both identity and users
|
||||||
IdentityMetadata = "identity_metadata",
|
IdentityMetadata = "identity_metadata",
|
||||||
|
ResourceMetadata = "resource_metadata",
|
||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
AccessApprovalPolicy = "access_approval_policies",
|
AccessApprovalPolicy = "access_approval_policies",
|
||||||
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// 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,7 +24,8 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({
|
|||||||
requestId: z.string().uuid(),
|
requestId: z.string().uuid(),
|
||||||
op: z.string(),
|
op: z.string(),
|
||||||
secretId: z.string().uuid().nullable().optional(),
|
secretId: z.string().uuid().nullable().optional(),
|
||||||
secretVersion: z.string().uuid().nullable().optional()
|
secretVersion: z.string().uuid().nullable().optional(),
|
||||||
|
secretMetadata: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;
|
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
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(
|
const approvalRequestUser = z.object({ userId: z.string().nullable().optional() }).merge(
|
||||||
UsersSchema.pick({
|
UsersSchema.pick({
|
||||||
@@ -274,6 +275,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
.extend({
|
.extend({
|
||||||
op: z.string(),
|
op: z.string(),
|
||||||
tags: tagSchema,
|
tags: tagSchema,
|
||||||
|
secretMetadata: ResourceMetadataSchema.nullish(),
|
||||||
secret: z
|
secret: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
@@ -291,7 +293,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
secretKey: z.string(),
|
secretKey: z.string(),
|
||||||
secretValue: z.string().optional(),
|
secretValue: z.string().optional(),
|
||||||
secretComment: z.string().optional(),
|
secretComment: z.string().optional(),
|
||||||
tags: tagSchema
|
tags: tagSchema,
|
||||||
|
secretMetadata: ResourceMetadataSchema.nullish()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`,
|
||||||
db.ref("id").withSchema("secVerTag")
|
db.ref("id").withSchema("secVerTag")
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2))
|
.select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2))
|
||||||
.select({
|
.select({
|
||||||
secVerTagId: "secVerTag.id",
|
secVerTagId: "secVerTag.id",
|
||||||
@@ -279,6 +280,11 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
|
db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"),
|
||||||
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
|
db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"),
|
||||||
db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment")
|
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({
|
const formatedDoc = sqlNestRelationships({
|
||||||
data: doc,
|
data: doc,
|
||||||
@@ -338,9 +344,19 @@ 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 }) => ({
|
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
secret: secret?.[0],
|
secret: secret?.[0],
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { KmsDataKey } from "@app/services/kms/kms-types";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
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 { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import {
|
import {
|
||||||
decryptSecretWithBot,
|
decryptSecretWithBot,
|
||||||
@@ -91,6 +93,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "findById">;
|
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "findById">;
|
||||||
@@ -138,7 +141,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
projectSlackConfigDAL
|
projectSlackConfigDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
@@ -241,6 +245,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretKey: el.key,
|
secretKey: el.key,
|
||||||
id: el.id,
|
id: el.id,
|
||||||
version: el.version,
|
version: el.version,
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
|
secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "",
|
||||||
secretComment: el.encryptedComment
|
secretComment: el.encryptedComment
|
||||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString()
|
||||||
@@ -269,7 +274,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretComment: el.secretVersion.encryptedComment
|
secretComment: el.secretVersion.encryptedComment
|
||||||
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString()
|
||||||
: "",
|
: "",
|
||||||
tags: el.secretVersion.tags
|
tags: el.secretVersion.tags,
|
||||||
|
secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}));
|
}));
|
||||||
@@ -543,6 +549,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
? await fnSecretV2BridgeBulkInsert({
|
? await fnSecretV2BridgeBulkInsert({
|
||||||
tx,
|
tx,
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
inputSecrets: secretCreationCommits.map((el) => ({
|
inputSecrets: secretCreationCommits.map((el) => ({
|
||||||
tagIds: el?.tags.map(({ id }) => id),
|
tagIds: el?.tags.map(({ id }) => id),
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -550,6 +557,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
encryptedValue: el.encryptedValue,
|
encryptedValue: el.encryptedValue,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.key,
|
key: el.key,
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
references: el.encryptedValue
|
references: el.encryptedValue
|
||||||
? getAllSecretReferencesV2Bridge(
|
? getAllSecretReferencesV2Bridge(
|
||||||
secretManagerDecryptor({
|
secretManagerDecryptor({
|
||||||
@@ -559,6 +567,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
: [],
|
: [],
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
})),
|
})),
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
@@ -568,6 +577,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const updatedSecrets = secretUpdationCommits.length
|
const updatedSecrets = secretUpdationCommits.length
|
||||||
? await fnSecretV2BridgeBulkUpdate({
|
? await fnSecretV2BridgeBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: secretUpdationCommits.map((el) => {
|
inputSecrets: secretUpdationCommits.map((el) => {
|
||||||
const encryptedValue =
|
const encryptedValue =
|
||||||
@@ -592,6 +602,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.key,
|
key: el.key,
|
||||||
tags: el?.tags.map(({ id }) => id),
|
tags: el?.tags.map(({ id }) => id),
|
||||||
|
secretMetadata: el.secretMetadata as ResourceMetadataDTO,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -599,7 +610,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionTagV2BridgeDAL
|
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const deletedSecret = secretDeletionCommits.length
|
const deletedSecret = secretDeletionCommits.length
|
||||||
@@ -824,6 +836,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}
|
}
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: folder.path,
|
secretPath: folder.path,
|
||||||
environmentSlug: folder.environmentSlug,
|
environmentSlug: folder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -1208,6 +1221,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
),
|
),
|
||||||
skipMultilineEncoding: createdSecret.skipMultilineEncoding,
|
skipMultilineEncoding: createdSecret.skipMultilineEncoding,
|
||||||
key: createdSecret.secretKey,
|
key: createdSecret.secretKey,
|
||||||
|
secretMetadata: createdSecret.secretMetadata,
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
@@ -1263,12 +1277,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderNote,
|
reminderNote,
|
||||||
secretComment,
|
secretComment,
|
||||||
metadata,
|
metadata,
|
||||||
skipMultilineEncoding
|
skipMultilineEncoding,
|
||||||
|
secretMetadata
|
||||||
}) => {
|
}) => {
|
||||||
const secretId = updatingSecretsGroupByKey[secretKey][0].id;
|
const secretId = updatingSecretsGroupByKey[secretKey][0].id;
|
||||||
if (tagIds?.length) commitTagIds[secretKey] = tagIds;
|
if (tagIds?.length) commitTagIds[secretKey] = tagIds;
|
||||||
return {
|
return {
|
||||||
...latestSecretVersions[secretId],
|
...latestSecretVersions[secretId],
|
||||||
|
secretMetadata,
|
||||||
key: newSecretName || secretKey,
|
key: newSecretName || secretKey,
|
||||||
encryptedComment: setKnexStringValue(
|
encryptedComment: setKnexStringValue(
|
||||||
secretComment,
|
secretComment,
|
||||||
@@ -1370,7 +1386,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderRepeatDays,
|
reminderRepeatDays,
|
||||||
encryptedValue,
|
encryptedValue,
|
||||||
secretId,
|
secretId,
|
||||||
secretVersion
|
secretVersion,
|
||||||
|
secretMetadata
|
||||||
}) => ({
|
}) => ({
|
||||||
version,
|
version,
|
||||||
requestId: doc.id,
|
requestId: doc.id,
|
||||||
@@ -1383,7 +1400,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
reminderRepeatDays,
|
reminderRepeatDays,
|
||||||
reminderNote,
|
reminderNote,
|
||||||
encryptedComment,
|
encryptedComment,
|
||||||
key
|
key,
|
||||||
|
secretMetadata: JSON.stringify(secretMetadata)
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
tx
|
tx
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
|
import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
import { SecretOperations } from "@app/services/secret/secret-types";
|
import { SecretOperations } from "@app/services/secret/secret-types";
|
||||||
|
|
||||||
export enum RequestState {
|
export enum RequestState {
|
||||||
@@ -34,6 +35,7 @@ export type TApprovalCreateSecretV2Bridge = {
|
|||||||
reminderRepeatDays?: number | null;
|
reminderRepeatDays?: number | null;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
metadata?: Record<string, string>;
|
metadata?: Record<string, string>;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { ActorType } from "@app/services/auth/auth-type";
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
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 { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
import { fnSecretBulkInsert, fnSecretBulkUpdate } from "@app/services/secret/secret-fns";
|
||||||
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
import { TSecretQueueFactory, uniqueSecretQueueKey } from "@app/services/secret/secret-queue";
|
||||||
@@ -56,6 +58,7 @@ type TSecretReplicationServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "find" | "insertMany">;
|
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "find" | "insertMany">;
|
||||||
secretVersionV2TagBridgeDAL: Pick<TSecretVersionV2TagDALFactory, "find" | "insertMany">;
|
secretVersionV2TagBridgeDAL: Pick<TSecretVersionV2TagDALFactory, "find" | "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "replicateSecrets">;
|
||||||
queueService: Pick<TQueueServiceFactory, "start" | "listen" | "queue" | "stopJobById">;
|
queueService: Pick<TQueueServiceFactory, "start" | "listen" | "queue" | "stopJobById">;
|
||||||
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
|
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
|
||||||
@@ -121,7 +124,8 @@ export const secretReplicationServiceFactory = ({
|
|||||||
secretVersionV2TagBridgeDAL,
|
secretVersionV2TagBridgeDAL,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretReplicationServiceFactoryDep) => {
|
}: TSecretReplicationServiceFactoryDep) => {
|
||||||
const $getReplicatedSecrets = (
|
const $getReplicatedSecrets = (
|
||||||
botKey: string,
|
botKey: string,
|
||||||
@@ -151,8 +155,10 @@ export const secretReplicationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const $getReplicatedSecretsV2 = (
|
const $getReplicatedSecretsV2 = (
|
||||||
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[],
|
localSecrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[],
|
||||||
importedSecrets: { secrets: (TSecretsV2 & { secretKey: string; secretValue?: string })[] }[]
|
importedSecrets: {
|
||||||
|
secrets: (TSecretsV2 & { secretKey: string; secretValue?: string; secretMetadata?: ResourceMetadataDTO })[];
|
||||||
|
}[]
|
||||||
) => {
|
) => {
|
||||||
const deDupe = new Set<string>();
|
const deDupe = new Set<string>();
|
||||||
const secrets = [...localSecrets];
|
const secrets = [...localSecrets];
|
||||||
@@ -178,6 +184,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
secretPath,
|
secretPath,
|
||||||
environmentSlug,
|
environmentSlug,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
pickOnlyImportIds,
|
pickOnlyImportIds,
|
||||||
@@ -222,6 +229,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
.map(({ folderId }) =>
|
.map(({ folderId }) =>
|
||||||
secretQueueService.replicateSecrets({
|
secretQueueService.replicateSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -267,6 +275,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString()
|
||||||
: undefined
|
: undefined
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const sourceSecrets = $getReplicatedSecretsV2(sourceDecryptedLocalSecrets, sourceImportedSecrets);
|
const sourceSecrets = $getReplicatedSecretsV2(sourceDecryptedLocalSecrets, sourceImportedSecrets);
|
||||||
const sourceSecretsGroupByKey = groupBy(sourceSecrets, (i) => i.key);
|
const sourceSecretsGroupByKey = groupBy(sourceSecrets, (i) => i.key);
|
||||||
|
|
||||||
@@ -333,13 +342,29 @@ export const secretReplicationServiceFactory = ({
|
|||||||
.map((el) => ({ ...el, operation: SecretOperations.Create })); // rewrite update ops to create
|
.map((el) => ({ ...el, operation: SecretOperations.Create })); // rewrite update ops to create
|
||||||
|
|
||||||
const locallyUpdatedSecrets = sourceSecrets
|
const locallyUpdatedSecrets = sourceSecrets
|
||||||
.filter(
|
.filter(({ key, secretKey, secretValue, secretMetadata }) => {
|
||||||
({ key, secretKey, secretValue }) =>
|
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 (
|
||||||
destinationLocalSecretsGroupedByKey[key]?.[0] &&
|
destinationLocalSecretsGroupedByKey[key]?.[0] &&
|
||||||
// if key or value changed
|
// if key or value changed
|
||||||
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretKey !== secretKey ||
|
(destinationLocalSecretsGroupedByKey[key]?.[0]?.secretKey !== secretKey ||
|
||||||
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue)
|
destinationLocalSecretsGroupedByKey[key]?.[0]?.secretValue !== secretValue ||
|
||||||
)
|
sourceSecretMetadataJson !== destinationSecretMetadataJson)
|
||||||
|
);
|
||||||
|
})
|
||||||
.map((el) => ({ ...el, operation: SecretOperations.Update })); // rewrite update ops to create
|
.map((el) => ({ ...el, operation: SecretOperations.Update })); // rewrite update ops to create
|
||||||
|
|
||||||
const locallyDeletedSecrets = destinationLocalSecrets
|
const locallyDeletedSecrets = destinationLocalSecrets
|
||||||
@@ -387,6 +412,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
op: operation,
|
op: operation,
|
||||||
requestId: approvalRequestDoc.id,
|
requestId: approvalRequestDoc.id,
|
||||||
metadata: doc.metadata,
|
metadata: doc.metadata,
|
||||||
|
secretMetadata: JSON.stringify(doc.secretMetadata),
|
||||||
key: doc.key,
|
key: doc.key,
|
||||||
encryptedValue: doc.encryptedValue,
|
encryptedValue: doc.encryptedValue,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
@@ -406,10 +432,12 @@ export const secretReplicationServiceFactory = ({
|
|||||||
if (locallyCreatedSecrets.length) {
|
if (locallyCreatedSecrets.length) {
|
||||||
await fnSecretV2BridgeBulkInsert({
|
await fnSecretV2BridgeBulkInsert({
|
||||||
folderId: destinationReplicationFolderId,
|
folderId: destinationReplicationFolderId,
|
||||||
|
orgId,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
tx,
|
tx,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
||||||
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
||||||
return {
|
return {
|
||||||
@@ -419,6 +447,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
encryptedValue: doc.encryptedValue,
|
encryptedValue: doc.encryptedValue,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -426,10 +455,12 @@ export const secretReplicationServiceFactory = ({
|
|||||||
}
|
}
|
||||||
if (locallyUpdatedSecrets.length) {
|
if (locallyUpdatedSecrets.length) {
|
||||||
await fnSecretV2BridgeBulkUpdate({
|
await fnSecretV2BridgeBulkUpdate({
|
||||||
|
orgId,
|
||||||
folderId: destinationReplicationFolderId,
|
folderId: destinationReplicationFolderId,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
tx,
|
tx,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
secretVersionTagDAL: secretVersionV2TagBridgeDAL,
|
||||||
inputSecrets: locallyUpdatedSecrets.map((doc) => {
|
inputSecrets: locallyUpdatedSecrets.map((doc) => {
|
||||||
@@ -445,6 +476,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
encryptedValue: doc.encryptedValue as Buffer,
|
encryptedValue: doc.encryptedValue as Buffer,
|
||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
references: doc.secretValue ? getAllSecretReferences(doc.secretValue).nestedReferences : []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -466,6 +498,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environmentSlug,
|
environmentSlug: destinationFolder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -751,6 +784,7 @@ export const secretReplicationServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environmentSlug,
|
environmentSlug: destinationFolder.environmentSlug,
|
||||||
actorId,
|
actorId,
|
||||||
|
|||||||
@@ -1150,7 +1150,8 @@ export const INTEGRATION = {
|
|||||||
shouldMaskSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Masked'.",
|
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'.",
|
shouldProtectSecrets: "Specifies if the secrets synced from Infisical to Gitlab should be marked as 'Protected'.",
|
||||||
shouldEnableDelete: "The flag to enable deletion of secrets.",
|
shouldEnableDelete: "The flag to enable deletion of secrets.",
|
||||||
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy."
|
octopusDeployScopeValues: "Specifies the scope values to set on synced secrets to Octopus Deploy.",
|
||||||
|
metadataSyncMode: "The mode for syncing metadata to external system"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ import { projectUserMembershipRoleDALFactory } from "@app/services/project-membe
|
|||||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||||
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
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 { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
||||||
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
@@ -374,6 +375,7 @@ export const registerRoutes = async (
|
|||||||
const externalGroupOrgRoleMappingDAL = externalGroupOrgRoleMappingDALFactory(db);
|
const externalGroupOrgRoleMappingDAL = externalGroupOrgRoleMappingDALFactory(db);
|
||||||
|
|
||||||
const projectTemplateDAL = projectTemplateDALFactory(db);
|
const projectTemplateDAL = projectTemplateDALFactory(db);
|
||||||
|
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
||||||
|
|
||||||
const permissionService = permissionServiceFactory({
|
const permissionService = permissionServiceFactory({
|
||||||
permissionDAL,
|
permissionDAL,
|
||||||
@@ -854,7 +856,8 @@ export const registerRoutes = async (
|
|||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
orgService
|
orgService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectService = projectServiceFactory({
|
const projectService = projectServiceFactory({
|
||||||
@@ -980,7 +983,8 @@ export const registerRoutes = async (
|
|||||||
secretApprovalPolicyService,
|
secretApprovalPolicyService,
|
||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
snapshotService
|
snapshotService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
|
const secretApprovalRequestService = secretApprovalRequestServiceFactory({
|
||||||
@@ -1007,7 +1011,8 @@ export const registerRoutes = async (
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
projectSlackConfigDAL
|
projectSlackConfigDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretService = secretServiceFactory({
|
const secretService = secretServiceFactory({
|
||||||
@@ -1086,8 +1091,10 @@ export const registerRoutes = async (
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionV2TagBridgeDAL: secretVersionTagV2BridgeDAL,
|
secretVersionV2TagBridgeDAL: secretVersionTagV2BridgeDAL,
|
||||||
secretVersionV2BridgeDAL
|
secretVersionV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretRotationQueue = secretRotationQueueFactory({
|
const secretRotationQueue = secretRotationQueueFactory({
|
||||||
telemetryService,
|
telemetryService,
|
||||||
secretRotationDAL,
|
secretRotationDAL,
|
||||||
@@ -1339,7 +1346,8 @@ export const registerRoutes = async (
|
|||||||
folderDAL,
|
folderDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
queueService,
|
queueService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const migrationService = externalMigrationServiceFactory({
|
const migrationService = externalMigrationServiceFactory({
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
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 { SecretsOrderBy } from "@app/services/secret/secret-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -408,6 +410,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -693,6 +696,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -864,6 +868,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { getUserAgentType } from "@app/server/plugins/audit-log";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
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 { SecretOperations, SecretProtectionType } from "@app/services/secret/secret-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
@@ -205,6 +206,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets: secretRawSchema
|
secrets: secretRawSchema
|
||||||
.extend({
|
.extend({
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SecretTagsSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
slug: true,
|
slug: true,
|
||||||
@@ -220,7 +222,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretPath: z.string(),
|
secretPath: z.string(),
|
||||||
environment: z.string(),
|
environment: z.string(),
|
||||||
folderId: z.string().optional(),
|
folderId: z.string().optional(),
|
||||||
secrets: secretRawSchema.omit({ createdAt: true, updatedAt: true }).array()
|
secrets: secretRawSchema
|
||||||
|
.omit({ createdAt: true, updatedAt: true })
|
||||||
|
.extend({
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -348,7 +355,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.extend({ name: z.string() })
|
.extend({ name: z.string() })
|
||||||
.array()
|
.array()
|
||||||
.optional()
|
.optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -450,6 +458,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
||||||
.describe(RAW_SECRETS.CREATE.secretValue),
|
.describe(RAW_SECRETS.CREATE.secretValue),
|
||||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds),
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type),
|
||||||
@@ -484,6 +493,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretValue: req.body.secretValue,
|
secretValue: req.body.secretValue,
|
||||||
skipMultilineEncoding: req.body.skipMultilineEncoding,
|
skipMultilineEncoding: req.body.skipMultilineEncoding,
|
||||||
secretComment: req.body.secretComment,
|
secretComment: req.body.secretComment,
|
||||||
|
secretMetadata: req.body.secretMetadata,
|
||||||
tagIds: req.body.tagIds,
|
tagIds: req.body.tagIds,
|
||||||
secretReminderNote: req.body.secretReminderNote,
|
secretReminderNote: req.body.secretReminderNote,
|
||||||
secretReminderRepeatDays: req.body.secretReminderRepeatDays
|
secretReminderRepeatDays: req.body.secretReminderRepeatDays
|
||||||
@@ -558,6 +568,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
||||||
metadata: z.record(z.string()).optional(),
|
metadata: z.record(z.string()).optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
||||||
secretReminderRepeatDays: z
|
secretReminderRepeatDays: z
|
||||||
.number()
|
.number()
|
||||||
@@ -595,8 +606,10 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretReminderNote: req.body.secretReminderNote,
|
secretReminderNote: req.body.secretReminderNote,
|
||||||
metadata: req.body.metadata,
|
metadata: req.body.metadata,
|
||||||
newSecretName: req.body.newSecretName,
|
newSecretName: req.body.newSecretName,
|
||||||
secretComment: req.body.secretComment
|
secretComment: req.body.secretComment,
|
||||||
|
secretMetadata: req.body.secretMetadata
|
||||||
});
|
});
|
||||||
|
|
||||||
if (secretOperation.type === SecretProtectionType.Approval) {
|
if (secretOperation.type === SecretProtectionType.Approval) {
|
||||||
return { approval: secretOperation.approval };
|
return { approval: secretOperation.approval };
|
||||||
}
|
}
|
||||||
@@ -1850,6 +1863,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
||||||
metadata: z.record(z.string()).optional(),
|
metadata: z.record(z.string()).optional(),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.CREATE.tagIds)
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
@@ -1952,6 +1966,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
|
newSecretName: z.string().min(1).optional().describe(RAW_SECRETS.UPDATE.newSecretName),
|
||||||
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
tagIds: z.string().array().optional().describe(RAW_SECRETS.UPDATE.tagIds),
|
||||||
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
secretReminderNote: z.string().optional().nullable().describe(RAW_SECRETS.UPDATE.secretReminderNote),
|
||||||
|
secretMetadata: ResourceMetadataSchema.optional(),
|
||||||
secretReminderRepeatDays: z
|
secretReminderRepeatDays: z
|
||||||
.number()
|
.number()
|
||||||
.optional()
|
.optional()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { TProjectDALFactory } from "../project/project-dal";
|
|||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
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 { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -35,6 +36,8 @@ export type TImportDataIntoInfisicalDTO = {
|
|||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "create">;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
secretVersionTagDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany" | "create">;
|
||||||
|
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
|
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath" | "findById">;
|
folderDAL: Pick<TSecretFolderDALFactory, "create" | "findBySecretPath" | "findById">;
|
||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
@@ -503,6 +506,7 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
input: { data, actor, actorId, actorOrgId, actorAuthMethod }
|
||||||
}: TImportDataIntoInfisicalDTO) => {
|
}: TImportDataIntoInfisicalDTO) => {
|
||||||
// Import data to infisical
|
// Import data to infisical
|
||||||
@@ -762,6 +766,8 @@ export const importDataIntoInfisicalFn = async ({
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
folderId: selectedFolder.id,
|
folderId: selectedFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { TProjectDALFactory } from "../project/project-dal";
|
|||||||
import { TProjectServiceFactory } from "../project/project-service";
|
import { TProjectServiceFactory } from "../project/project-service";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
import { TProjectEnvServiceFactory } from "../project-env/project-env-service";
|
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 { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
import { TSecretTagDALFactory } from "../secret-tag/secret-tag-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -35,6 +36,8 @@ export type TExternalMigrationQueueFactoryDep = {
|
|||||||
projectService: Pick<TProjectServiceFactory, "createProject">;
|
projectService: Pick<TProjectServiceFactory, "createProject">;
|
||||||
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
projectEnvService: Pick<TProjectEnvServiceFactory, "createEnvironment">;
|
||||||
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
secretV2BridgeService: Pick<TSecretV2BridgeServiceFactory, "createManySecret">;
|
||||||
|
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
export type TExternalMigrationQueueFactory = ReturnType<typeof externalMigrationQueueFactory>;
|
||||||
@@ -52,7 +55,8 @@ export const externalMigrationQueueFactory = ({
|
|||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
folderDAL
|
folderDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TExternalMigrationQueueFactoryDep) => {
|
}: TExternalMigrationQueueFactoryDep) => {
|
||||||
const startImport = async (dto: {
|
const startImport = async (dto: {
|
||||||
actorEmail: string;
|
actorEmail: string;
|
||||||
@@ -109,7 +113,8 @@ export const externalMigrationQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
projectService,
|
projectService,
|
||||||
projectEnvService,
|
projectEnvService,
|
||||||
secretV2BridgeService
|
secretV2BridgeService,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
if (projectsNotImported.length) {
|
if (projectsNotImported.length) {
|
||||||
|
|||||||
@@ -427,3 +427,8 @@ export const getIntegrationOptions = async () => {
|
|||||||
|
|
||||||
return INTEGRATION_OPTIONS;
|
return INTEGRATION_OPTIONS;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum IntegrationMetadataSyncMode {
|
||||||
|
CUSTOM = "custom",
|
||||||
|
SECRET_METADATA = "secret-metadata"
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/
|
|||||||
|
|
||||||
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
import { TIntegrationDALFactory } from "../integration/integration-dal";
|
||||||
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
import { IntegrationMetadataSchema } from "../integration/integration-schema";
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { IntegrationAuthMetadataSchema } from "./integration-auth-schema";
|
import { IntegrationAuthMetadataSchema } from "./integration-auth-schema";
|
||||||
import {
|
import {
|
||||||
CircleCiScope,
|
CircleCiScope,
|
||||||
@@ -48,6 +49,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
IntegrationInitialSyncBehavior,
|
IntegrationInitialSyncBehavior,
|
||||||
IntegrationMappingBehavior,
|
IntegrationMappingBehavior,
|
||||||
|
IntegrationMetadataSyncMode,
|
||||||
Integrations,
|
Integrations,
|
||||||
IntegrationUrls
|
IntegrationUrls
|
||||||
} from "./integration-list";
|
} from "./integration-list";
|
||||||
@@ -1082,14 +1084,14 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
projectId
|
projectId
|
||||||
}: {
|
}: {
|
||||||
integration: TIntegrations;
|
integration: TIntegrations;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
|
||||||
accessId: string | null;
|
accessId: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
awsAssumeRoleArn: string | null;
|
awsAssumeRoleArn: string | null;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const metadata = z.record(z.any()).parse(integration.metadata || {});
|
const metadata = IntegrationMetadataSchema.parse(integration.metadata || {});
|
||||||
|
|
||||||
if (!accessId && !awsAssumeRoleArn) {
|
if (!accessId && !awsAssumeRoleArn) {
|
||||||
throw new Error("AWS access ID/AWS Assume Role is required");
|
throw new Error("AWS access ID/AWS Assume Role is required");
|
||||||
@@ -1137,8 +1139,25 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
const processAwsSecret = async (
|
const processAwsSecret = async (
|
||||||
secretId: string,
|
secretId: string,
|
||||||
secretValue: Record<string, string | null | undefined> | string
|
secretValue: Record<string, string | null | undefined> | string,
|
||||||
|
secretMetadata?: ResourceMetadataDTO
|
||||||
) => {
|
) => {
|
||||||
|
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 {
|
try {
|
||||||
const awsSecretManagerSecret = await secretsManager.send(
|
const awsSecretManagerSecret = await secretsManager.send(
|
||||||
new GetSecretValueCommand({
|
new GetSecretValueCommand({
|
||||||
@@ -1174,9 +1193,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
|
if (shouldTag) {
|
||||||
|
|
||||||
if (secretAWSTag && secretAWSTag.length) {
|
|
||||||
const describedSecret = await secretsManager.send(
|
const describedSecret = await secretsManager.send(
|
||||||
// requires secretsmanager:DescribeSecret policy
|
// requires secretsmanager:DescribeSecret policy
|
||||||
new DescribeSecretCommand({
|
new DescribeSecretCommand({
|
||||||
@@ -1186,14 +1203,6 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
if (!describedSecret.Tags) return;
|
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(
|
const awsTagObj = (describedSecret.Tags || []).reduce(
|
||||||
(acc, item) => {
|
(acc, item) => {
|
||||||
if (item.Key && item.Value) {
|
if (item.Key && item.Value) {
|
||||||
@@ -1225,7 +1234,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
secretAWSTag?.forEach((tag) => {
|
tagArray.forEach((tag) => {
|
||||||
if (!(tag.key in awsTagObj)) {
|
if (!(tag.key in awsTagObj)) {
|
||||||
// create tag in AWS secret manager
|
// create tag in AWS secret manager
|
||||||
tagsToUpdate.push({
|
tagsToUpdate.push({
|
||||||
@@ -1262,8 +1271,8 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
Name: secretId,
|
Name: secretId,
|
||||||
SecretString: typeof secretValue === "string" ? secretValue : JSON.stringify(secretValue),
|
SecretString: typeof secretValue === "string" ? secretValue : JSON.stringify(secretValue),
|
||||||
...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
|
...(metadata.kmsKeyId && { KmsKeyId: metadata.kmsKeyId }),
|
||||||
Tags: metadata.secretAWSTag
|
Tags: shouldTag
|
||||||
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
|
? tagArray.map((tag: { key: string; value: string }) => ({
|
||||||
Key: tag.key,
|
Key: tag.key,
|
||||||
Value: tag.value
|
Value: tag.value
|
||||||
}))
|
}))
|
||||||
@@ -1280,7 +1289,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
||||||
for await (const [key, value] of Object.entries(secrets)) {
|
for await (const [key, value] of Object.entries(secrets)) {
|
||||||
await processAwsSecret(key, value.value);
|
await processAwsSecret(key, value.value, value.secretMetadata);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
||||||
@@ -4440,7 +4449,7 @@ export const syncIntegrationSecrets = async ({
|
|||||||
secretPath: string;
|
secretPath: string;
|
||||||
};
|
};
|
||||||
integrationAuth: TIntegrationAuths;
|
integrationAuth: TIntegrationAuths;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string; secretMetadata?: ResourceMetadataDTO }>;
|
||||||
accessId: string | null;
|
accessId: string | null;
|
||||||
awsAssumeRoleArn: string | null;
|
awsAssumeRoleArn: string | null;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { INTEGRATION } from "@app/lib/api-docs";
|
import { INTEGRATION } from "@app/lib/api-docs";
|
||||||
|
|
||||||
import { IntegrationMappingBehavior } from "../integration-auth/integration-list";
|
import { IntegrationMappingBehavior, IntegrationMetadataSyncMode } from "../integration-auth/integration-list";
|
||||||
|
|
||||||
export const IntegrationMetadataSchema = z.object({
|
export const IntegrationMetadataSchema = z.object({
|
||||||
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||||
@@ -50,6 +50,11 @@ export const IntegrationMetadataSchema = z.object({
|
|||||||
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
shouldMaskSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldMaskSecrets),
|
||||||
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets),
|
shouldProtectSecrets: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldProtectSecrets),
|
||||||
|
|
||||||
|
metadataSyncMode: z
|
||||||
|
.nativeEnum(IntegrationMetadataSyncMode)
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.metadataSyncMode),
|
||||||
|
|
||||||
octopusDeployScopeValues: z
|
octopusDeployScopeValues: z
|
||||||
.object({
|
.object({
|
||||||
// in Octopus Deploy Scope Value Format
|
// in Octopus Deploy Scope Value Format
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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,6 +1,7 @@
|
|||||||
import { SecretType, TSecretImports, TSecrets, TSecretsV2 } from "@app/db/schemas";
|
import { SecretType, TSecretImports, TSecrets, TSecretsV2 } from "@app/db/schemas";
|
||||||
import { groupBy, unique } from "@app/lib/fn";
|
import { groupBy, unique } from "@app/lib/fn";
|
||||||
|
|
||||||
|
import { ResourceMetadataDTO } from "../resource-metadata/resource-metadata-schema";
|
||||||
import { TSecretDALFactory } from "../secret/secret-dal";
|
import { TSecretDALFactory } from "../secret/secret-dal";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@@ -39,6 +40,7 @@ type TSecretImportSecretsV2 = {
|
|||||||
// But for somereason ts consider ? and undefined explicit as different just ts things
|
// But for somereason ts consider ? and undefined explicit as different just ts things
|
||||||
secretValue: string;
|
secretValue: string;
|
||||||
secretComment: string;
|
secretComment: string;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
})[];
|
})[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ export const secretImportServiceFactory = ({
|
|||||||
if (secImport.isReplication && sourceFolder) {
|
if (secImport.isReplication && sourceFolder) {
|
||||||
await secretQueueService.replicateSecrets({
|
await secretQueueService.replicateSecrets({
|
||||||
secretPath: secImport.importPath,
|
secretPath: secImport.importPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: importEnv.slug,
|
environmentSlug: importEnv.slug,
|
||||||
pickOnlyImportIds: [secImport.id],
|
pickOnlyImportIds: [secImport.id],
|
||||||
@@ -169,6 +170,7 @@ export const secretImportServiceFactory = ({
|
|||||||
} else {
|
} else {
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -340,6 +342,7 @@ export const secretImportServiceFactory = ({
|
|||||||
|
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
actor,
|
actor,
|
||||||
@@ -415,6 +418,7 @@ export const secretImportServiceFactory = ({
|
|||||||
|
|
||||||
if (membership && sourceFolder) {
|
if (membership && sourceFolder) {
|
||||||
await secretQueueService.replicateSecrets({
|
await secretQueueService.replicateSecrets({
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: secretImportDoc.importPath,
|
secretPath: secretImportDoc.importPath,
|
||||||
projectId,
|
projectId,
|
||||||
environmentSlug: secretImportDoc.importEnv.slug,
|
environmentSlug: secretImportDoc.importEnv.slug,
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${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(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
@@ -103,6 +109,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -221,7 +236,9 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
|
const secs = await (tx || db.replicaNode())(TableName.SecretV2)
|
||||||
.where({ folderId })
|
.where({ folderId })
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
void bd.whereNull("userId").orWhere({ userId: userId || null });
|
void bd
|
||||||
|
.whereNull(`${TableName.SecretV2}.userId`)
|
||||||
|
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
|
||||||
})
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretV2JnTag,
|
TableName.SecretV2JnTag,
|
||||||
@@ -233,10 +250,16 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
.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");
|
.orderBy("id", "asc");
|
||||||
|
|
||||||
const data = sqlNestRelationships({
|
const data = sqlNestRelationships({
|
||||||
@@ -253,6 +276,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -367,7 +399,9 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
void bd.whereNull(`${TableName.SecretV2}.userId`).orWhere({ userId: userId || null });
|
void bd
|
||||||
|
.whereNull(`${TableName.SecretV2}.userId`)
|
||||||
|
.orWhere({ [`${TableName.SecretV2}.userId` as "userId"]: userId || null });
|
||||||
})
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretV2JnTag,
|
TableName.SecretV2JnTag,
|
||||||
@@ -379,13 +413,23 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.SecretV2),
|
selectAllTableCols(TableName.SecretV2),
|
||||||
db.raw(`DENSE_RANK() OVER (ORDER BY "key" ${filters?.orderDirection ?? OrderByDirection.ASC}) as rank`)
|
db.raw(
|
||||||
|
`DENSE_RANK() OVER (ORDER BY "${TableName.SecretV2}".key ${
|
||||||
|
filters?.orderDirection ?? OrderByDirection.ASC
|
||||||
|
}) as rank`
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
|
.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) => {
|
.where((bd) => {
|
||||||
const slugs = filters?.tagSlugs?.filter(Boolean);
|
const slugs = filters?.tagSlugs?.filter(Boolean);
|
||||||
if (slugs && slugs.length > 0) {
|
if (slugs && slugs.length > 0) {
|
||||||
@@ -425,6 +469,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -545,10 +598,17 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
`${TableName.SecretV2JnTag}.${TableName.SecretTag}Id`,
|
||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
.select(selectAllTableCols(TableName.SecretV2))
|
.select(selectAllTableCols(TableName.SecretV2))
|
||||||
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
.select(db.ref("id").withSchema(TableName.SecretTag).as("tagId"))
|
||||||
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
|
||||||
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"));
|
.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")
|
||||||
|
);
|
||||||
|
|
||||||
const docs = sqlNestRelationships({
|
const docs = sqlNestRelationships({
|
||||||
data: rawDocs,
|
data: rawDocs,
|
||||||
key: "id",
|
key: "id",
|
||||||
@@ -563,6 +623,15 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
slug,
|
slug,
|
||||||
name: slug
|
name: slug
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "metadataId",
|
||||||
|
label: "secretMetadata" as const,
|
||||||
|
mapper: ({ metadataKey, metadataValue, metadataId }) => ({
|
||||||
|
id: metadataId,
|
||||||
|
key: metadataKey,
|
||||||
|
value: metadataValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { groupBy } from "@app/lib/fn";
|
|||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
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 { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
import { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
||||||
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
import { TFnSecretBulkDelete, TFnSecretBulkInsert, TFnSecretBulkUpdate } from "./secret-v2-bridge-types";
|
||||||
@@ -54,9 +55,11 @@ export const getAllSecretReferences = (maybeSecretReference: string) => {
|
|||||||
export const fnSecretBulkInsert = async ({
|
export const fnSecretBulkInsert = async ({
|
||||||
// TODO: Pick types here
|
// TODO: Pick types here
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId,
|
||||||
inputSecrets,
|
inputSecrets,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
tx
|
tx
|
||||||
@@ -91,6 +94,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
|
sanitizedInputSecrets.map((el) => ({ ...el, folderId })),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
const newSecretGroupedByKeyName = groupBy(newSecrets, (item) => item.key);
|
||||||
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
const newSecretTags = inputSecrets.flatMap(({ tagIds: secretTags = [], key }) =>
|
||||||
secretTags.map((tag) => ({
|
secretTags.map((tag) => ({
|
||||||
@@ -106,6 +110,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
await secretDAL.upsertSecretReferences(
|
await secretDAL.upsertSecretReferences(
|
||||||
inputSecrets.map(({ references = [], key }) => ({
|
inputSecrets.map(({ references = [], key }) => ({
|
||||||
secretId: newSecretGroupedByKeyName[key][0].id,
|
secretId: newSecretGroupedByKeyName[key][0].id,
|
||||||
@@ -113,6 +118,22 @@ export const fnSecretBulkInsert = async ({
|
|||||||
})),
|
})),
|
||||||
tx
|
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) {
|
if (newSecretTags.length) {
|
||||||
const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx);
|
const secTags = await secretTagDAL.saveTagsToSecretV2(newSecretTags, tx);
|
||||||
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);
|
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);
|
||||||
@@ -120,6 +141,7 @@ export const fnSecretBulkInsert = async ({
|
|||||||
[`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id,
|
[`${TableName.SecretVersionV2}Id` as const]: secVersionsGroupBySecId[secrets_v2Id][0].id,
|
||||||
[`${TableName.SecretTag}Id` as const]: secret_tagsId
|
[`${TableName.SecretTag}Id` as const]: secret_tagsId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
|
await secretVersionTagDAL.insertMany(newSecretVersionTags, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +152,12 @@ export const fnSecretBulkUpdate = async ({
|
|||||||
tx,
|
tx,
|
||||||
inputSecrets,
|
inputSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL
|
secretVersionTagDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TFnSecretBulkUpdate) => {
|
}: TFnSecretBulkUpdate) => {
|
||||||
const sanitizedInputSecrets = inputSecrets.map(
|
const sanitizedInputSecrets = inputSecrets.map(
|
||||||
({
|
({
|
||||||
@@ -231,6 +255,34 @@ 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 }));
|
return newSecrets.map((secret) => ({ ...secret, _id: secret.id }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -570,6 +622,7 @@ export const reshapeBridgeSecret = (
|
|||||||
color?: string | null;
|
color?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
}
|
}
|
||||||
) => ({
|
) => ({
|
||||||
secretKey: secret.key,
|
secretKey: secret.key,
|
||||||
@@ -588,6 +641,7 @@ export const reshapeBridgeSecret = (
|
|||||||
secretReminderRepeatDays: secret.reminderRepeatDays,
|
secretReminderRepeatDays: secret.reminderRepeatDays,
|
||||||
secretReminderNote: secret.reminderNote,
|
secretReminderNote: secret.reminderNote,
|
||||||
metadata: secret.metadata,
|
metadata: secret.metadata,
|
||||||
|
secretMetadata: secret.secretMetadata,
|
||||||
createdAt: secret.createdAt,
|
createdAt: secret.createdAt,
|
||||||
updatedAt: secret.updatedAt
|
updatedAt: secret.updatedAt
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { ActorType } from "../auth/auth-type";
|
|||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { KmsDataKey } from "../kms/kms-types";
|
import { KmsDataKey } from "../kms/kms-types";
|
||||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||||
|
import { TResourceMetadataDALFactory } from "../resource-metadata/resource-metadata-dal";
|
||||||
import { TSecretQueueFactory } from "../secret/secret-queue";
|
import { TSecretQueueFactory } from "../secret/secret-queue";
|
||||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
@@ -74,6 +75,7 @@ type TSecretV2BridgeServiceFactoryDep = {
|
|||||||
"insertV2Bridge" | "insertApprovalSecretV2Tags"
|
"insertV2Bridge" | "insertApprovalSecretV2Tags"
|
||||||
>;
|
>;
|
||||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
|
export type TSecretV2BridgeServiceFactory = ReturnType<typeof secretV2BridgeServiceFactory>;
|
||||||
@@ -95,7 +97,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
secretApprovalPolicyService,
|
secretApprovalPolicyService,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretV2BridgeServiceFactoryDep) => {
|
}: TSecretV2BridgeServiceFactoryDep) => {
|
||||||
const $validateSecretReferences = async (
|
const $validateSecretReferences = async (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -141,7 +144,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -186,6 +189,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretMetadata,
|
||||||
...inputSecret
|
...inputSecret
|
||||||
}: TCreateSecretDTO) => {
|
}: TCreateSecretDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
||||||
@@ -255,6 +259,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const secret = await secretDAL.transaction((tx) =>
|
const secret = await secretDAL.transaction((tx) =>
|
||||||
fnSecretBulkInsert({
|
fnSecretBulkInsert({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
inputSecrets: [
|
inputSecrets: [
|
||||||
{
|
{
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -272,9 +277,11 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
key: secretName,
|
key: secretName,
|
||||||
userId: inputSecret.type === SecretType.Personal ? actorId : null,
|
userId: inputSecret.type === SecretType.Personal ? actorId : null,
|
||||||
tagIds: inputSecret.tagIds,
|
tagIds: inputSecret.tagIds,
|
||||||
references: nestedReferences
|
references: nestedReferences,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
@@ -287,6 +294,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
orgId: actorOrgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -309,6 +317,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretMetadata,
|
||||||
...inputSecret
|
...inputSecret
|
||||||
}: TUpdateSecretDTO) => {
|
}: TUpdateSecretDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
||||||
@@ -435,6 +444,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
const updatedSecret = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretBulkUpdate({
|
fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
inputSecrets: [
|
inputSecrets: [
|
||||||
{
|
{
|
||||||
filter: { id: secretId },
|
filter: { id: secretId },
|
||||||
@@ -448,6 +459,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: inputSecret.skipMultilineEncoding,
|
skipMultilineEncoding: inputSecret.skipMultilineEncoding,
|
||||||
key: inputSecret.newSecretName || secretName,
|
key: inputSecret.newSecretName || secretName,
|
||||||
tags: inputSecret.tagIds,
|
tags: inputSecret.tagIds,
|
||||||
|
secretMetadata,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,6 +487,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -562,6 +575,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -961,8 +975,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
? secretDAL.findOneWithTags({
|
? secretDAL.findOneWithTags({
|
||||||
folderId,
|
folderId,
|
||||||
type: secretType,
|
type: secretType,
|
||||||
key: secretName,
|
[`${TableName.SecretV2}.key` as "key"]: secretName,
|
||||||
userId: secretType === SecretType.Personal ? actorId : null
|
[`${TableName.SecretV2}.userId` as "userId"]: secretType === SecretType.Personal ? actorId : null
|
||||||
})
|
})
|
||||||
: secretVersionDAL
|
: secretVersionDAL
|
||||||
.findOne({
|
.findOne({
|
||||||
@@ -1113,7 +1127,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1185,11 +1199,14 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
key: el.secretKey,
|
key: el.secretKey,
|
||||||
tagIds: el.tagIds,
|
tagIds: el.tagIds,
|
||||||
references,
|
references,
|
||||||
|
secretMetadata: el.secretMetadata,
|
||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
@@ -1203,6 +1220,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1254,7 +1272,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1319,7 +1337,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1371,6 +1389,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
const secrets = await secretDAL.transaction(async (tx) =>
|
const secrets = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretBulkUpdate({
|
fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: actorOrgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: inputSecrets.map((el) => {
|
inputSecrets: inputSecrets.map((el) => {
|
||||||
const originalSecret = secretsToUpdateInDBGroupedByKey[el.secretKey][0];
|
const originalSecret = secretsToUpdateInDBGroupedByKey[el.secretKey][0];
|
||||||
@@ -1394,6 +1413,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
key: el.newSecretName || el.secretKey,
|
key: el.newSecretName || el.secretKey,
|
||||||
tags: el.tagIds,
|
tags: el.tagIds,
|
||||||
|
secretMetadata: el.secretMetadata,
|
||||||
...encryptedValue
|
...encryptedValue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1401,7 +1421,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL
|
secretVersionTagDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
@@ -1410,6 +1431,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1461,7 +1483,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
value: [
|
value: [
|
||||||
{
|
{
|
||||||
operator: "eq",
|
operator: "eq",
|
||||||
field: "key",
|
field: `${TableName.SecretV2}.key` as "key",
|
||||||
value: el.secretKey
|
value: el.secretKey
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1512,6 +1534,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1815,10 +1838,12 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
if (locallyCreatedSecrets.length) {
|
if (locallyCreatedSecrets.length) {
|
||||||
await fnSecretBulkInsert({
|
await fnSecretBulkInsert({
|
||||||
folderId: destinationFolder.id,
|
folderId: destinationFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
tx,
|
tx,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionTagDAL,
|
secretVersionTagDAL,
|
||||||
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
inputSecrets: locallyCreatedSecrets.map((doc) => {
|
||||||
return {
|
return {
|
||||||
@@ -1830,6 +1855,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
reminderNote: doc.reminderNote,
|
reminderNote: doc.reminderNote,
|
||||||
reminderRepeatDays: doc.reminderRepeatDays,
|
reminderRepeatDays: doc.reminderRepeatDays,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
references: doc.value ? getAllSecretReferences(doc.value).nestedReferences : []
|
references: doc.value ? getAllSecretReferences(doc.value).nestedReferences : []
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -1838,6 +1864,8 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
if (locallyUpdatedSecrets.length) {
|
if (locallyUpdatedSecrets.length) {
|
||||||
await fnSecretBulkUpdate({
|
await fnSecretBulkUpdate({
|
||||||
folderId: destinationFolder.id,
|
folderId: destinationFolder.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
tx,
|
tx,
|
||||||
@@ -1855,6 +1883,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
encryptedComment: doc.encryptedComment,
|
encryptedComment: doc.encryptedComment,
|
||||||
skipMultilineEncoding: doc.skipMultilineEncoding,
|
skipMultilineEncoding: doc.skipMultilineEncoding,
|
||||||
reminderNote: doc.reminderNote,
|
reminderNote: doc.reminderNote,
|
||||||
|
secretMetadata: doc.secretMetadata,
|
||||||
reminderRepeatDays: doc.reminderRepeatDays,
|
reminderRepeatDays: doc.reminderRepeatDays,
|
||||||
...(doc.encryptedValue
|
...(doc.encryptedValue
|
||||||
? {
|
? {
|
||||||
@@ -1938,6 +1967,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(destinationFolder.id);
|
await snapshotService.performSnapshot(destinationFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environment.slug,
|
environmentSlug: destinationFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -1949,6 +1979,7 @@ export const secretV2BridgeServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(sourceFolder.id);
|
await snapshotService.performSnapshot(sourceFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
secretPath: sourceFolder.path,
|
secretPath: sourceFolder.path,
|
||||||
environmentSlug: sourceFolder.environment.slug,
|
environmentSlug: sourceFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
|||||||
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
||||||
import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-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 { TSecretV2BridgeDALFactory } from "./secret-v2-bridge-dal";
|
||||||
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "./secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "./secret-version-tag-dal";
|
||||||
@@ -58,6 +60,7 @@ export type TCreateSecretDTO = TProjectPermission & {
|
|||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateSecretDTO = TProjectPermission & {
|
export type TUpdateSecretDTO = TProjectPermission & {
|
||||||
@@ -75,6 +78,7 @@ export type TUpdateSecretDTO = TProjectPermission & {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretDTO = TProjectPermission & {
|
export type TDeleteSecretDTO = TProjectPermission & {
|
||||||
@@ -94,6 +98,7 @@ export type TCreateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
@@ -113,6 +118,7 @@ export type TUpdateManySecretDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,8 +142,16 @@ export type TSecretReference = { environment: string; secretPath: string; secret
|
|||||||
|
|
||||||
export type TFnSecretBulkInsert = {
|
export type TFnSecretBulkInsert = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
|
orgId: string;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
inputSecrets: Array<Omit<TSecretsV2Insert, "folderId"> & { tagIds?: string[]; references: TSecretReference[] }>;
|
inputSecrets: Array<
|
||||||
|
Omit<TSecretsV2Insert, "folderId"> & {
|
||||||
|
tagIds?: string[];
|
||||||
|
references: TSecretReference[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "insertMany" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2">;
|
||||||
@@ -156,10 +170,12 @@ type TRequireReferenceIfValue =
|
|||||||
|
|
||||||
export type TFnSecretBulkUpdate = {
|
export type TFnSecretBulkUpdate = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
|
orgId: string;
|
||||||
inputSecrets: {
|
inputSecrets: {
|
||||||
filter: Partial<TSecretsV2>;
|
filter: Partial<TSecretsV2>;
|
||||||
data: TRequireReferenceIfValue & { tags?: string[] };
|
data: TRequireReferenceIfValue & { tags?: string[]; secretMetadata?: ResourceMetadataDTO };
|
||||||
}[];
|
}[];
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretV2BridgeDALFactory, "bulkUpdate" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionV2DALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecretV2" | "deleteTagsToSecretV2">;
|
||||||
|
|||||||
@@ -749,7 +749,8 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TCreateManySecretsRawFnFactory) => {
|
}: TCreateManySecretsRawFnFactory) => {
|
||||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||||
const createManySecretsRawFn = async ({
|
const createManySecretsRawFn = async ({
|
||||||
@@ -760,7 +761,7 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
userId
|
userId
|
||||||
}: TCreateManySecretsRawFn) => {
|
}: TCreateManySecretsRawFn) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -814,7 +815,9 @@ export const createManySecretsRawFnFactory = ({
|
|||||||
tagIds: el.tags
|
tagIds: el.tags
|
||||||
})),
|
})),
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
secretVersionTagDAL: secretVersionTagV2BridgeDAL,
|
||||||
@@ -909,6 +912,7 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
secretVersionTagV2BridgeDAL,
|
secretVersionTagV2BridgeDAL,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
|
resourceMetadataDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}: TUpdateManySecretsRawFnFactory) => {
|
}: TUpdateManySecretsRawFnFactory) => {
|
||||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||||
@@ -920,6 +924,7 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
userId
|
userId
|
||||||
}: TUpdateManySecretsRawFn): Promise<Array<{ id: string }>> => {
|
}: TUpdateManySecretsRawFn): Promise<Array<{ id: string }>> => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await getBotKeyFn(projectId);
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
@@ -988,11 +993,13 @@ export const updateManySecretsRawFnFactory = ({
|
|||||||
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
const updatedSecrets = await secretDAL.transaction(async (tx) =>
|
||||||
fnSecretV2BridgeBulkUpdate({
|
fnSecretV2BridgeBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
|
orgId: project.orgId,
|
||||||
tx,
|
tx,
|
||||||
inputSecrets: inputSecrets.map((el) => ({
|
inputSecrets: inputSecrets.map((el) => ({
|
||||||
filter: { id: secretsToUpdateInDBGroupedByKey[el.key][0].id, type: SecretType.Shared },
|
filter: { id: secretsToUpdateInDBGroupedByKey[el.key][0].id, type: SecretType.Shared },
|
||||||
data: el
|
data: el
|
||||||
})),
|
})),
|
||||||
|
resourceMetadataDAL,
|
||||||
secretDAL: secretV2BridgeDAL,
|
secretDAL: secretV2BridgeDAL,
|
||||||
secretVersionDAL: secretVersionV2BridgeDAL,
|
secretVersionDAL: secretVersionV2BridgeDAL,
|
||||||
secretTagDAL,
|
secretTagDAL,
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
|||||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-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 { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||||
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
||||||
@@ -104,6 +106,7 @@ type TSecretQueueFactoryDep = {
|
|||||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecrets = {
|
export type TGetSecrets = {
|
||||||
@@ -120,7 +123,12 @@ export const uniqueSecretQueueKey = (environment: string, secretPath: string) =>
|
|||||||
|
|
||||||
type TIntegrationSecret = Record<
|
type TIntegrationSecret = Record<
|
||||||
string,
|
string,
|
||||||
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
|
{
|
||||||
|
value: string;
|
||||||
|
comment?: string;
|
||||||
|
skipMultilineEncoding?: boolean | null | undefined;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// TODO(akhilmhdh): split this into multiple queue
|
// TODO(akhilmhdh): split this into multiple queue
|
||||||
@@ -157,7 +165,8 @@ export const secretQueueFactory = ({
|
|||||||
auditLogService,
|
auditLogService,
|
||||||
orgService,
|
orgService,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
projectKeyDAL
|
projectKeyDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
}: TSecretQueueFactoryDep) => {
|
}: TSecretQueueFactoryDep) => {
|
||||||
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
|
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
|
||||||
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
|
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
|
||||||
@@ -306,7 +315,8 @@ export const secretQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL
|
secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateManySecretsRawFn = updateManySecretsRawFnFactory({
|
const updateManySecretsRawFn = updateManySecretsRawFnFactory({
|
||||||
@@ -321,7 +331,8 @@ export const secretQueueFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
secretVersionV2BridgeDAL,
|
secretVersionV2BridgeDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
secretVersionTagV2BridgeDAL
|
secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -372,6 +383,7 @@ export const secretQueueFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
|
||||||
|
content[secretKey].secretMetadata = secret.secretMetadata;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -397,7 +409,8 @@ export const secretQueueFactory = ({
|
|||||||
content[importedSecret.key] = {
|
content[importedSecret.key] = {
|
||||||
skipMultilineEncoding: importedSecret.skipMultilineEncoding,
|
skipMultilineEncoding: importedSecret.skipMultilineEncoding,
|
||||||
comment: importedSecret.secretComment,
|
comment: importedSecret.secretComment,
|
||||||
value: importedSecret.secretValue || ""
|
value: importedSecret.secretValue || "",
|
||||||
|
secretMetadata: importedSecret.secretMetadata
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,6 +610,7 @@ export const secretQueueFactory = ({
|
|||||||
_depth: depth,
|
_depth: depth,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication,
|
excludeReplication,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -625,6 +639,7 @@ export const secretQueueFactory = ({
|
|||||||
_deDupeReplicationQueue: deDupeReplicationQueue,
|
_deDupeReplicationQueue: deDupeReplicationQueue,
|
||||||
_depth: depth,
|
_depth: depth,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId,
|
||||||
secretPath,
|
secretPath,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
@@ -681,6 +696,7 @@ export const secretQueueFactory = ({
|
|||||||
if (!folder) {
|
if (!folder) {
|
||||||
throw new Error("Secret path not found");
|
throw new Error("Secret path not found");
|
||||||
}
|
}
|
||||||
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
// find all imports made with the given environment and secret path
|
// find all imports made with the given environment and secret path
|
||||||
const linkSourceDto = {
|
const linkSourceDto = {
|
||||||
@@ -715,6 +731,7 @@ export const secretQueueFactory = ({
|
|||||||
.map(({ folderId }) =>
|
.map(({ folderId }) =>
|
||||||
syncSecrets({
|
syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
secretPath: foldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: foldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
_deDupeQueue: deDupeQueue,
|
_deDupeQueue: deDupeQueue,
|
||||||
@@ -767,6 +784,7 @@ export const secretQueueFactory = ({
|
|||||||
.map((folderId) =>
|
.map((folderId) =>
|
||||||
syncSecrets({
|
syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: referencedFoldersGroupedById[folderId][0]?.path as string,
|
secretPath: referencedFoldersGroupedById[folderId][0]?.path as string,
|
||||||
environmentSlug: referencedFoldersGroupedById[folderId][0]?.environmentSlug as string,
|
environmentSlug: referencedFoldersGroupedById[folderId][0]?.environmentSlug as string,
|
||||||
_deDupeQueue: deDupeQueue,
|
_deDupeQueue: deDupeQueue,
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -429,6 +430,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
|
orgId: actorOrgId,
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -526,6 +528,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -820,6 +823,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -928,6 +932,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1014,6 +1019,7 @@ export const secretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
secretPath: path,
|
secretPath: path,
|
||||||
projectId,
|
projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
environmentSlug: folder.environment.slug
|
environmentSlug: folder.environment.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1385,7 +1391,8 @@ export const secretServiceFactory = ({
|
|||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
tagIds,
|
tagIds,
|
||||||
secretReminderNote,
|
secretReminderNote,
|
||||||
secretReminderRepeatDays
|
secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}: TCreateSecretRawDTO) => {
|
}: TCreateSecretRawDTO) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
const policy =
|
const policy =
|
||||||
@@ -1412,7 +1419,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue,
|
secretValue,
|
||||||
tagIds,
|
tagIds,
|
||||||
reminderNote: secretReminderNote,
|
reminderNote: secretReminderNote,
|
||||||
reminderRepeatDays: secretReminderRepeatDays
|
reminderRepeatDays: secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1435,7 +1443,8 @@ export const secretServiceFactory = ({
|
|||||||
tagIds,
|
tagIds,
|
||||||
secretReminderNote,
|
secretReminderNote,
|
||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
secretReminderRepeatDays
|
secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return { secret, type: SecretProtectionType.Direct as const };
|
return { secret, type: SecretProtectionType.Direct as const };
|
||||||
}
|
}
|
||||||
@@ -1525,7 +1534,8 @@ export const secretServiceFactory = ({
|
|||||||
secretReminderRepeatDays,
|
secretReminderRepeatDays,
|
||||||
metadata,
|
metadata,
|
||||||
secretComment,
|
secretComment,
|
||||||
newSecretName
|
newSecretName,
|
||||||
|
secretMetadata
|
||||||
}: TUpdateSecretRawDTO) => {
|
}: TUpdateSecretRawDTO) => {
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
const policy =
|
const policy =
|
||||||
@@ -1553,7 +1563,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue,
|
secretValue,
|
||||||
tagIds,
|
tagIds,
|
||||||
reminderNote: secretReminderNote,
|
reminderNote: secretReminderNote,
|
||||||
reminderRepeatDays: secretReminderRepeatDays
|
reminderRepeatDays: secretReminderRepeatDays,
|
||||||
|
secretMetadata
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1577,7 +1588,8 @@ export const secretServiceFactory = ({
|
|||||||
secretName,
|
secretName,
|
||||||
newSecretName,
|
newSecretName,
|
||||||
metadata,
|
metadata,
|
||||||
secretValue
|
secretValue,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return { type: SecretProtectionType.Direct as const, secret };
|
return { type: SecretProtectionType.Direct as const, secret };
|
||||||
}
|
}
|
||||||
@@ -1793,7 +1805,8 @@ export const secretServiceFactory = ({
|
|||||||
secretComment: el.secretComment,
|
secretComment: el.secretComment,
|
||||||
metadata: el.metadata,
|
metadata: el.metadata,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
secretKey: el.secretKey
|
secretKey: el.secretKey,
|
||||||
|
secretMetadata: el.secretMetadata
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1919,7 +1932,8 @@ export const secretServiceFactory = ({
|
|||||||
secretValue: el.secretValue,
|
secretValue: el.secretValue,
|
||||||
secretComment: el.secretComment,
|
secretComment: el.secretComment,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
secretKey: el.secretKey
|
secretKey: el.secretKey,
|
||||||
|
secretMetadata: el.secretMetadata
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2262,6 +2276,7 @@ export const secretServiceFactory = ({
|
|||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication: true
|
excludeReplication: true
|
||||||
});
|
});
|
||||||
@@ -2370,6 +2385,7 @@ export const secretServiceFactory = ({
|
|||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
secretPath,
|
secretPath,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
environmentSlug: environment,
|
environmentSlug: environment,
|
||||||
excludeReplication: true
|
excludeReplication: true
|
||||||
});
|
});
|
||||||
@@ -2828,6 +2844,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(destinationFolder.id);
|
await snapshotService.performSnapshot(destinationFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: destinationFolder.path,
|
secretPath: destinationFolder.path,
|
||||||
environmentSlug: destinationFolder.environment.slug,
|
environmentSlug: destinationFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -2839,6 +2856,7 @@ export const secretServiceFactory = ({
|
|||||||
await snapshotService.performSnapshot(sourceFolder.id);
|
await snapshotService.performSnapshot(sourceFolder.id);
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
|
orgId: project.orgId,
|
||||||
secretPath: sourceFolder.path,
|
secretPath: sourceFolder.path,
|
||||||
environmentSlug: sourceFolder.environment.slug,
|
environmentSlug: sourceFolder.environment.slug,
|
||||||
actorId,
|
actorId,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { TSecretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
|||||||
|
|
||||||
import { ActorType } from "../auth/auth-type";
|
import { ActorType } from "../auth/auth-type";
|
||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
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 { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
|
||||||
@@ -211,6 +213,7 @@ export type TCreateSecretRawDTO = TProjectPermission & {
|
|||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateSecretRawDTO = TProjectPermission & {
|
export type TUpdateSecretRawDTO = TProjectPermission & {
|
||||||
@@ -228,6 +231,7 @@ export type TUpdateSecretRawDTO = TProjectPermission & {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretRawDTO = TProjectPermission & {
|
export type TDeleteSecretRawDTO = TProjectPermission & {
|
||||||
@@ -248,6 +252,7 @@ export type TCreateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
@@ -266,6 +271,7 @@ export type TUpdateManySecretRawDTO = Omit<TProjectPermission, "projectId"> & {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
}[];
|
}[];
|
||||||
@@ -293,7 +299,13 @@ export type TSecretReference = { environment: string; secretPath: string };
|
|||||||
export type TFnSecretBulkInsert = {
|
export type TFnSecretBulkInsert = {
|
||||||
folderId: string;
|
folderId: string;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
inputSecrets: Array<Omit<TSecretsInsert, "folderId"> & { tags?: string[]; references?: TSecretReference[] }>;
|
inputSecrets: Array<
|
||||||
|
Omit<TSecretsInsert, "folderId"> & {
|
||||||
|
tags?: string[];
|
||||||
|
references?: TSecretReference[];
|
||||||
|
secretMetadata?: ResourceMetadataDTO;
|
||||||
|
}
|
||||||
|
>;
|
||||||
secretDAL: Pick<TSecretDALFactory, "insertMany" | "upsertSecretReferences">;
|
secretDAL: Pick<TSecretDALFactory, "insertMany" | "upsertSecretReferences">;
|
||||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionDALFactory, "insertMany">;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "saveTagsToSecret">;
|
||||||
@@ -389,6 +401,7 @@ export type TCreateManySecretsRawFnFactory = {
|
|||||||
>;
|
>;
|
||||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateManySecretsRawFn = {
|
export type TCreateManySecretsRawFn = {
|
||||||
@@ -425,6 +438,7 @@ export type TUpdateManySecretsRawFnFactory = {
|
|||||||
>;
|
>;
|
||||||
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
secretVersionV2BridgeDAL: Pick<TSecretVersionV2DALFactory, "insertMany" | "findLatestVersionMany">;
|
||||||
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
secretVersionTagV2BridgeDAL: Pick<TSecretVersionV2TagDALFactory, "insertMany">;
|
||||||
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateManySecretsRawFn = {
|
export type TUpdateManySecretsRawFn = {
|
||||||
@@ -460,6 +474,7 @@ export type TSyncSecretsDTO<T extends boolean = false> = {
|
|||||||
_depth?: number;
|
_depth?: number;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
orgId: string;
|
||||||
environmentSlug: string;
|
environmentSlug: string;
|
||||||
// cases for just doing sync integration and webhook
|
// cases for just doing sync integration and webhook
|
||||||
excludeReplication?: T;
|
excludeReplication?: T;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 580 KiB |
@@ -17,6 +17,7 @@ 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.
|
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
|
The following steps are for instances not deployed on AWS
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Create an IAM User">
|
<Step title="Create an IAM User">
|
||||||
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
|
Navigate to [Create IAM User](https://console.aws.amazon.com/iamv2/home#/users/create) in your AWS Console.
|
||||||
@@ -40,9 +41,10 @@ The following steps are for instances not deployed on AWS
|
|||||||
<Step title="Obtain the IAM User Credentials">
|
<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.
|
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.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Set Up Integration Keys">
|
<Step title="Set Up Integration Keys">
|
||||||
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
|
1. Set the access key as **CLIENT_ID_AWS_INTEGRATION**.
|
||||||
@@ -59,6 +61,7 @@ The following steps are for instances not deployed on AWS
|
|||||||
2. Select **AWS Account** as the **Trusted Entity Type**.
|
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.
|
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.
|
4. Optionally, enable **Require external ID** and enter your **project ID** to further enhance security.
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Add Required Permissions for the IAM Role">
|
<Step title="Add Required Permissions for the IAM Role">
|
||||||
@@ -89,11 +92,13 @@ The following steps are for instances not deployed on AWS
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Copy the AWS IAM Role ARN">
|
<Step title="Copy the AWS IAM Role ARN">
|
||||||

|

|
||||||
|
</Step>
|
||||||
|
|
||||||
<Step title="Authorize Infisical for AWS Secrets Manager">
|
<Step title="Authorize Infisical for AWS Secrets Manager">
|
||||||
1. Navigate to your project's integrations tab in Infisical.
|
1. Navigate to your project's integrations tab in Infisical.
|
||||||
@@ -104,6 +109,7 @@ The following steps are for instances not deployed on AWS
|
|||||||

|

|
||||||
|
|
||||||
4. Provide the **AWS IAM Role ARN** obtained from the previous step.
|
4. Provide the **AWS IAM Role ARN** obtained from the previous step.
|
||||||
|
|
||||||
</Step> <Step title="Start integration">
|
</Step> <Step title="Start integration">
|
||||||
Select how you want to integration to work by specifying a number of parameters:
|
Select how you want to integration to work by specifying a number of parameters:
|
||||||
|
|
||||||
@@ -127,6 +133,12 @@ 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:
|
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>
|
<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.
|
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>
|
</ParamField>
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { createNotification } from "@app/components/notifications";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { workspaceKeys } from "../workspace";
|
import { workspaceKeys } from "../workspace";
|
||||||
import { TCloudIntegration, TIntegrationWithEnv, TOctopusDeployScopeValues } from "./types";
|
import {
|
||||||
|
IntegrationMetadataSyncMode,
|
||||||
|
TCloudIntegration,
|
||||||
|
TIntegrationWithEnv,
|
||||||
|
TOctopusDeployScopeValues
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const integrationQueryKeys = {
|
export const integrationQueryKeys = {
|
||||||
getIntegrations: () => ["integrations"] as const,
|
getIntegrations: () => ["integrations"] as const,
|
||||||
@@ -88,6 +93,7 @@ export const useCreateIntegration = () => {
|
|||||||
shouldProtectSecrets?: boolean;
|
shouldProtectSecrets?: boolean;
|
||||||
shouldEnableDelete?: boolean;
|
shouldEnableDelete?: boolean;
|
||||||
octopusDeployScopeValues?: TOctopusDeployScopeValues;
|
octopusDeployScopeValues?: TOctopusDeployScopeValues;
|
||||||
|
metadataSyncMode?: IntegrationMetadataSyncMode;
|
||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export type TIntegration = {
|
|||||||
octopusDeployScopeValues?: TOctopusDeployScopeValues;
|
octopusDeployScopeValues?: TOctopusDeployScopeValues;
|
||||||
awsIamRole?: string;
|
awsIamRole?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
|
metadataSyncMode?: IntegrationMetadataSyncMode;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,3 +93,8 @@ export enum IntegrationMappingBehavior {
|
|||||||
ONE_TO_ONE = "one-to-one",
|
ONE_TO_ONE = "one-to-one",
|
||||||
MANY_TO_ONE = "many-to-one"
|
MANY_TO_ONE = "many-to-one"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum IntegrationMetadataSyncMode {
|
||||||
|
CUSTOM = "custom",
|
||||||
|
SECRET_METADATA = "secret-metadata"
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export type TSecretApprovalRequest = {
|
|||||||
conflicts: Array<{ secretId: string; op: CommitType.UPDATE }>;
|
conflicts: Array<{ secretId: string; op: CommitType.UPDATE }>;
|
||||||
commits: ({
|
commits: ({
|
||||||
// if there is no secret means it was creation
|
// if there is no secret means it was creation
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
secret?: { version: number };
|
secret?: { version: number };
|
||||||
secretVersion: SecretV3Raw;
|
secretVersion: SecretV3Raw;
|
||||||
// if there is no new version its for Delete
|
// if there is no new version its for Delete
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ export const useUpdateSecretV3 = ({
|
|||||||
secretReminderRepeatDays,
|
secretReminderRepeatDays,
|
||||||
secretReminderNote,
|
secretReminderNote,
|
||||||
newSecretName,
|
newSecretName,
|
||||||
skipMultilineEncoding
|
skipMultilineEncoding,
|
||||||
|
secretMetadata
|
||||||
}) => {
|
}) => {
|
||||||
const { data } = await apiRequest.patch(`/api/v3/secrets/raw/${secretKey}`, {
|
const { data } = await apiRequest.patch(`/api/v3/secrets/raw/${secretKey}`, {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -97,7 +98,8 @@ export const useUpdateSecretV3 = ({
|
|||||||
newSecretName,
|
newSecretName,
|
||||||
secretComment,
|
secretComment,
|
||||||
tagIds,
|
tagIds,
|
||||||
secretValue
|
secretValue,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ export const mergePersonalSecrets = (rawSecrets: SecretV3Raw[]) => {
|
|||||||
updatedAt: el.updatedAt,
|
updatedAt: el.updatedAt,
|
||||||
version: el.version,
|
version: el.version,
|
||||||
skipMultilineEncoding: el.skipMultilineEncoding,
|
skipMultilineEncoding: el.skipMultilineEncoding,
|
||||||
path: el.secretPath
|
path: el.secretPath,
|
||||||
|
secretMetadata: el.secretMetadata
|
||||||
};
|
};
|
||||||
|
|
||||||
if (el.type === SecretType.Personal) {
|
if (el.type === SecretType.Personal) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export type SecretV3RawSanitized = {
|
|||||||
overrideAction?: string;
|
overrideAction?: string;
|
||||||
folderId?: string;
|
folderId?: string;
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SecretV3Raw = {
|
export type SecretV3Raw = {
|
||||||
@@ -63,6 +64,7 @@ export type SecretV3Raw = {
|
|||||||
secretComment?: string;
|
secretComment?: string;
|
||||||
secretReminderNote?: string;
|
secretReminderNote?: string;
|
||||||
secretReminderRepeatDays?: number;
|
secretReminderRepeatDays?: number;
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
skipMultilineEncoding?: boolean;
|
skipMultilineEncoding?: boolean;
|
||||||
metadata?: Record<string, string>;
|
metadata?: Record<string, string>;
|
||||||
tags?: WsTag[];
|
tags?: WsTag[];
|
||||||
@@ -148,6 +150,7 @@ export type TUpdateSecretsV3DTO = {
|
|||||||
secretReminderRepeatDays?: number | null;
|
secretReminderRepeatDays?: number | null;
|
||||||
secretReminderNote?: string | null;
|
secretReminderNote?: string | null;
|
||||||
tagIds?: string[];
|
tagIds?: string[];
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretsV3DTO = {
|
export type TDeleteSecretsV3DTO = {
|
||||||
|
|||||||
+2
-1
@@ -32,7 +32,8 @@ const metadataMappings: Record<keyof NonNullable<TIntegrationWithEnv["metadata"]
|
|||||||
shouldEnableDelete: "GitHub Secret Deletion Enabled",
|
shouldEnableDelete: "GitHub Secret Deletion Enabled",
|
||||||
octopusDeployScopeValues: "Octopus Deploy Scope Values",
|
octopusDeployScopeValues: "Octopus Deploy Scope Values",
|
||||||
awsIamRole: "AWS IAM Role",
|
awsIamRole: "AWS IAM Role",
|
||||||
region: "Region"
|
region: "Region",
|
||||||
|
metadataSyncMode: "Metadata Sync Mode"
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const IntegrationSettingsSection = ({ integration }: Props) => {
|
export const IntegrationSettingsSection = ({ integration }: Props) => {
|
||||||
|
|||||||
+92
-7
@@ -1,4 +1,4 @@
|
|||||||
import { faExclamationTriangle, faInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faExclamationTriangle, faInfo, faKey } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -18,7 +18,10 @@ import { CommitType, SecretV3Raw, TSecretApprovalSecChange, WsTag } from "@app/h
|
|||||||
export type Props = {
|
export type Props = {
|
||||||
op: CommitType;
|
op: CommitType;
|
||||||
secretVersion?: SecretV3Raw;
|
secretVersion?: SecretV3Raw;
|
||||||
newVersion?: Omit<TSecretApprovalSecChange, "tags"> & { tags?: WsTag[] };
|
newVersion?: Omit<TSecretApprovalSecChange, "tags"> & {
|
||||||
|
tags?: WsTag[];
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
|
};
|
||||||
presentSecretVersionNumber: number;
|
presentSecretVersionNumber: number;
|
||||||
hasMerged?: boolean;
|
hasMerged?: boolean;
|
||||||
conflicts: Array<{ secretId: string; op: CommitType }>;
|
conflicts: Array<{ secretId: string; op: CommitType }>;
|
||||||
@@ -80,11 +83,12 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
<Table>
|
<Table>
|
||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
{op === CommitType.UPDATE && <Th className="w-12" />}
|
{op === CommitType.UPDATE && <Th className="w-12 shrink-0" />}
|
||||||
<Th className="min-table-row">Secret</Th>
|
<Th className="w-48 shrink-0">Secret</Th>
|
||||||
<Th>Value</Th>
|
<Th className="min-w-0 flex-1">Value</Th>
|
||||||
<Th className="min-table-row">Comment</Th>
|
<Th className="w-24 shrink-0">Comment</Th>
|
||||||
<Th className="min-table-row">Tags</Th>
|
<Th className="w-24 shrink-0">Tags</Th>
|
||||||
|
<Th className="w-40 shrink-0">Metadata</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
{op === CommitType.UPDATE ? (
|
{op === CommitType.UPDATE ? (
|
||||||
@@ -110,6 +114,33 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</Td>
|
</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>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td className="text-green-600">NEW</Td>
|
<Td className="text-green-600">NEW</Td>
|
||||||
@@ -132,6 +163,33 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</Td>
|
</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>
|
</Tr>
|
||||||
</TBody>
|
</TBody>
|
||||||
) : (
|
) : (
|
||||||
@@ -173,6 +231,33 @@ export const SecretApprovalRequestChangeItem = ({
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</Td>
|
</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>
|
</Tr>
|
||||||
</TBody>
|
</TBody>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+76
-5
@@ -8,7 +8,8 @@ import {
|
|||||||
faClock,
|
faClock,
|
||||||
faPlus,
|
faPlus,
|
||||||
faShare,
|
faShare,
|
||||||
faTag
|
faTag,
|
||||||
|
faTrash
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@@ -26,6 +27,7 @@ import {
|
|||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
Switch,
|
Switch,
|
||||||
@@ -87,11 +89,16 @@ export const SecretDetailSidebar = ({
|
|||||||
|
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
|
|
||||||
const { fields, append, remove } = useFieldArray({
|
const tagFields = useFieldArray({
|
||||||
control,
|
control,
|
||||||
name: "tags"
|
name: "tags"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const metadataFormFields = useFieldArray({
|
||||||
|
control,
|
||||||
|
name: "secretMetadata"
|
||||||
|
});
|
||||||
|
|
||||||
const secretKey = secret?.key || "";
|
const secretKey = secret?.key || "";
|
||||||
const selectedTags = watch("tags", []) || [];
|
const selectedTags = watch("tags", []) || [];
|
||||||
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
|
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
|
||||||
@@ -153,10 +160,10 @@ export const SecretDetailSidebar = ({
|
|||||||
if (selectedTagsGroupById?.[tag.id]) {
|
if (selectedTagsGroupById?.[tag.id]) {
|
||||||
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
|
const tagPos = selectedTags.findIndex(({ id }) => id === tag.id);
|
||||||
if (tagPos !== -1) {
|
if (tagPos !== -1) {
|
||||||
remove(tagPos);
|
tagFields.remove(tagPos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
append(tag);
|
tagFields.append(tag);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,9 +284,73 @@ 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="">
|
<FormControl label="Tags" className="">
|
||||||
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
|
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
|
||||||
{fields.map(({ tagColor, id: formId, slug, id }) => (
|
{tagFields.fields.map(({ tagColor, id: formId, slug, id }) => (
|
||||||
<Tag
|
<Tag
|
||||||
className="flex w-min items-center space-x-2"
|
className="flex w-min items-center space-x-2"
|
||||||
key={formId}
|
key={formId}
|
||||||
|
|||||||
+15
-6
@@ -78,7 +78,8 @@ export const SecretListView = ({
|
|||||||
tags,
|
tags,
|
||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
newKey,
|
newKey,
|
||||||
secretId
|
secretId,
|
||||||
|
secretMetadata
|
||||||
}: Partial<{
|
}: Partial<{
|
||||||
value: string;
|
value: string;
|
||||||
comment: string;
|
comment: string;
|
||||||
@@ -88,6 +89,7 @@ export const SecretListView = ({
|
|||||||
skipMultilineEncoding: boolean;
|
skipMultilineEncoding: boolean;
|
||||||
newKey: string;
|
newKey: string;
|
||||||
secretId: string;
|
secretId: string;
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
}> = {}
|
}> = {}
|
||||||
) => {
|
) => {
|
||||||
if (operation === "delete") {
|
if (operation === "delete") {
|
||||||
@@ -115,7 +117,8 @@ export const SecretListView = ({
|
|||||||
secretReminderRepeatDays: reminderRepeatDays,
|
secretReminderRepeatDays: reminderRepeatDays,
|
||||||
secretReminderNote: reminderNote,
|
secretReminderNote: reminderNote,
|
||||||
skipMultilineEncoding,
|
skipMultilineEncoding,
|
||||||
newSecretName: newKey
|
newSecretName: newKey,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,7 +141,10 @@ export const SecretListView = ({
|
|||||||
const handleSaveSecret = useCallback(
|
const handleSaveSecret = useCallback(
|
||||||
async (
|
async (
|
||||||
orgSecret: SecretV3RawSanitized,
|
orgSecret: SecretV3RawSanitized,
|
||||||
modSecret: Omit<SecretV3RawSanitized, "tags"> & { tags?: { id: string }[] },
|
modSecret: Omit<SecretV3RawSanitized, "tags"> & {
|
||||||
|
tags?: { id: string }[];
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
|
},
|
||||||
cb?: () => void
|
cb?: () => void
|
||||||
) => {
|
) => {
|
||||||
const { key: oldKey } = orgSecret;
|
const { key: oldKey } = orgSecret;
|
||||||
@@ -151,7 +157,8 @@ export const SecretListView = ({
|
|||||||
tags,
|
tags,
|
||||||
comment,
|
comment,
|
||||||
reminderRepeatDays,
|
reminderRepeatDays,
|
||||||
reminderNote
|
reminderNote,
|
||||||
|
secretMetadata
|
||||||
} = modSecret;
|
} = modSecret;
|
||||||
const hasKeyChanged = oldKey !== key && key;
|
const hasKeyChanged = oldKey !== key && key;
|
||||||
|
|
||||||
@@ -166,7 +173,8 @@ export const SecretListView = ({
|
|||||||
"comment",
|
"comment",
|
||||||
"skipMultilineEncoding",
|
"skipMultilineEncoding",
|
||||||
"reminderRepeatDays",
|
"reminderRepeatDays",
|
||||||
"reminderNote"
|
"reminderNote",
|
||||||
|
"secretMetadata"
|
||||||
] as const
|
] as const
|
||||||
).every((el) => orgSecret[el] === modSecret[el]) && isSameTags;
|
).every((el) => orgSecret[el] === modSecret[el]) && isSameTags;
|
||||||
|
|
||||||
@@ -199,7 +207,8 @@ export const SecretListView = ({
|
|||||||
reminderNote,
|
reminderNote,
|
||||||
secretId: orgSecret.id,
|
secretId: orgSecret.id,
|
||||||
newKey: hasKeyChanged ? key : undefined,
|
newKey: hasKeyChanged ? key : undefined,
|
||||||
skipMultilineEncoding: modSecret.skipMultilineEncoding
|
skipMultilineEncoding: modSecret.skipMultilineEncoding,
|
||||||
|
secretMetadata
|
||||||
});
|
});
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -47,7 +47,13 @@ export const formSchema = z.object({
|
|||||||
.nullable()
|
.nullable()
|
||||||
.optional(),
|
.optional(),
|
||||||
reminderNote: z.string().trim().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
|
tags: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|||||||
+90
-28
@@ -34,7 +34,10 @@ import { useWorkspace } from "@app/context";
|
|||||||
import { useCreateIntegration } from "@app/hooks/api";
|
import { useCreateIntegration } from "@app/hooks/api";
|
||||||
import { useGetIntegrationAuthById } from "@app/hooks/api/integrationAuth";
|
import { useGetIntegrationAuthById } from "@app/hooks/api/integrationAuth";
|
||||||
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
|
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
|
||||||
import { IntegrationMappingBehavior } from "@app/hooks/api/integrations/types";
|
import {
|
||||||
|
IntegrationMappingBehavior,
|
||||||
|
IntegrationMetadataSyncMode
|
||||||
|
} from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
enum TabSections {
|
enum TabSections {
|
||||||
Connection = "connection",
|
Connection = "connection",
|
||||||
@@ -94,6 +97,7 @@ const schema = z
|
|||||||
mappingBehavior: z.nativeEnum(IntegrationMappingBehavior),
|
mappingBehavior: z.nativeEnum(IntegrationMappingBehavior),
|
||||||
kmsKeyId: z.string().optional(),
|
kmsKeyId: z.string().optional(),
|
||||||
shouldTag: z.boolean().optional(),
|
shouldTag: z.boolean().optional(),
|
||||||
|
metadataSyncMode: z.nativeEnum(IntegrationMetadataSyncMode).optional(),
|
||||||
tags: z
|
tags: z
|
||||||
.object({
|
.object({
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
@@ -136,6 +140,7 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const shouldTagState = watch("shouldTag");
|
const shouldTagState = watch("shouldTag");
|
||||||
|
const selectedMetadataSyncMode = watch("metadataSyncMode");
|
||||||
const selectedSourceEnvironment = watch("sourceEnvironment");
|
const selectedSourceEnvironment = watch("sourceEnvironment");
|
||||||
const selectedAWSRegion = watch("awsRegion");
|
const selectedAWSRegion = watch("awsRegion");
|
||||||
const selectedMappingBehavior = watch("mappingBehavior");
|
const selectedMappingBehavior = watch("mappingBehavior");
|
||||||
@@ -171,7 +176,8 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
tags,
|
tags,
|
||||||
secretPrefix,
|
secretPrefix,
|
||||||
kmsKeyId,
|
kmsKeyId,
|
||||||
mappingBehavior
|
mappingBehavior,
|
||||||
|
metadataSyncMode
|
||||||
}: TFormSchema) => {
|
}: TFormSchema) => {
|
||||||
try {
|
try {
|
||||||
if (!integrationAuth?.id) return;
|
if (!integrationAuth?.id) return;
|
||||||
@@ -186,7 +192,8 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
metadata: {
|
metadata: {
|
||||||
...(shouldTag
|
...(shouldTag
|
||||||
? {
|
? {
|
||||||
secretAWSTag: tags
|
secretAWSTag: tags,
|
||||||
|
metadataSyncMode
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(secretPrefix && { secretPrefix }),
|
...(secretPrefix && { secretPrefix }),
|
||||||
@@ -339,7 +346,12 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
defaultValue={field.value}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => {
|
||||||
|
if (e === IntegrationMappingBehavior.MANY_TO_ONE) {
|
||||||
|
setValue("metadataSyncMode", IntegrationMetadataSyncMode.CUSTOM);
|
||||||
|
}
|
||||||
|
onChange(e);
|
||||||
|
}}
|
||||||
className="w-full border border-mineshaft-500"
|
className="w-full border border-mineshaft-500"
|
||||||
dropdownContainerClassName="max-w-full"
|
dropdownContainerClassName="max-w-full"
|
||||||
>
|
>
|
||||||
@@ -386,14 +398,25 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
<div className="ml-1 mt-2">
|
<div className="mb-3 ml-1 mt-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="shouldTag"
|
name="shouldTag"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<Switch
|
<Switch
|
||||||
id="tag-aws"
|
id="tag-aws"
|
||||||
onCheckedChange={(isChecked) => onChange(isChecked)}
|
onCheckedChange={(isChecked) => {
|
||||||
|
if (
|
||||||
|
isChecked &&
|
||||||
|
selectedMappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE
|
||||||
|
) {
|
||||||
|
setValue(
|
||||||
|
"metadataSyncMode",
|
||||||
|
IntegrationMetadataSyncMode.SECRET_METADATA
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onChange(isChecked);
|
||||||
|
}}
|
||||||
isChecked={value}
|
isChecked={value}
|
||||||
>
|
>
|
||||||
Tag in AWS Secrets Manager
|
Tag in AWS Secrets Manager
|
||||||
@@ -401,37 +424,76 @@ export const AwsSecretManagerConfigurePage = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{shouldTagState && (
|
{shouldTagState &&
|
||||||
<div className="mt-4 flex justify-between">
|
selectedMappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE && (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="tags.0.key"
|
name="metadataSyncMode"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Tag Key"
|
label="Tag Sync Mode"
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
>
|
>
|
||||||
<Input placeholder="managed-by" {...field} />
|
<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>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
)}
|
||||||
control={control}
|
{shouldTagState &&
|
||||||
name="tags.0.value"
|
selectedMetadataSyncMode === IntegrationMetadataSyncMode.CUSTOM && (
|
||||||
render={({ field, fieldState: { error } }) => (
|
<div className="mt-4 flex justify-between">
|
||||||
<FormControl
|
<Controller
|
||||||
label="Tag Value"
|
control={control}
|
||||||
errorText={error?.message}
|
name="tags.0.key"
|
||||||
isError={Boolean(error)}
|
render={({ field, fieldState: { error } }) => (
|
||||||
>
|
<FormControl
|
||||||
<Input placeholder="infisical" {...field} />
|
label="Tag Key"
|
||||||
</FormControl>
|
errorText={error?.message}
|
||||||
)}
|
isError={Boolean(error)}
|
||||||
/>
|
>
|
||||||
</div>
|
<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
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="secretPrefix"
|
name="secretPrefix"
|
||||||
|
|||||||
Reference in New Issue
Block a user