mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(infisical-pg): completed tag support to all secret operations
This commit is contained in:
51
backend-pg/src/@types/knex.d.ts
vendored
51
backend-pg/src/@types/knex.d.ts
vendored
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -368,8 +368,9 @@ export const registerRoutes = async (
|
||||
const sarService = secretApprovalRequestServiceFactory({
|
||||
permissionService,
|
||||
folderDAL,
|
||||
sarSecretDAL,
|
||||
sarReviewerDAL,
|
||||
secretTagDAL,
|
||||
secretApprovalRequestSecretDAL: sarSecretDAL,
|
||||
secretApprovalRequestReviewerDAL: sarReviewerDAL,
|
||||
secretVersionDAL,
|
||||
secretBlindIndexDAL,
|
||||
secretApprovalRequestDAL,
|
||||
|
||||
@@ -726,7 +726,8 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
||||
skipMultilineEncoding,
|
||||
secretKeyTag,
|
||||
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("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" });
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user