mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(infisical-pg): fixed missing secret path and commiter message in frontend
This commit is contained in:
@@ -40,6 +40,10 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
approvers: z.string().array(),
|
||||
secretPath: z.string().optional().nullable()
|
||||
}),
|
||||
commits: z
|
||||
.object({ op: z.string(), secretId: z.string().nullable().optional() })
|
||||
.array(),
|
||||
environment: z.string(),
|
||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||
approvers: z.string().array()
|
||||
})
|
||||
@@ -105,8 +109,10 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
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: SaRequestSecretsSchema.omit({ secretBlindIndex: true })
|
||||
.merge(
|
||||
z.object({
|
||||
|
||||
@@ -53,16 +53,17 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
`${TableName.SarReviewer}.requestId`
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.select(tx.ref("member").withSchema(TableName.SarReviewer).as("reviewerMemberId"))
|
||||
.select(tx.ref("status").withSchema(TableName.SarReviewer).as("reviewerStatus"))
|
||||
.select(tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"))
|
||||
.select(tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"))
|
||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
||||
.select(
|
||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath")
|
||||
)
|
||||
.select(tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"))
|
||||
.select(tx.ref("approverId").withSchema(TableName.SapApprover));
|
||||
tx.ref("member").withSchema(TableName.SarReviewer).as("reviewerMemberId"),
|
||||
tx.ref("status").withSchema(TableName.SarReviewer).as("reviewerStatus"),
|
||||
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||
tx.ref("projectId").withSchema(TableName.Environment),
|
||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||
tx.ref("approverId").withSchema(TableName.SapApprover)
|
||||
);
|
||||
|
||||
const findById = async (id: string, tx?: Knex) => {
|
||||
try {
|
||||
@@ -74,6 +75,7 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
parentMapper: (el) => ({
|
||||
...SecretApprovalRequestsSchema.parse(el),
|
||||
projectId: el.projectId,
|
||||
environment: el.environment,
|
||||
policy: {
|
||||
id: el.policyId,
|
||||
name: el.policyName,
|
||||
@@ -193,6 +195,11 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
`${TableName.SecretApprovalRequest}.id`,
|
||||
`${TableName.SarReviewer}.requestId`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.SarSecret,
|
||||
`${TableName.SarSecret}.requestId`,
|
||||
`${TableName.SecretApprovalRequest}.id`
|
||||
)
|
||||
.where(
|
||||
stripUndefinedInWhere({
|
||||
projectId,
|
||||
@@ -207,23 +214,23 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.select(db.ref("projectId").withSchema(TableName.Environment))
|
||||
.select(db.ref("id").withSchema(TableName.SarReviewer).as("reviewerMemberId"))
|
||||
.select(db.ref("status").withSchema(TableName.SarReviewer).as("reviewerStatus"))
|
||||
.select(db.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"))
|
||||
.select(db.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"))
|
||||
.select(
|
||||
db.ref("projectId").withSchema(TableName.Environment),
|
||||
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||
db.ref("id").withSchema(TableName.SarReviewer).as("reviewerMemberId"),
|
||||
db.ref("status").withSchema(TableName.SarReviewer).as("reviewerStatus"),
|
||||
db.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||
db.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||
db.ref("op").withSchema(TableName.SarSecret).as("commitOp"),
|
||||
db.ref("secretId").withSchema(TableName.SarSecret).as("commitSecretId"),
|
||||
db.ref("id").withSchema(TableName.SarSecret).as("commitId"),
|
||||
db.raw(
|
||||
`DENSE_RANK() OVER (partition by ${TableName.Environment}."projectId" ORDER BY ${TableName.SecretApprovalRequest}."id" DESC) as rank`
|
||||
)
|
||||
),
|
||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||
db.ref("approverId").withSchema(TableName.SapApprover)
|
||||
)
|
||||
.select(
|
||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath")
|
||||
)
|
||||
.select(
|
||||
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals")
|
||||
)
|
||||
.select(db.ref("approverId").withSchema(TableName.SapApprover))
|
||||
.orderBy("createdAt", "desc");
|
||||
|
||||
const docs = await (tx || db)
|
||||
@@ -232,12 +239,12 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
.from<Awaited<typeof query>[number]>("w")
|
||||
.where("w.rank", ">=", offset)
|
||||
.andWhere("w.rank", "<", offset + limit);
|
||||
|
||||
const formatedDoc = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (el) => ({
|
||||
...SecretApprovalRequestsSchema.parse(el),
|
||||
environment: el.environment,
|
||||
projectId: el.projectId,
|
||||
policy: {
|
||||
id: el.policyId,
|
||||
@@ -253,7 +260,20 @@ export const secretApprovalRequestDalFactory = (db: TDbClient) => {
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) =>
|
||||
member ? { member, status: s } : undefined
|
||||
},
|
||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
||||
{
|
||||
key: "approverId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverId }) => approverId
|
||||
},
|
||||
{
|
||||
key: "commitId",
|
||||
label: "commits" as const,
|
||||
mapper: ({ commitSecretId: secretId, commitId: id, commitOp: op }) => ({
|
||||
op,
|
||||
id,
|
||||
secretId
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
return formatedDoc.map((el) => ({
|
||||
|
||||
@@ -41,7 +41,10 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
secretApprovalRequestDal: TSecretApprovalRequestDalFactory;
|
||||
sarSecretDal: TSarSecretDalFactory;
|
||||
sarReviewerDal: TSarReviewerDalFactory;
|
||||
folderDal: Pick<TSecretFolderDalFactory, "findBySecretPath" | "findById">;
|
||||
folderDal: Pick<
|
||||
TSecretFolderDalFactory,
|
||||
"findBySecretPath" | "findById" | "findSecretPathByFolderIds"
|
||||
>;
|
||||
secretBlindIndexDal: Pick<TSecretBlindIndexDalFactory, "findOne">;
|
||||
snapshotService: Pick<TSecretSnapshotServiceFactory, "performSnapshot">;
|
||||
secretVersionDal: Pick<TSecretVersionDalFactory, "findLatestVersionMany">;
|
||||
@@ -135,7 +138,10 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}
|
||||
|
||||
const secrets = await sarSecretDal.findByRequestId(secretApprovalRequest.id);
|
||||
return { ...secretApprovalRequest, commits: secrets };
|
||||
const secretPath = await folderDal.findSecretPathByFolderIds(secretApprovalRequest.projectId, [
|
||||
secretApprovalRequest.folderId
|
||||
]);
|
||||
return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets };
|
||||
};
|
||||
|
||||
const reviewApproval = async ({ approvalId, actor, status, actorId }: TReviewRequestDTO) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
TSecretFoldersUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
export const validateFolderName = (folderName: string) => {
|
||||
@@ -176,6 +177,54 @@ const sqlFindFolderByPathQuery = (
|
||||
);
|
||||
};
|
||||
|
||||
const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) =>
|
||||
db
|
||||
.withRecursive("parent", (baseQb) => {
|
||||
// first remember our folders are connected as a link list or known as adjacency list
|
||||
// Thus each node has connection to parent node
|
||||
// we first find the folder given in folder id
|
||||
baseQb
|
||||
.from(TableName.SecretFolder)
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select({
|
||||
// this is for root condition
|
||||
// if the given folder id is root folder id then intial path is set as / instead of /root
|
||||
// if not root folder the path here will be /<folder name>
|
||||
path: db.raw(
|
||||
`CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))`
|
||||
),
|
||||
child: db.raw("NULL::uuid")
|
||||
})
|
||||
.join(
|
||||
TableName.Environment,
|
||||
`${TableName.SecretFolder}.envId`,
|
||||
`${TableName.Environment}.id`
|
||||
)
|
||||
.where({ projectId })
|
||||
.whereIn(`${TableName.SecretFolder}.id`, folderIds)
|
||||
.union((qb) =>
|
||||
// then we keep going up
|
||||
// until parent id is null
|
||||
qb
|
||||
.select(selectAllTableCols(TableName.SecretFolder))
|
||||
.select({
|
||||
// then we join join this folder name behind previous as we are going from child to parent
|
||||
// the root folder check is used to avoid last / and also root name in folders
|
||||
path: db.raw(
|
||||
`CONCAT( CASE
|
||||
WHEN ${TableName.SecretFolder}."parentId" is NULL THEN ''
|
||||
ELSE CONCAT('/', secret_folders.name)
|
||||
END, parent.path )`
|
||||
),
|
||||
child: db.raw("COALESCE(parent.child, parent.id)")
|
||||
})
|
||||
.from(TableName.SecretFolder)
|
||||
.join("parent", "parent.parentId", `${TableName.SecretFolder}.id`)
|
||||
);
|
||||
})
|
||||
.select("*")
|
||||
.from<TSecretFolders & { child: string | null; path: string }>("parent");
|
||||
|
||||
export type TSecretFolderDalFactory = ReturnType<typeof secretFolderDalFactory>;
|
||||
// never change this. If u do write a migration for it
|
||||
export const ROOT_FOLDER_NAME = "root";
|
||||
@@ -220,6 +269,21 @@ export const secretFolderDalFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
// this is used to do an inverse query in folders
|
||||
// that is instances in which for a given folderid find the secret path
|
||||
const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => {
|
||||
try {
|
||||
const folders = await sqlFindSecretPathByFolderId(tx || db, projectId, folderIds);
|
||||
const rootFolders = groupBy(
|
||||
folders.filter(({ parentId }) => parentId === null),
|
||||
(i) => i.child || i.id // root condition then child and parent will null
|
||||
);
|
||||
return folderIds.map((folderId) => rootFolders[folderId]?.[0]);
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find by secret path" });
|
||||
}
|
||||
};
|
||||
|
||||
const update = async (filter: Partial<TSecretFolders>, data: TSecretFoldersUpdate, tx?: Knex) => {
|
||||
try {
|
||||
const folder = await (tx || db)(TableName.SecretFolder)
|
||||
@@ -259,5 +323,12 @@ export const secretFolderDalFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
return { ...secretFolderOrm, update, findBySecretPath, findById, findByManySecretPath };
|
||||
return {
|
||||
...secretFolderOrm,
|
||||
update,
|
||||
findBySecretPath,
|
||||
findById,
|
||||
findByManySecretPath,
|
||||
findSecretPathByFolderIds
|
||||
};
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ export const serviceTokenServiceFactory = ({
|
||||
scopes.forEach(({ environment, secretPath }) => {
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath: secretPath })
|
||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||
);
|
||||
})
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export type TSecretApprovalRequest<J extends unknown = EncryptedSecret> = {
|
||||
id: string;
|
||||
slug: string;
|
||||
createdAt: string;
|
||||
committer: string;
|
||||
committerId: string;
|
||||
reviewers: {
|
||||
member: string;
|
||||
status: ApprovalStatus;
|
||||
|
||||
@@ -89,7 +89,7 @@ export const SecretApprovalRequest = () => {
|
||||
members={membersGroupById}
|
||||
approvalRequestId={selectedApproval?.id || ""}
|
||||
onGoBack={handleGoBackSecretRequestDetail}
|
||||
committer={membersGroupById?.[selectedApproval?.committer || ""]}
|
||||
committer={membersGroupById?.[selectedApproval?.committerId || ""]}
|
||||
/>
|
||||
</motion.div>
|
||||
) : (
|
||||
@@ -195,7 +195,7 @@ export const SecretApprovalRequest = () => {
|
||||
const {
|
||||
id: reqId,
|
||||
commits,
|
||||
committer,
|
||||
committerId,
|
||||
createdAt,
|
||||
policy,
|
||||
reviewers,
|
||||
@@ -225,9 +225,9 @@ export const SecretApprovalRequest = () => {
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">
|
||||
Opened {formatDistance(new Date(createdAt), new Date())} ago by{" "}
|
||||
{membersGroupById?.[committer]?.user?.firstName}{" "}
|
||||
{membersGroupById?.[committer]?.user?.lastName} (
|
||||
{membersGroupById?.[committer]?.user?.email}){" "}
|
||||
{membersGroupById?.[committerId]?.user?.firstName}{" "}
|
||||
{membersGroupById?.[committerId]?.user?.lastName} (
|
||||
{membersGroupById?.[committerId]?.user?.email}){" "}
|
||||
{isApprover && !isReviewed && status === "open" && "- Review required"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user