diff --git a/backend/src/db/migrations/20241218165837_resource-metadata.ts b/backend/src/db/migrations/20241218165837_resource-metadata.ts index 90313b344..af62f9895 100644 --- a/backend/src/db/migrations/20241218165837_resource-metadata.ts +++ b/backend/src/db/migrations/20241218165837_resource-metadata.ts @@ -19,8 +19,22 @@ export async function up(knex: Knex): Promise { 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 { 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"); + }); + } } diff --git a/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts b/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts index ee25ed6ef..298985fed 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts @@ -24,7 +24,8 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({ requestId: z.string().uuid(), op: z.string(), 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; diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index e1c56583c..fe4d8ee81 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -12,6 +12,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { secretRawSchema } from "@app/server/routes/sanitizedSchemas"; import { AuthMode } from "@app/services/auth/auth-type"; +import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema"; const approvalRequestUser = z.object({ userId: z.string().nullable().optional() }).merge( UsersSchema.pick({ @@ -274,6 +275,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv .extend({ op: z.string(), tags: tagSchema, + secretMetadata: ResourceMetadataSchema.nullish(), secret: z .object({ id: z.string(), @@ -291,7 +293,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv secretKey: z.string(), secretValue: z.string().optional(), secretComment: z.string().optional(), - tags: tagSchema + tags: tagSchema, + secretMetadata: ResourceMetadataSchema.nullish() }) .optional() }) diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts index 77f38dd53..f72977a9b 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts @@ -256,6 +256,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { `${TableName.SecretVersionV2Tag}.${TableName.SecretTag}Id`, db.ref("id").withSchema("secVerTag") ) + .leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`) .select(selectAllTableCols(TableName.SecretApprovalRequestSecretV2)) .select({ secVerTagId: "secVerTag.id", @@ -279,6 +280,11 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { db.ref("key").withSchema(TableName.SecretVersionV2).as("secVerKey"), db.ref("encryptedValue").withSchema(TableName.SecretVersionV2).as("secVerValue"), db.ref("encryptedComment").withSchema(TableName.SecretVersionV2).as("secVerComment") + ) + .select( + db.ref("id").withSchema(TableName.ResourceMetadata).as("metadataId"), + db.ref("key").withSchema(TableName.ResourceMetadata).as("metadataKey"), + db.ref("value").withSchema(TableName.ResourceMetadata).as("metadataValue") ); const formatedDoc = sqlNestRelationships({ data: doc, @@ -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 }) => ({ ...el, secret: secret?.[0], diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 430f49214..77f195dc2 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -23,6 +23,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TResourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal"; +import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema"; import { TSecretDALFactory } from "@app/services/secret/secret-dal"; import { decryptSecretWithBot, @@ -244,6 +245,7 @@ export const secretApprovalRequestServiceFactory = ({ secretKey: el.key, id: el.id, version: el.version, + secretMetadata: el.secretMetadata as ResourceMetadataDTO, secretValue: el.encryptedValue ? secretManagerDecryptor({ cipherTextBlob: el.encryptedValue }).toString() : "", secretComment: el.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.encryptedComment }).toString() @@ -272,7 +274,8 @@ export const secretApprovalRequestServiceFactory = ({ secretComment: el.secretVersion.encryptedComment ? secretManagerDecryptor({ cipherTextBlob: el.secretVersion.encryptedComment }).toString() : "", - tags: el.secretVersion.tags + tags: el.secretVersion.tags, + secretMetadata: el.oldSecretMetadata as ResourceMetadataDTO } : undefined })); @@ -554,6 +557,7 @@ export const secretApprovalRequestServiceFactory = ({ encryptedValue: el.encryptedValue, skipMultilineEncoding: el.skipMultilineEncoding, key: el.key, + secretMetadata: el.secretMetadata as ResourceMetadataDTO, references: el.encryptedValue ? getAllSecretReferencesV2Bridge( secretManagerDecryptor({ @@ -598,6 +602,7 @@ export const secretApprovalRequestServiceFactory = ({ skipMultilineEncoding: el.skipMultilineEncoding, key: el.key, tags: el?.tags.map(({ id }) => id), + secretMetadata: el.secretMetadata as ResourceMetadataDTO, ...encryptedValue } }; @@ -1216,6 +1221,7 @@ export const secretApprovalRequestServiceFactory = ({ ), skipMultilineEncoding: createdSecret.skipMultilineEncoding, key: createdSecret.secretKey, + secretMetadata: createdSecret.secretMetadata, type: SecretType.Shared })) ); @@ -1271,12 +1277,14 @@ export const secretApprovalRequestServiceFactory = ({ reminderNote, secretComment, metadata, - skipMultilineEncoding + skipMultilineEncoding, + secretMetadata }) => { const secretId = updatingSecretsGroupByKey[secretKey][0].id; if (tagIds?.length) commitTagIds[secretKey] = tagIds; return { ...latestSecretVersions[secretId], + secretMetadata, key: newSecretName || secretKey, encryptedComment: setKnexStringValue( secretComment, @@ -1378,7 +1386,8 @@ export const secretApprovalRequestServiceFactory = ({ reminderRepeatDays, encryptedValue, secretId, - secretVersion + secretVersion, + secretMetadata }) => ({ version, requestId: doc.id, @@ -1391,7 +1400,8 @@ export const secretApprovalRequestServiceFactory = ({ reminderRepeatDays, reminderNote, encryptedComment, - key + key, + secretMetadata: JSON.stringify(secretMetadata) }) ), tx diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts index 50a70fd60..89af253dd 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts @@ -1,5 +1,6 @@ import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; +import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema"; import { SecretOperations } from "@app/services/secret/secret-types"; export enum RequestState { @@ -34,6 +35,7 @@ export type TApprovalCreateSecretV2Bridge = { reminderRepeatDays?: number | null; skipMultilineEncoding?: boolean; metadata?: Record; + secretMetadata?: ResourceMetadataDTO; tagIds?: string[]; }; diff --git a/backend/src/ee/services/secret-replication/secret-replication-service.ts b/backend/src/ee/services/secret-replication/secret-replication-service.ts index 815e255e9..e72f3b4fa 100644 --- a/backend/src/ee/services/secret-replication/secret-replication-service.ts +++ b/backend/src/ee/services/secret-replication/secret-replication-service.ts @@ -396,6 +396,7 @@ export const secretReplicationServiceFactory = ({ op: operation, requestId: approvalRequestDoc.id, metadata: doc.metadata, + secretMetadata: JSON.stringify(doc.secretMetadata), key: doc.key, encryptedValue: doc.encryptedValue, encryptedComment: doc.encryptedComment, diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 89d44e51e..f353c3800 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -1419,7 +1419,8 @@ export const secretServiceFactory = ({ secretValue, tagIds, reminderNote: secretReminderNote, - reminderRepeatDays: secretReminderRepeatDays + reminderRepeatDays: secretReminderRepeatDays, + secretMetadata } ] } @@ -1562,7 +1563,8 @@ export const secretServiceFactory = ({ secretValue, tagIds, reminderNote: secretReminderNote, - reminderRepeatDays: secretReminderRepeatDays + reminderRepeatDays: secretReminderRepeatDays, + secretMetadata } ] } @@ -1803,7 +1805,8 @@ export const secretServiceFactory = ({ secretComment: el.secretComment, metadata: el.metadata, skipMultilineEncoding: el.skipMultilineEncoding, - secretKey: el.secretKey + secretKey: el.secretKey, + secretMetadata: el.secretMetadata })) } }); diff --git a/backend/src/services/secret/secret-types.ts b/backend/src/services/secret/secret-types.ts index 662ff6ed4..aa6902556 100644 --- a/backend/src/services/secret/secret-types.ts +++ b/backend/src/services/secret/secret-types.ts @@ -252,6 +252,7 @@ export type TCreateManySecretRawDTO = Omit & { secretComment?: string; skipMultilineEncoding?: boolean; tagIds?: string[]; + secretMetadata?: ResourceMetadataDTO; metadata?: { source?: string; }; diff --git a/frontend/src/hooks/api/secretApprovalRequest/types.ts b/frontend/src/hooks/api/secretApprovalRequest/types.ts index c8a685115..a03526dae 100644 --- a/frontend/src/hooks/api/secretApprovalRequest/types.ts +++ b/frontend/src/hooks/api/secretApprovalRequest/types.ts @@ -82,6 +82,7 @@ export type TSecretApprovalRequest = { conflicts: Array<{ secretId: string; op: CommitType.UPDATE }>; commits: ({ // if there is no secret means it was creation + secretMetadata?: { key: string; value: string }[]; secret?: { version: number }; secretVersion: SecretV3Raw; // if there is no new version its for Delete diff --git a/frontend/src/views/SecretApprovalPage/components/SecretApprovalRequest/components/SecretApprovalRequestChangeItem.tsx b/frontend/src/views/SecretApprovalPage/components/SecretApprovalRequest/components/SecretApprovalRequestChangeItem.tsx index 0c14e7a58..3348b0bc2 100644 --- a/frontend/src/views/SecretApprovalPage/components/SecretApprovalRequest/components/SecretApprovalRequestChangeItem.tsx +++ b/frontend/src/views/SecretApprovalPage/components/SecretApprovalRequest/components/SecretApprovalRequestChangeItem.tsx @@ -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 { @@ -18,7 +18,10 @@ import { CommitType, SecretV3Raw, TSecretApprovalSecChange, WsTag } from "@app/h export type Props = { op: CommitType; secretVersion?: SecretV3Raw; - newVersion?: Omit & { tags?: WsTag[] }; + newVersion?: Omit & { + tags?: WsTag[]; + secretMetadata?: { key: string; value: string }[]; + }; presentSecretVersionNumber: number; hasMerged?: Boolean; conflicts: Array<{ secretId: string; op: CommitType }>; @@ -80,11 +83,12 @@ export const SecretApprovalRequestChangeItem = ({ - {op === CommitType.UPDATE && - - - + {op === CommitType.UPDATE && + + + + {op === CommitType.UPDATE ? ( @@ -110,6 +114,33 @@ export const SecretApprovalRequestChangeItem = ({ ))} + @@ -132,6 +163,33 @@ export const SecretApprovalRequestChangeItem = ({ ))} + ) : ( @@ -173,6 +231,33 @@ export const SecretApprovalRequestChangeItem = ({ ) )} + )}
} - SecretValueCommentTags} + SecretValueCommentTagsMetadata
+ {secretVersion?.secretMetadata?.length ? ( +
+ {secretVersion.secretMetadata?.map((el) => ( +
+ + +
{el.key}
+
+ +
+ {el.value} +
+
+
+ ))} +
+ ) : ( +

-

+ )} +
NEW + {newVersion?.secretMetadata?.length ? ( +
+ {newVersion.secretMetadata?.map((el) => ( +
+ + +
{el.key}
+
+ +
+ {el.value} +
+
+
+ ))} +
+ ) : ( +

-

+ )} +
+ {newVersion?.secretMetadata?.length ? ( +
+ {newVersion.secretMetadata?.map((el) => ( +
+ + +
{el.key}
+
+ +
+ {el.value} +
+
+
+ ))} +
+ ) : ( +

-

+ )} +