grant endpoints

This commit is contained in:
x032205
2025-12-06 20:25:42 -05:00
parent c60c397e6a
commit 68eef39dd7
6 changed files with 156 additions and 5 deletions
@@ -19,7 +19,8 @@ export const registerApprovalPolicyEndpoints = <P extends TApprovalPolicy>({
updatePolicySchema, updatePolicySchema,
policyResponseSchema, policyResponseSchema,
createRequestSchema, createRequestSchema,
requestResponseSchema requestResponseSchema,
grantResponseSchema
}: { }: {
server: FastifyZodProvider; server: FastifyZodProvider;
policyType: ApprovalPolicyType; policyType: ApprovalPolicyType;
@@ -38,6 +39,7 @@ export const registerApprovalPolicyEndpoints = <P extends TApprovalPolicy>({
policyResponseSchema: z.ZodTypeAny; policyResponseSchema: z.ZodTypeAny;
createRequestSchema: z.ZodType<TCreateRequestDTO>; createRequestSchema: z.ZodType<TCreateRequestDTO>;
requestResponseSchema: z.ZodTypeAny; requestResponseSchema: z.ZodTypeAny;
grantResponseSchema: z.ZodTypeAny;
}) => { }) => {
// Policies // Policies
server.route({ server.route({
@@ -365,4 +367,93 @@ export const registerApprovalPolicyEndpoints = <P extends TApprovalPolicy>({
return { request }; return { request };
} }
}); });
// Grants
server.route({
method: "GET",
url: "/grants",
config: {
rateLimit: readLimit
},
schema: {
description: "List approval grants",
querystring: z.object({
projectId: z.string().uuid()
}),
response: {
200: z.object({
grants: z.array(grantResponseSchema)
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { grants } = await server.services.approvalPolicy.listGrants(
policyType,
req.query.projectId,
req.permission
);
// TODO(andrey): Audit log
return { grants };
}
});
server.route({
method: "GET",
url: "/grants/:grantId",
config: {
rateLimit: readLimit
},
schema: {
description: "Get approval grant",
params: z.object({
grantId: z.string().uuid()
}),
response: {
200: z.object({
grant: grantResponseSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { grant } = await server.services.approvalPolicy.getGrantById(req.params.grantId, req.permission);
// TODO(andrey): Audit log
return { grant };
}
});
server.route({
method: "POST",
url: "/grants/:grantId/revoke",
config: {
rateLimit: writeLimit
},
schema: {
description: "Revoke approval grant",
params: z.object({
grantId: z.string().uuid()
}),
body: z.object({
revocationReason: z.string().optional()
}),
response: {
200: z.object({
grant: grantResponseSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { grant } = await server.services.approvalPolicy.revokeGrant(req.params.grantId, req.body, req.permission);
// TODO(andrey): Audit log
return { grant };
}
});
}; };
@@ -3,6 +3,7 @@ import {
CreatePamAccessPolicySchema, CreatePamAccessPolicySchema,
CreatePamAccessRequestSchema, CreatePamAccessRequestSchema,
PamAccessPolicySchema, PamAccessPolicySchema,
PamAccessRequestGrantSchema,
PamAccessRequestSchema, PamAccessRequestSchema,
UpdatePamAccessPolicySchema UpdatePamAccessPolicySchema
} from "@app/services/approval-policy/pam-access/pam-access-policy-schemas"; } from "@app/services/approval-policy/pam-access/pam-access-policy-schemas";
@@ -21,7 +22,8 @@ export const APPROVAL_POLICY_REGISTER_ROUTER_MAP: Record<
updatePolicySchema: UpdatePamAccessPolicySchema, updatePolicySchema: UpdatePamAccessPolicySchema,
policyResponseSchema: PamAccessPolicySchema, policyResponseSchema: PamAccessPolicySchema,
createRequestSchema: CreatePamAccessRequestSchema, createRequestSchema: CreatePamAccessRequestSchema,
requestResponseSchema: PamAccessRequestSchema requestResponseSchema: PamAccessRequestSchema,
grantResponseSchema: PamAccessRequestGrantSchema
}); });
} }
}; };
@@ -3,6 +3,7 @@ import { z } from "zod";
import { import {
ApprovalPoliciesSchema, ApprovalPoliciesSchema,
ApprovalRequestApprovalsSchema, ApprovalRequestApprovalsSchema,
ApprovalRequestGrantsSchema,
ApprovalRequestsSchema, ApprovalRequestsSchema,
ApprovalRequestStepsSchema ApprovalRequestStepsSchema
} from "@app/db/schemas"; } from "@app/db/schemas";
@@ -89,3 +90,6 @@ export const BaseCreateApprovalRequestSchema = z.object({
.nullable() .nullable()
.optional() .optional()
}); });
// Grants
export const BaseApprovalRequestGrantSchema = ApprovalRequestGrantsSchema;
@@ -1,7 +1,7 @@
import { ActionProjectType, ProjectMembershipRole, TApprovalPolicies, TApprovalRequests } from "@app/db/schemas"; import { ActionProjectType, ProjectMembershipRole, TApprovalPolicies, TApprovalRequests } from "@app/db/schemas";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal"; import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { ms } from "@app/lib/ms"; import { ms } from "@app/lib/ms";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { TNotificationServiceFactory } from "@app/services/notification/notification-service"; import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
@@ -21,6 +21,7 @@ import {
import { import {
ApprovalPolicyType, ApprovalPolicyType,
ApprovalRequestApprovalDecision, ApprovalRequestApprovalDecision,
ApprovalRequestGrantStatus,
ApprovalRequestStatus, ApprovalRequestStatus,
ApprovalRequestStepStatus, ApprovalRequestStepStatus,
ApproverType ApproverType
@@ -699,6 +700,50 @@ export const approvalPolicyServiceFactory = ({
return { request: { ...updatedRequest, steps } }; return { request: { ...updatedRequest, steps } };
}; };
const listGrants = async (policyType: ApprovalPolicyType, projectId: string, actor: OrgServiceActor) => {
// TODO(andrey): Perm check
const grants = await approvalRequestGrantsDAL.find({ projectId, type: policyType });
return { grants };
};
const getGrantById = async (grantId: string, actor: OrgServiceActor) => {
// TODO(andrey): Perm check
const grant = await approvalRequestGrantsDAL.findById(grantId);
if (!grant) {
throw new NotFoundError({ message: "Grant not found" });
}
return { grant };
};
const revokeGrant = async (
grantId: string,
{ revocationReason }: { revocationReason?: string },
actor: OrgServiceActor
) => {
// TODO(andrey): Perm check
const grant = await approvalRequestGrantsDAL.findById(grantId);
if (!grant) {
throw new NotFoundError({ message: "Grant not found" });
}
if (grant.status !== ApprovalRequestGrantStatus.Active) {
throw new BadRequestError({ message: "Grant is not active" });
}
const updatedGrant = await approvalRequestGrantsDAL.updateById(grantId, {
status: ApprovalRequestGrantStatus.Revoked,
revokedAt: new Date(),
revokedByUserId: actor.id,
revocationReason
});
return { grant: updatedGrant };
};
return { return {
create, create,
list, list,
@@ -710,6 +755,9 @@ export const approvalPolicyServiceFactory = ({
getRequestById, getRequestById,
approveRequest, approveRequest,
rejectRequest, rejectRequest,
cancelRequest cancelRequest,
listGrants,
getGrantById,
revokeGrant
}; };
}; };
@@ -54,7 +54,7 @@ export interface TCreateRequestDTO {
projectId: TApprovalRequest["projectId"]; projectId: TApprovalRequest["projectId"];
requestData: TApprovalRequest["requestData"]["requestData"]; requestData: TApprovalRequest["requestData"]["requestData"];
justification?: TApprovalRequest["justification"]; justification?: TApprovalRequest["justification"];
requestDuration?: string; requestDuration?: string | null;
} }
// Factory // Factory
@@ -4,6 +4,7 @@ import { ms } from "@app/lib/ms";
import { import {
BaseApprovalPolicySchema, BaseApprovalPolicySchema,
BaseApprovalRequestGrantSchema,
BaseApprovalRequestSchema, BaseApprovalRequestSchema,
BaseCreateApprovalPolicySchema, BaseCreateApprovalPolicySchema,
BaseCreateApprovalRequestSchema, BaseCreateApprovalRequestSchema,
@@ -79,3 +80,8 @@ export const PamAccessRequestSchema = BaseApprovalRequestSchema.extend({
export const CreatePamAccessRequestSchema = BaseCreateApprovalRequestSchema.extend({ export const CreatePamAccessRequestSchema = BaseCreateApprovalRequestSchema.extend({
requestData: PamAccessPolicyRequestDataSchema requestData: PamAccessPolicyRequestDataSchema
}); });
// Grants
export const PamAccessRequestGrantSchema = BaseApprovalRequestGrantSchema.extend({
attributes: PamAccessPolicyRequestDataSchema
});