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

This commit is contained in:
Akhil Mohan
2024-01-20 20:48:56 +05:30
parent 42eb01e1e2
commit 80b6c4ad94
10 changed files with 371 additions and 206 deletions

View File

@@ -83,23 +83,23 @@ import {
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
TSapApprovers,
TSapApproversInsert,
TSapApproversUpdate,
TSaRequestSecrets,
TSaRequestSecretsInsert,
TSaRequestSecretsUpdate,
TSaRequestSecretTags,
TSaRequestSecretTagsInsert,
TSaRequestSecretTagsUpdate,
TSarReviewers,
TSarReviewersInsert,
TSarReviewersUpdate,
TSecretApprovalPolicies,
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate,
TSecretApprovalPoliciesInsert,
TSecretApprovalPoliciesUpdate,
TSecretApprovalRequests,
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate,
TSecretApprovalRequestsInsert,
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
TSecretApprovalRequestsReviewersUpdate,
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate,
TSecretApprovalRequestsUpdate,
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
@@ -166,7 +166,8 @@ import {
TUsersUpdate,
TWebhooks,
TWebhooksInsert,
TWebhooksUpdate} from "@app/db/schemas";
TWebhooksUpdate
} from "@app/db/schemas";
declare module "knex/types/tables" {
interface Tables {
@@ -327,9 +328,9 @@ declare module "knex/types/tables" {
TSecretApprovalPoliciesUpdate
>;
[TableName.SecretApprovalPolicyApprover]: Knex.CompositeTableType<
TSapApprovers,
TSapApproversInsert,
TSapApproversUpdate
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate
>;
[TableName.SecretApprovalRequest]: Knex.CompositeTableType<
TSecretApprovalRequests,
@@ -337,19 +338,19 @@ declare module "knex/types/tables" {
TSecretApprovalRequestsUpdate
>;
[TableName.SecretApprovalRequestReviewer]: Knex.CompositeTableType<
TSarReviewers,
TSarReviewersInsert,
TSarReviewersUpdate
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
TSecretApprovalRequestsReviewersUpdate
>;
[TableName.SecretApprovalRequestSecret]: Knex.CompositeTableType<
TSaRequestSecrets,
TSaRequestSecretsInsert,
TSaRequestSecretsUpdate
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate
>;
[TableName.SecretApprovalRequestSecretTag]: Knex.CompositeTableType<
TSaRequestSecretTags,
TSaRequestSecretTagsInsert,
TSaRequestSecretTagsUpdate
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate
>;
[TableName.SecretRotation]: Knex.CompositeTableType<
TSecretRotations,

View File

@@ -5,6 +5,7 @@ import {
SecretApprovalRequestsSchema,
SecretApprovalRequestsSecretsSchema,
SecretsSchema,
SecretTagsSchema,
SecretVersionsSchema
} from "@app/db/schemas";
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({
url: "/:id/merge",
method: "POST",
@@ -257,4 +187,92 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
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 };
}
});
};

View File

@@ -1,19 +1,35 @@
import { Knex } from "knex";
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 { 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) => {
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) => {
try {
const doc = await (tx || db)(TableName.SecretApprovalRequestSecret)
const doc = await (tx || db)({
secVerTag: TableName.SecretTag
})
.from(TableName.SecretApprovalRequestSecret)
.where({ requestId })
.leftJoin(
TableName.SecretApprovalRequestSecretTag,
`${TableName.SecretApprovalRequestSecret}.id`,
`${TableName.SecretApprovalRequestSecretTag}.secretId`
)
.leftJoin(
TableName.SecretTag,
`${TableName.SecretApprovalRequestSecretTag}.tagId`,
`${TableName.SecretTag}.id`
)
.leftJoin(
TableName.Secret,
`${TableName.SecretApprovalRequestSecret}.secretId`,
@@ -24,7 +40,30 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
`${TableName.SecretVersion}.id`,
`${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({
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(
db.ref("secretBlindIndex").withSchema(TableName.Secret).as("orgSecBlindIndex"),
db.ref("version").withSchema(TableName.Secret).as("orgSecVersion"),
@@ -42,7 +81,6 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
.as("orgSecCommentCiphertext")
)
.select(
// db.ref("secretBlindIndex").withSchema(TableName.Secret).as("orgSecBlindInex"),
db.ref("version").withSchema(TableName.SecretVersion).as("secVerVersion"),
db.ref("secretKeyIV").withSchema(TableName.SecretVersion).as("secVerKeyIV"),
db.ref("secretKeyTag").withSchema(TableName.SecretVersion).as("secVerKeyTag"),
@@ -63,67 +101,119 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
.withSchema(TableName.SecretVersion)
.as("secVerCommentCiphertext")
);
return doc.map(
({
orgSecKeyIV,
orgSecKeyTag,
orgSecValueIV,
orgSecVersion,
orgSecValueTag,
orgSecCommentIV,
orgSecBlindIndex,
orgSecCommentTag,
orgSecKeyCiphertext,
orgSecValueCiphertext,
orgSecCommentCiphertext,
secVerCommentIV,
secVerCommentCiphertext,
secVerCommentTag,
secVerValueCiphertext,
secVerKeyIV,
secVerKeyTag,
secVerValueIV,
secVerVersion,
secVerValueTag,
secVerKeyCiphertext,
...el
}) => ({
...el,
secret: el.secretId
? {
id: el.secretId,
version: orgSecVersion,
secretBlindIndex: orgSecBlindIndex,
secretKeyIV: orgSecKeyIV,
secretKeyTag: orgSecKeyTag,
secretKeyCiphertext: orgSecKeyCiphertext,
secretValueIV: orgSecValueIV,
secretValueTag: orgSecValueTag,
secretValueCiphertext: orgSecValueCiphertext,
secretCommentIV: orgSecCommentIV,
secretCommentTag: orgSecCommentTag,
secretCommentCiphertext: orgSecCommentCiphertext
const formatedDoc = sqlNestRelationships({
data: doc,
key: "id",
parentMapper: (data) =>
SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data),
childrenMapper: [
{
key: "tagJnId",
label: "tags" as const,
mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color }) => ({
id,
name,
slug,
color
})
},
{
key: "secretId",
label: "secret" as const,
mapper: ({
orgSecKeyIV,
orgSecKeyTag,
orgSecValueIV,
orgSecVersion,
orgSecValueTag,
orgSecCommentIV,
orgSecBlindIndex,
orgSecCommentTag,
orgSecKeyCiphertext,
orgSecValueCiphertext,
orgSecCommentCiphertext,
secretId
}) =>
secretId
? {
id: secretId,
version: orgSecVersion,
secretBlindIndex: orgSecBlindIndex,
secretKeyIV: orgSecKeyIV,
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,
secretKeyTag: secVerKeyTag,
secretKeyCiphertext: secVerKeyCiphertext,
secretValueIV: secVerValueIV,
secretValueTag: secVerValueTag,
secretValueCiphertext: secVerValueCiphertext,
secretCommentIV: secVerCommentIV,
secretCommentTag: secVerCommentTag,
secretCommentCiphertext: secVerCommentCiphertext
}
: undefined
})
);
]
}
]
});
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
...el,
secret: secret?.[0],
secretVersion: secretVersion?.[0]
}));
} catch (error) {
throw new DatabaseError({ error, name: "FindByRequestId" });
}
};
return { ...sarSecretOrm, findByRequestId };
return {
...secretApprovalRequestSecretOrm,
findByRequestId,
insertApprovalSecretTags: secretApprovalRequestSecretTagOrm.insertMany
};
};

View File

@@ -8,7 +8,7 @@ import {
TSecretApprovalRequestsSecretsInsert
} from "@app/db/schemas";
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 { ActorType } from "@app/services/auth/auth-type";
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 { TSecretVersionDALFactory } from "@app/services/secret/secret-version-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 { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
@@ -39,12 +40,13 @@ import {
type TSecretApprovalRequestServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
sarSecretDAL: TSecretApprovalRequestSecretDALFactory;
sarReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
folderDAL: Pick<
TSecretFolderDALFactory,
"findBySecretPath" | "findById" | "findSecretPathByFolderIds"
>;
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById">;
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany">;
@@ -66,8 +68,9 @@ export type TSecretApprovalRequestServiceFactory = ReturnType<
export const secretApprovalRequestServiceFactory = ({
secretApprovalRequestDAL,
folderDAL,
sarReviewerDAL,
sarSecretDAL,
secretTagDAL,
secretApprovalRequestReviewerDAL,
secretApprovalRequestSecretDAL,
secretBlindIndexDAL,
permissionService,
snapshotService,
@@ -137,7 +140,7 @@ export const secretApprovalRequestServiceFactory = ({
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, [
secretApprovalRequest.folderId
]);
@@ -163,8 +166,8 @@ export const secretApprovalRequestServiceFactory = ({
) {
throw new UnauthorizedError({ message: "User has no access" });
}
const reviewStatus = await sarReviewerDAL.transaction(async (tx) => {
const review = await sarReviewerDAL.findOne(
const reviewStatus = await secretApprovalRequestReviewerDAL.transaction(async (tx) => {
const review = await secretApprovalRequestReviewerDAL.findOne(
{
requestId: secretApprovalRequest.id,
member: membership.id
@@ -172,7 +175,7 @@ export const secretApprovalRequestServiceFactory = ({
tx
);
if (!review) {
return sarReviewerDAL.create(
return secretApprovalRequestReviewerDAL.create(
{
status,
requestId: secretApprovalRequest.id,
@@ -181,7 +184,7 @@ export const secretApprovalRequestServiceFactory = ({
tx
);
}
return sarReviewerDAL.updateById(review.id, { status }, tx);
return secretApprovalRequestReviewerDAL.updateById(review.id, { status }, tx);
});
return reviewStatus;
};
@@ -255,7 +258,9 @@ export const secretApprovalRequestServiceFactory = ({
if (!hasMinApproval)
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" });
const conflicts: Array<{ secretId: string; op: CommitType }> = [];
@@ -328,11 +333,12 @@ export const secretApprovalRequestServiceFactory = ({
"skipMultilineEncoding",
"secretReminderNote",
"secretReminderRepeatDays",
"version",
"algorithm",
"keyEncoding",
"secretBlindIndex"
]),
tags: el?.tags.map(({ id }) => id),
version: 1,
type: SecretType.Shared
}))
})
@@ -344,25 +350,28 @@ export const secretApprovalRequestServiceFactory = ({
tx,
inputSecrets: secretUpdationCommits.map((el) => ({
filter: {
id: el.secretId,
id: el.secretId as string, // this null check is already checked at top on conflict strategy
type: SecretType.Shared
},
data: pick(el, [
"secretCommentCiphertext",
"secretCommentTag",
"secretCommentIV",
"secretValueIV",
"secretValueTag",
"secretValueCiphertext",
"secretKeyCiphertext",
"secretKeyTag",
"secretKeyIV",
"metadata",
"skipMultilineEncoding",
"secretReminderNote",
"secretReminderRepeatDays",
"secretBlindIndex"
])
data: {
tags: el?.tags.map(({ id }) => id),
...pick(el, [
"secretCommentCiphertext",
"secretCommentTag",
"secretCommentIV",
"secretValueIV",
"secretValueTag",
"secretValueCiphertext",
"secretKeyCiphertext",
"secretKeyTag",
"secretKeyIV",
"metadata",
"skipMultilineEncoding",
"secretReminderNote",
"secretReminderRepeatDays",
"secretBlindIndex"
])
}
}))
})
: [];
@@ -438,6 +447,7 @@ export const secretApprovalRequestServiceFactory = ({
throw new BadRequestError({ message: "Blind index not found", name: "Update secret" });
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
const commitTagIds: Record<string, string[]> = {};
// for created secret approval change
const createdSecrets = data[CommitType.Create];
if (createdSecrets && createdSecrets?.length) {
@@ -452,12 +462,15 @@ export const secretApprovalRequestServiceFactory = ({
...createdSecrets.map(({ secretName, ...el }) => ({
...el,
op: CommitType.Create as const,
version: 0,
version: 1,
secretBlindIndex: keyName2BlindIndex[secretName],
algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.BASE64
}))
);
createdSecrets.forEach(({ tagIds, secretName }) => {
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
});
}
// not secret approval for update operations
const updatedSecrets = data[CommitType.Update];
@@ -495,18 +508,21 @@ export const secretApprovalRequestServiceFactory = ({
updatedSecretIds
);
commits.push(
...updatedSecrets.map(({ newSecretName, secretName, ...el }) => {
...updatedSecrets.map(({ newSecretName, secretName, tagIds, ...el }) => {
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 {
...latestSecretVersions[secretId],
...el,
op: CommitType.Update as const,
secret: secretId,
secretVersion: latestSecretVersions[secretId].id,
secretBlindIndex:
newSecretName && newKeyName2BlindIndex[newSecretName]
? newKeyName2BlindIndex?.[secretName]
: keyName2BlindIndex[secretName],
secretBlindIndex,
version: secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].version || 1
};
})
@@ -546,6 +562,11 @@ export const secretApprovalRequestServiceFactory = ({
}
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 doc = await secretApprovalRequestDAL.create(
{
@@ -558,7 +579,7 @@ export const secretApprovalRequestServiceFactory = ({
},
tx
);
const approvalCommits = await sarSecretDAL.insertMany(
const approvalCommits = await secretApprovalRequestSecretDAL.insertMany(
commits.map(
({
version,
@@ -607,6 +628,20 @@ export const secretApprovalRequestServiceFactory = ({
),
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 secretApprovalRequest;

View File

@@ -16,3 +16,25 @@ export const groupBy = <T, Key extends string | number | symbol>(
},
{} 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);
};

View File

@@ -72,6 +72,7 @@ const sqlChildMapper = <
childrenMapper: C
) => {
if (!docsByPk) return;
type Cm = MappedRecord<(typeof childrenMapper)[number]>;
childrenMapper.forEach(({ label, mapper, key: childPk, childrenMapper: nestedMappers }) => {
// eslint-disable-next-line
@@ -79,12 +80,12 @@ const sqlChildMapper = <
if (doc?.[childPk] !== null && typeof doc?.[childPk] !== "undefined") {
const ck = `${prefix}-${label}-${doc[childPk]}`;
const val = mapper(doc);
if (!lookupTable.has(ck)) {
const val = mapper(doc);
if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val);
lookupTable.add(ck);
}
if (nestedMappers) {
if (nestedMappers && val) {
sqlChildMapper(
doc,
docsByPk[pk][label as keyof P],

View File

@@ -368,8 +368,9 @@ export const registerRoutes = async (
const sarService = secretApprovalRequestServiceFactory({
permissionService,
folderDAL,
sarSecretDAL,
sarReviewerDAL,
secretTagDAL,
secretApprovalRequestSecretDAL: sarSecretDAL,
secretApprovalRequestReviewerDAL: sarReviewerDAL,
secretVersionDAL,
secretBlindIndexDAL,
secretApprovalRequestDAL,

View File

@@ -726,7 +726,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
skipMultilineEncoding,
secretKeyTag,
secretKeyCiphertext,
secretKeyIV
secretKeyIV,
tagIds: tags
}
]
}

View File

@@ -99,7 +99,6 @@ export const secretDALFactory = (db: TDbClient) => {
.select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor"))
.select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug"))
.select(db.ref("name").withSchema(TableName.SecretTag).as("tagName"));
console.log(JSON.stringify(secs, null, 4));
const data = sqlNestRelationships({
data: secs,
key: "id",
@@ -117,7 +116,6 @@ export const secretDALFactory = (db: TDbClient) => {
}
]
});
console.log(JSON.stringify(data, null, 4));
return data;
} catch (error) {
throw new DatabaseError({ error, name: "get all secret" });

View File

@@ -164,19 +164,17 @@ export const secretServiceFactory = ({
tags !== undefined ? { tags, secretId: newSecrets[i].id } : []
);
if (secsUpdatedTag.length) {
const delTags = await secretTagDAL.deleteTagsManySecret(
await secretTagDAL.deleteTagsManySecret(
projectId,
secsUpdatedTag.map(({ secretId }) => secretId),
tx
);
console.log(delTags);
const newSecretTags = secsUpdatedTag.flatMap(({ tags: secretTags = [], secretId }) =>
secretTags.map((tag) => ({
[`${TableName.SecretTag}Id` as const]: tag,
[`${TableName.Secret}Id` as const]: secretId
}))
);
console.log(newSecretTags);
if (newSecretTags.length) {
const secTags = await secretTagDAL.saveTagsToSecret(newSecretTags, tx);
const secVersionsGroupBySecId = groupBy(secretVersions, (i) => i.secretId);