feat(infisical-pg): completed tag support to all secret operations

This commit is contained in:
Akhil Mohan
2024-01-27 12:39:54 +05:30
parent 42eb01e1e2
commit 80b6c4ad94
10 changed files with 371 additions and 206 deletions
+26 -25
View File
@@ -83,23 +83,23 @@ import {
TSamlConfigs, TSamlConfigs,
TSamlConfigsInsert, TSamlConfigsInsert,
TSamlConfigsUpdate, TSamlConfigsUpdate,
TSapApprovers,
TSapApproversInsert,
TSapApproversUpdate,
TSaRequestSecrets,
TSaRequestSecretsInsert,
TSaRequestSecretsUpdate,
TSaRequestSecretTags,
TSaRequestSecretTagsInsert,
TSaRequestSecretTagsUpdate,
TSarReviewers,
TSarReviewersInsert,
TSarReviewersUpdate,
TSecretApprovalPolicies, TSecretApprovalPolicies,
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate,
TSecretApprovalPoliciesInsert, TSecretApprovalPoliciesInsert,
TSecretApprovalPoliciesUpdate, TSecretApprovalPoliciesUpdate,
TSecretApprovalRequests, TSecretApprovalRequests,
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate,
TSecretApprovalRequestsInsert, TSecretApprovalRequestsInsert,
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
TSecretApprovalRequestsReviewersUpdate,
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate,
TSecretApprovalRequestsUpdate, TSecretApprovalRequestsUpdate,
TSecretBlindIndexes, TSecretBlindIndexes,
TSecretBlindIndexesInsert, TSecretBlindIndexesInsert,
@@ -166,7 +166,8 @@ import {
TUsersUpdate, TUsersUpdate,
TWebhooks, TWebhooks,
TWebhooksInsert, TWebhooksInsert,
TWebhooksUpdate} from "@app/db/schemas"; TWebhooksUpdate
} from "@app/db/schemas";
declare module "knex/types/tables" { declare module "knex/types/tables" {
interface Tables { interface Tables {
@@ -327,9 +328,9 @@ declare module "knex/types/tables" {
TSecretApprovalPoliciesUpdate TSecretApprovalPoliciesUpdate
>; >;
[TableName.SecretApprovalPolicyApprover]: Knex.CompositeTableType< [TableName.SecretApprovalPolicyApprover]: Knex.CompositeTableType<
TSapApprovers, TSecretApprovalPoliciesApprovers,
TSapApproversInsert, TSecretApprovalPoliciesApproversInsert,
TSapApproversUpdate TSecretApprovalPoliciesApproversUpdate
>; >;
[TableName.SecretApprovalRequest]: Knex.CompositeTableType< [TableName.SecretApprovalRequest]: Knex.CompositeTableType<
TSecretApprovalRequests, TSecretApprovalRequests,
@@ -337,19 +338,19 @@ declare module "knex/types/tables" {
TSecretApprovalRequestsUpdate TSecretApprovalRequestsUpdate
>; >;
[TableName.SecretApprovalRequestReviewer]: Knex.CompositeTableType< [TableName.SecretApprovalRequestReviewer]: Knex.CompositeTableType<
TSarReviewers, TSecretApprovalRequestsReviewers,
TSarReviewersInsert, TSecretApprovalRequestsReviewersInsert,
TSarReviewersUpdate TSecretApprovalRequestsReviewersUpdate
>; >;
[TableName.SecretApprovalRequestSecret]: Knex.CompositeTableType< [TableName.SecretApprovalRequestSecret]: Knex.CompositeTableType<
TSaRequestSecrets, TSecretApprovalRequestsSecrets,
TSaRequestSecretsInsert, TSecretApprovalRequestsSecretsInsert,
TSaRequestSecretsUpdate TSecretApprovalRequestsSecretsUpdate
>; >;
[TableName.SecretApprovalRequestSecretTag]: Knex.CompositeTableType< [TableName.SecretApprovalRequestSecretTag]: Knex.CompositeTableType<
TSaRequestSecretTags, TSecretApprovalRequestSecretTags,
TSaRequestSecretTagsInsert, TSecretApprovalRequestSecretTagsInsert,
TSaRequestSecretTagsUpdate TSecretApprovalRequestSecretTagsUpdate
>; >;
[TableName.SecretRotation]: Knex.CompositeTableType< [TableName.SecretRotation]: Knex.CompositeTableType<
TSecretRotations, TSecretRotations,
@@ -5,6 +5,7 @@ import {
SecretApprovalRequestsSchema, SecretApprovalRequestsSchema,
SecretApprovalRequestsSecretsSchema, SecretApprovalRequestsSecretsSchema,
SecretsSchema, SecretsSchema,
SecretTagsSchema,
SecretVersionsSchema SecretVersionsSchema
} from "@app/db/schemas"; } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
@@ -90,77 +91,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
} }
}); });
server.route({
url: "/:id",
method: "GET",
schema: {
params: z.object({
id: z.string()
}),
response: {
200: z.object({
approval: SecretApprovalRequestsSchema.merge(
z.object({
// secretPath: z.string(),
policy: z.object({
id: z.string(),
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().optional().nullable()
}),
environment: z.string(),
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
approvers: z.string().array(),
secretPath: z.string(),
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
.merge(
z.object({
secret: SecretsSchema.pick({
id: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
})
.optional()
.nullable(),
secretVersion: SecretVersionsSchema.pick({
id: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
}).optional()
})
)
.array()
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({
actor: req.permission.type,
actorId: req.permission.id,
id: req.params.id
});
return { approval };
}
});
server.route({ server.route({
url: "/:id/merge", url: "/:id/merge",
method: "POST", method: "POST",
@@ -257,4 +187,92 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
return { approval }; return { approval };
} }
}); });
const tagSchema = SecretTagsSchema.pick({
id: true,
slug: true,
name: true,
color: true
})
.array()
.optional();
server.route({
url: "/:id",
method: "GET",
schema: {
params: z.object({
id: z.string()
}),
response: {
200: z.object({
approval: SecretApprovalRequestsSchema.merge(
z.object({
// secretPath: z.string(),
policy: z.object({
id: z.string(),
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().optional().nullable()
}),
environment: z.string(),
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
approvers: z.string().array(),
secretPath: z.string(),
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
.merge(
z.object({
tags: tagSchema,
secret: SecretsSchema.pick({
id: true,
version: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
})
.optional()
.nullable(),
secretVersion: SecretVersionsSchema.pick({
id: true,
version: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
})
.merge(
z.object({
tags: tagSchema
})
)
.optional()
})
)
.array()
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({
actor: req.permission.type,
actorId: req.permission.id,
id: req.params.id
});
return { approval };
}
});
}; };
@@ -1,19 +1,35 @@
import { Knex } from "knex"; import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas"; import { SecretApprovalRequestsSecretsSchema, TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols } from "@app/lib/knex"; import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex";
export type TSecretApprovalRequestSecretDALFactory = ReturnType<typeof secretApprovalRequestSecretDALFactory>; export type TSecretApprovalRequestSecretDALFactory = ReturnType<
typeof secretApprovalRequestSecretDALFactory
>;
export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
const sarSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret); const secretApprovalRequestSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret);
const secretApprovalRequestSecretTagOrm = ormify(db, TableName.SecretApprovalRequestSecretTag);
const findByRequestId = async (requestId: string, tx?: Knex) => { const findByRequestId = async (requestId: string, tx?: Knex) => {
try { try {
const doc = await (tx || db)(TableName.SecretApprovalRequestSecret) const doc = await (tx || db)({
secVerTag: TableName.SecretTag
})
.from(TableName.SecretApprovalRequestSecret)
.where({ requestId }) .where({ requestId })
.leftJoin(
TableName.SecretApprovalRequestSecretTag,
`${TableName.SecretApprovalRequestSecret}.id`,
`${TableName.SecretApprovalRequestSecretTag}.secretId`
)
.leftJoin(
TableName.SecretTag,
`${TableName.SecretApprovalRequestSecretTag}.tagId`,
`${TableName.SecretTag}.id`
)
.leftJoin( .leftJoin(
TableName.Secret, TableName.Secret,
`${TableName.SecretApprovalRequestSecret}.secretId`, `${TableName.SecretApprovalRequestSecret}.secretId`,
@@ -24,7 +40,30 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
`${TableName.SecretVersion}.id`, `${TableName.SecretVersion}.id`,
`${TableName.SecretApprovalRequestSecret}.secretVersion` `${TableName.SecretApprovalRequestSecret}.secretVersion`
) )
.leftJoin(
TableName.SecretVersionTag,
`${TableName.SecretVersionTag}.${TableName.SecretVersion}Id`,
`${TableName.SecretVersion}.id`
)
.leftJoin(
db.ref(TableName.SecretTag).as("secVerTag"),
`${TableName.SecretVersionTag}.${TableName.SecretTag}Id`,
db.ref("id").withSchema("secVerTag")
)
.select(selectAllTableCols(TableName.SecretApprovalRequestSecret)) .select(selectAllTableCols(TableName.SecretApprovalRequestSecret))
.select({
secVerTagId: "secVerTag.id",
secVerTagColor: "secVerTag.color",
secVerTagSlug: "secVerTag.slug",
secVerTagName: "secVerTag.name"
})
.select(
db.ref("id").withSchema(TableName.SecretTag).as("tagId"),
db.ref("id").withSchema(TableName.SecretApprovalRequestSecretTag).as("tagJnId"),
db.ref("color").withSchema(TableName.SecretTag).as("tagColor"),
db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"),
db.ref("name").withSchema(TableName.SecretTag).as("tagName")
)
.select( .select(
db.ref("secretBlindIndex").withSchema(TableName.Secret).as("orgSecBlindIndex"), db.ref("secretBlindIndex").withSchema(TableName.Secret).as("orgSecBlindIndex"),
db.ref("version").withSchema(TableName.Secret).as("orgSecVersion"), db.ref("version").withSchema(TableName.Secret).as("orgSecVersion"),
@@ -42,7 +81,6 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
.as("orgSecCommentCiphertext") .as("orgSecCommentCiphertext")
) )
.select( .select(
// db.ref("secretBlindIndex").withSchema(TableName.Secret).as("orgSecBlindInex"),
db.ref("version").withSchema(TableName.SecretVersion).as("secVerVersion"), db.ref("version").withSchema(TableName.SecretVersion).as("secVerVersion"),
db.ref("secretKeyIV").withSchema(TableName.SecretVersion).as("secVerKeyIV"), db.ref("secretKeyIV").withSchema(TableName.SecretVersion).as("secVerKeyIV"),
db.ref("secretKeyTag").withSchema(TableName.SecretVersion).as("secVerKeyTag"), db.ref("secretKeyTag").withSchema(TableName.SecretVersion).as("secVerKeyTag"),
@@ -63,67 +101,119 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
.withSchema(TableName.SecretVersion) .withSchema(TableName.SecretVersion)
.as("secVerCommentCiphertext") .as("secVerCommentCiphertext")
); );
return doc.map( const formatedDoc = sqlNestRelationships({
({ data: doc,
orgSecKeyIV, key: "id",
orgSecKeyTag, parentMapper: (data) =>
orgSecValueIV, SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data),
orgSecVersion, childrenMapper: [
orgSecValueTag, {
orgSecCommentIV, key: "tagJnId",
orgSecBlindIndex, label: "tags" as const,
orgSecCommentTag, mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color }) => ({
orgSecKeyCiphertext, id,
orgSecValueCiphertext, name,
orgSecCommentCiphertext, slug,
secVerCommentIV, color
secVerCommentCiphertext, })
secVerCommentTag, },
secVerValueCiphertext, {
secVerKeyIV, key: "secretId",
secVerKeyTag, label: "secret" as const,
secVerValueIV, mapper: ({
secVerVersion, orgSecKeyIV,
secVerValueTag, orgSecKeyTag,
secVerKeyCiphertext, orgSecValueIV,
...el orgSecVersion,
}) => ({ orgSecValueTag,
...el, orgSecCommentIV,
secret: el.secretId orgSecBlindIndex,
? { orgSecCommentTag,
id: el.secretId, orgSecKeyCiphertext,
version: orgSecVersion, orgSecValueCiphertext,
secretBlindIndex: orgSecBlindIndex, orgSecCommentCiphertext,
secretKeyIV: orgSecKeyIV, secretId
secretKeyTag: orgSecKeyTag, }) =>
secretKeyCiphertext: orgSecKeyCiphertext, secretId
secretValueIV: orgSecValueIV, ? {
secretValueTag: orgSecValueTag, id: secretId,
secretValueCiphertext: orgSecValueCiphertext, version: orgSecVersion,
secretCommentIV: orgSecCommentIV, secretBlindIndex: orgSecBlindIndex,
secretCommentTag: orgSecCommentTag, secretKeyIV: orgSecKeyIV,
secretCommentCiphertext: orgSecCommentCiphertext secretKeyTag: orgSecKeyTag,
secretKeyCiphertext: orgSecKeyCiphertext,
secretValueIV: orgSecValueIV,
secretValueTag: orgSecValueTag,
secretValueCiphertext: orgSecValueCiphertext,
secretCommentIV: orgSecCommentIV,
secretCommentTag: orgSecCommentTag,
secretCommentCiphertext: orgSecCommentCiphertext
}
: undefined
},
{
key: "secretVersion",
label: "secretVersion" as const,
mapper: ({
secVerCommentIV,
secVerCommentCiphertext,
secVerCommentTag,
secVerValueCiphertext,
secVerKeyIV,
secVerKeyTag,
secVerValueIV,
secretVersion,
secVerValueTag,
secVerKeyCiphertext,
secVerVersion
}) =>
secretVersion
? {
version: secVerVersion,
id: secretVersion,
secretKeyIV: secVerKeyIV,
secretKeyTag: secVerKeyTag,
secretKeyCiphertext: secVerKeyCiphertext,
secretValueIV: secVerValueIV,
secretValueTag: secVerValueTag,
secretValueCiphertext: secVerValueCiphertext,
secretCommentIV: secVerCommentIV,
secretCommentTag: secVerCommentTag,
secretCommentCiphertext: secVerCommentCiphertext
}
: undefined,
childrenMapper: [
{
key: "secVerTagId",
label: "tags" as const,
mapper: ({
secVerTagId: id,
secVerTagName: name,
secVerTagSlug: slug,
secVerTagColor: color
}) => ({
id,
name,
slug,
color
})
} }
: undefined, ]
secretVersion: el.secretVersion }
? { ]
id: el.secretVersion, });
secretKeyIV: secVerKeyIV, return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
secretKeyTag: secVerKeyTag, ...el,
secretKeyCiphertext: secVerKeyCiphertext, secret: secret?.[0],
secretValueIV: secVerValueIV, secretVersion: secretVersion?.[0]
secretValueTag: secVerValueTag, }));
secretValueCiphertext: secVerValueCiphertext,
secretCommentIV: secVerCommentIV,
secretCommentTag: secVerCommentTag,
secretCommentCiphertext: secVerCommentCiphertext
}
: undefined
})
);
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "FindByRequestId" }); throw new DatabaseError({ error, name: "FindByRequestId" });
} }
}; };
return { ...sarSecretOrm, findByRequestId }; return {
...secretApprovalRequestSecretOrm,
findByRequestId,
insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany
};
}; };
@@ -8,7 +8,7 @@ import {
TSecretApprovalRequestsSecretsInsert TSecretApprovalRequestsSecretsInsert
} from "@app/db/schemas"; } from "@app/db/schemas";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { groupBy, pick } from "@app/lib/fn"; import { groupBy, pick, unique } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TSecretBlindIndexDALFactory } from "@app/services/secret/secret-blind-index-dal"; import { TSecretBlindIndexDALFactory } from "@app/services/secret/secret-blind-index-dal";
@@ -16,6 +16,7 @@ import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
import { TSecretServiceFactory } from "@app/services/secret/secret-service"; import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal"; import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
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 { TPermissionServiceFactory } from "../permission/permission-service"; import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
@@ -39,12 +40,13 @@ import {
type TSecretApprovalRequestServiceFactoryDep = { type TSecretApprovalRequestServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory; secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
sarSecretDAL: TSecretApprovalRequestSecretDALFactory; secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
sarReviewerDAL: TSecretApprovalRequestReviewerDALFactory; secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
folderDAL: Pick< folderDAL: Pick<
TSecretFolderDALFactory, TSecretFolderDALFactory,
"findBySecretPath" | "findById" | "findSecretPathByFolderIds" "findBySecretPath" | "findById" | "findSecretPathByFolderIds"
>; >;
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById">;
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">; secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">; snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany">; secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany">;
@@ -66,8 +68,9 @@ export type TSecretApprovalRequestServiceFactory = ReturnType<
export const secretApprovalRequestServiceFactory = ({ export const secretApprovalRequestServiceFactory = ({
secretApprovalRequestDAL, secretApprovalRequestDAL,
folderDAL, folderDAL,
sarReviewerDAL, secretTagDAL,
sarSecretDAL, secretApprovalRequestReviewerDAL,
secretApprovalRequestSecretDAL,
secretBlindIndexDAL, secretBlindIndexDAL,
permissionService, permissionService,
snapshotService, snapshotService,
@@ -137,7 +140,7 @@ export const secretApprovalRequestServiceFactory = ({
throw new UnauthorizedError({ message: "User has no access" }); throw new UnauthorizedError({ message: "User has no access" });
} }
const secrets = await sarSecretDAL.findByRequestId(secretApprovalRequest.id); const secrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
const secretPath = await folderDAL.findSecretPathByFolderIds(secretApprovalRequest.projectId, [ const secretPath = await folderDAL.findSecretPathByFolderIds(secretApprovalRequest.projectId, [
secretApprovalRequest.folderId secretApprovalRequest.folderId
]); ]);
@@ -163,8 +166,8 @@ export const secretApprovalRequestServiceFactory = ({
) { ) {
throw new UnauthorizedError({ message: "User has no access" }); throw new UnauthorizedError({ message: "User has no access" });
} }
const reviewStatus = await sarReviewerDAL.transaction(async (tx) => { const reviewStatus = await secretApprovalRequestReviewerDAL.transaction(async (tx) => {
const review = await sarReviewerDAL.findOne( const review = await secretApprovalRequestReviewerDAL.findOne(
{ {
requestId: secretApprovalRequest.id, requestId: secretApprovalRequest.id,
member: membership.id member: membership.id
@@ -172,7 +175,7 @@ export const secretApprovalRequestServiceFactory = ({
tx tx
); );
if (!review) { if (!review) {
return sarReviewerDAL.create( return secretApprovalRequestReviewerDAL.create(
{ {
status, status,
requestId: secretApprovalRequest.id, requestId: secretApprovalRequest.id,
@@ -181,7 +184,7 @@ export const secretApprovalRequestServiceFactory = ({
tx tx
); );
} }
return sarReviewerDAL.updateById(review.id, { status }, tx); return secretApprovalRequestReviewerDAL.updateById(review.id, { status }, tx);
}); });
return reviewStatus; return reviewStatus;
}; };
@@ -255,7 +258,9 @@ export const secretApprovalRequestServiceFactory = ({
if (!hasMinApproval) if (!hasMinApproval)
throw new BadRequestError({ message: "Doesn't have minimum approvals needed" }); throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
const secretApprovalSecrets = await sarSecretDAL.findByRequestId(secretApprovalRequest.id); const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(
secretApprovalRequest.id
);
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" }); if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
const conflicts: Array<{ secretId: string; op: CommitType }> = []; const conflicts: Array<{ secretId: string; op: CommitType }> = [];
@@ -328,11 +333,12 @@ export const secretApprovalRequestServiceFactory = ({
"skipMultilineEncoding", "skipMultilineEncoding",
"secretReminderNote", "secretReminderNote",
"secretReminderRepeatDays", "secretReminderRepeatDays",
"version",
"algorithm", "algorithm",
"keyEncoding", "keyEncoding",
"secretBlindIndex" "secretBlindIndex"
]), ]),
tags: el?.tags.map(({ id }) => id),
version: 1,
type: SecretType.Shared type: SecretType.Shared
})) }))
}) })
@@ -344,25 +350,28 @@ export const secretApprovalRequestServiceFactory = ({
tx, tx,
inputSecrets: secretUpdationCommits.map((el) => ({ inputSecrets: secretUpdationCommits.map((el) => ({
filter: { filter: {
id: el.secretId, id: el.secretId as string, // this null check is already checked at top on conflict strategy
type: SecretType.Shared type: SecretType.Shared
}, },
data: pick(el, [ data: {
"secretCommentCiphertext", tags: el?.tags.map(({ id }) => id),
"secretCommentTag", ...pick(el, [
"secretCommentIV", "secretCommentCiphertext",
"secretValueIV", "secretCommentTag",
"secretValueTag", "secretCommentIV",
"secretValueCiphertext", "secretValueIV",
"secretKeyCiphertext", "secretValueTag",
"secretKeyTag", "secretValueCiphertext",
"secretKeyIV", "secretKeyCiphertext",
"metadata", "secretKeyTag",
"skipMultilineEncoding", "secretKeyIV",
"secretReminderNote", "metadata",
"secretReminderRepeatDays", "skipMultilineEncoding",
"secretBlindIndex" "secretReminderNote",
]) "secretReminderRepeatDays",
"secretBlindIndex"
])
}
})) }))
}) })
: []; : [];
@@ -438,6 +447,7 @@ export const secretApprovalRequestServiceFactory = ({
throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = []; const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
const commitTagIds: Record<string, string[]> = {};
// for created secret approval change // for created secret approval change
const createdSecrets = data[CommitType.Create]; const createdSecrets = data[CommitType.Create];
if (createdSecrets && createdSecrets?.length) { if (createdSecrets && createdSecrets?.length) {
@@ -452,12 +462,15 @@ export const secretApprovalRequestServiceFactory = ({
...createdSecrets.map(({ secretName, ...el }) => ({ ...createdSecrets.map(({ secretName, ...el }) => ({
...el, ...el,
op: CommitType.Create as const, op: CommitType.Create as const,
version: 0, version: 1,
secretBlindIndex: keyName2BlindIndex[secretName], secretBlindIndex: keyName2BlindIndex[secretName],
algorithm: SecretEncryptionAlgo.AES_256_GCM, algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.BASE64 keyEncoding: SecretKeyEncoding.BASE64
})) }))
); );
createdSecrets.forEach(({ tagIds, secretName }) => {
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
});
} }
// not secret approval for update operations // not secret approval for update operations
const updatedSecrets = data[CommitType.Update]; const updatedSecrets = data[CommitType.Update];
@@ -495,18 +508,21 @@ export const secretApprovalRequestServiceFactory = ({
updatedSecretIds updatedSecretIds
); );
commits.push( commits.push(
...updatedSecrets.map(({ newSecretName, secretName, ...el }) => { ...updatedSecrets.map(({ newSecretName, secretName, tagIds, ...el }) => {
const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id; const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id;
const secretBlindIndex =
newSecretName && newKeyName2BlindIndex[newSecretName]
? newKeyName2BlindIndex?.[secretName]
: keyName2BlindIndex[secretName];
// add tags
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
return { return {
...latestSecretVersions[secretId], ...latestSecretVersions[secretId],
...el, ...el,
op: CommitType.Update as const, op: CommitType.Update as const,
secret: secretId, secret: secretId,
secretVersion: latestSecretVersions[secretId].id, secretVersion: latestSecretVersions[secretId].id,
secretBlindIndex: secretBlindIndex,
newSecretName && newKeyName2BlindIndex[newSecretName]
? newKeyName2BlindIndex?.[secretName]
: keyName2BlindIndex[secretName],
version: secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].version || 1 version: secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].version || 1
}; };
}) })
@@ -546,6 +562,11 @@ export const secretApprovalRequestServiceFactory = ({
} }
if (!commits.length) throw new BadRequestError({ message: "Empty commits" }); if (!commits.length) throw new BadRequestError({ message: "Empty commits" });
const tagIds = unique(Object.values(commitTagIds).flat());
const tags = tagIds.length ? await secretTagDAL.findManyTagsById(projectId, tagIds) : [];
if (tagIds.length !== tags.length) throw new BadRequestError({ message: "Tag not found" });
const secretApprovalRequest = await secretApprovalRequestDAL.transaction(async (tx) => { const secretApprovalRequest = await secretApprovalRequestDAL.transaction(async (tx) => {
const doc = await secretApprovalRequestDAL.create( const doc = await secretApprovalRequestDAL.create(
{ {
@@ -558,7 +579,7 @@ export const secretApprovalRequestServiceFactory = ({
}, },
tx tx
); );
const approvalCommits = await sarSecretDAL.insertMany( const approvalCommits = await secretApprovalRequestSecretDAL.insertMany(
commits.map( commits.map(
({ ({
version, version,
@@ -607,6 +628,20 @@ export const secretApprovalRequestServiceFactory = ({
), ),
tx tx
); );
const commitsGroupByBlindIndex = groupBy(approvalCommits, (i) => i.secretBlindIndex);
if (tagIds.length) {
await secretApprovalRequestSecretDAL.insertApprovalSecretTags(
Object.keys(commitTagIds).flatMap((blindIndex) =>
commitTagIds[blindIndex]
? commitTagIds[blindIndex].map((tagId) => ({
secretId: commitsGroupByBlindIndex[blindIndex][0].id,
tagId
}))
: []
),
tx
);
}
return { ...doc, commits: approvalCommits }; return { ...doc, commits: approvalCommits };
}); });
return secretApprovalRequest; return secretApprovalRequest;
+22
View File
@@ -16,3 +16,25 @@ export const groupBy = <T, Key extends string | number | symbol>(
}, },
{} as Record<Key, T[]> {} as Record<Key, T[]>
); );
/**
* Given a list of items returns a new list with only
* unique items. Accepts an optional identity function
* to convert each item in the list to a comparable identity
* value
*/
export const unique = <T, K extends string | number | symbol>(
array: readonly T[],
toKey?: (item: T) => K
): T[] => {
const valueMap = array.reduce(
(acc, item) => {
const key = toKey ? toKey(item) : (item as any as string | number | symbol);
if (acc[key]) return acc;
acc[key] = item;
return acc;
},
{} as Record<string | number | symbol, T>
);
return Object.values(valueMap);
};
+3 -2
View File
@@ -72,6 +72,7 @@ const sqlChildMapper = <
childrenMapper: C childrenMapper: C
) => { ) => {
if (!docsByPk) return; if (!docsByPk) return;
type Cm = MappedRecord<(typeof childrenMapper)[number]>; type Cm = MappedRecord<(typeof childrenMapper)[number]>;
childrenMapper.forEach(({ label, mapper, key: childPk, childrenMapper: nestedMappers }) => { childrenMapper.forEach(({ label, mapper, key: childPk, childrenMapper: nestedMappers }) => {
// eslint-disable-next-line // eslint-disable-next-line
@@ -79,12 +80,12 @@ const sqlChildMapper = <
if (doc?.[childPk] !== null && typeof doc?.[childPk] !== "undefined") { if (doc?.[childPk] !== null && typeof doc?.[childPk] !== "undefined") {
const ck = `${prefix}-${label}-${doc[childPk]}`; const ck = `${prefix}-${label}-${doc[childPk]}`;
const val = mapper(doc);
if (!lookupTable.has(ck)) { if (!lookupTable.has(ck)) {
const val = mapper(doc);
if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val); if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val);
lookupTable.add(ck); lookupTable.add(ck);
} }
if (nestedMappers) { if (nestedMappers && val) {
sqlChildMapper( sqlChildMapper(
doc, doc,
docsByPk[pk][label as keyof P], docsByPk[pk][label as keyof P],
+3 -2
View File
@@ -368,8 +368,9 @@ export const registerRoutes = async (
const sarService = secretApprovalRequestServiceFactory({ const sarService = secretApprovalRequestServiceFactory({
permissionService, permissionService,
folderDAL, folderDAL,
sarSecretDAL, secretTagDAL,
sarReviewerDAL, secretApprovalRequestSecretDAL: sarSecretDAL,
secretApprovalRequestReviewerDAL: sarReviewerDAL,
secretVersionDAL, secretVersionDAL,
secretBlindIndexDAL, secretBlindIndexDAL,
secretApprovalRequestDAL, secretApprovalRequestDAL,
@@ -726,7 +726,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
skipMultilineEncoding, skipMultilineEncoding,
secretKeyTag, secretKeyTag,
secretKeyCiphertext, secretKeyCiphertext,
secretKeyIV secretKeyIV,
tagIds: tags
} }
] ]
} }
@@ -99,7 +99,6 @@ export const secretDALFactory = (db: TDbClient) => {
.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("name").withSchema(TableName.SecretTag).as("tagName")); .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName"));
console.log(JSON.stringify(secs, null, 4));
const data = sqlNestRelationships({ const data = sqlNestRelationships({
data: secs, data: secs,
key: "id", key: "id",
@@ -117,7 +116,6 @@ export const secretDALFactory = (db: TDbClient) => {
} }
] ]
}); });
console.log(JSON.stringify(data, null, 4));
return data; return data;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "get all secret" }); throw new DatabaseError({ error, name: "get all secret" });
@@ -164,19 +164,17 @@ export const secretServiceFactory = ({
tags !== undefined ? { tags, secretId: newSecrets[i].id } : [] tags !== undefined ? { tags, secretId: newSecrets[i].id } : []
); );
if (secsUpdatedTag.length) { if (secsUpdatedTag.length) {
const delTags = await secretTagDAL.deleteTagsManySecret( await secretTagDAL.deleteTagsManySecret(
projectId, projectId,
secsUpdatedTag.map(({ secretId }) => secretId), secsUpdatedTag.map(({ secretId }) => secretId),
tx tx
); );
console.log(delTags);
const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) => const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) =>
secretTags.map((tag) => ({ secretTags.map((tag) => ({
[`${TableName.SecretTag}Id` as const]: tag, [`${TableName.SecretTag}Id` as const]: tag,
[`${TableName.Secret}Id` as const]: secretId [`${TableName.Secret}Id` as const]: secretId
})) }))
); );
console.log(newSecretTags);
if (newSecretTags.length) { if (newSecretTags.length) {
const secTags = await secretTagDAL.saveTagsToSecret(newSecretTags, tx); const secTags = await secretTagDAL.saveTagsToSecret(newSecretTags, tx);
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId); const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);