mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 21:27:31 +00:00
Review fixes:
- Review envName from endpoint params and derive it - Use variables in logic blocks - New function on frontend + memoization
This commit is contained in:
@@ -155,7 +155,6 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]),
|
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]),
|
||||||
envName: z.string().optional(), // For logging
|
|
||||||
bypassReason: z.string().min(10).max(1000).optional()
|
bypassReason: z.string().min(10).max(1000).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@@ -173,7 +172,6 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
requestId: req.params.requestId,
|
requestId: req.params.requestId,
|
||||||
status: req.body.status,
|
status: req.body.status,
|
||||||
envName: req.body.envName,
|
|
||||||
bypassReason: req.body.bypassReason
|
bypassReason: req.body.bypassReason
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -326,7 +326,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
envName,
|
|
||||||
bypassReason
|
bypassReason
|
||||||
}: TReviewAccessRequestDTO) => {
|
}: TReviewAccessRequestDTO) => {
|
||||||
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
||||||
@@ -334,7 +333,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { policy } = accessApprovalRequest;
|
const { policy, environment } = accessApprovalRequest;
|
||||||
if (policy.deletedAt) {
|
if (policy.deletedAt) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "The policy associated with this access request has been deleted."
|
message: "The policy associated with this access request has been deleted."
|
||||||
@@ -354,14 +353,15 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
|
||||||
!policy.allowedSelfApprovals &&
|
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
actorId === accessApprovalRequest.requestedByUserId &&
|
const canBypassApproval = permission.can(
|
||||||
!(
|
ProjectPermissionApprovalActions.AllowAccessBypass,
|
||||||
policy.enforcementLevel === EnforcementLevel.Soft &&
|
ProjectPermissionSub.SecretApproval
|
||||||
permission.can(ProjectPermissionApprovalActions.AllowAccessBypass, ProjectPermissionSub.SecretApproval)
|
);
|
||||||
)
|
const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypassApproval);
|
||||||
) {
|
|
||||||
|
if (!policy.allowedSelfApprovals && isSelfApproval && cannotBypassUnderSoftEnforcement) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
||||||
});
|
});
|
||||||
@@ -508,7 +508,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
requesterEmail: actingUser.email,
|
requesterEmail: actingUser.email,
|
||||||
bypassReason: bypassReason || "No reason provided",
|
bypassReason: bypassReason || "No reason provided",
|
||||||
secretPath: policy.secretPath || "/",
|
secretPath: policy.secretPath || "/",
|
||||||
environment: envName || "Unknown",
|
environment,
|
||||||
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`,
|
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`,
|
||||||
requestType: "access"
|
requestType: "access"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -129,30 +129,27 @@ export const useReviewAccessRequest = () => {
|
|||||||
requestId: string;
|
requestId: string;
|
||||||
status: "approved" | "rejected";
|
status: "approved" | "rejected";
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
envName?: string;
|
|
||||||
envSlug?: string;
|
envSlug?: string;
|
||||||
requestedBy?: string;
|
requestedBy?: string;
|
||||||
bypassReason?: string;
|
bypassReason?: string;
|
||||||
}
|
}
|
||||||
>({
|
>({
|
||||||
mutationFn: async ({ requestId, status, envName, bypassReason }) => {
|
mutationFn: async ({ requestId, status, bypassReason }) => {
|
||||||
const { data } = await apiRequest.post(
|
const { data } = await apiRequest.post(
|
||||||
`/api/v1/access-approvals/requests/${requestId}/review`,
|
`/api/v1/access-approvals/requests/${requestId}/review`,
|
||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
envName,
|
|
||||||
bypassReason
|
bypassReason
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { projectSlug, envSlug, requestedBy, envName, bypassReason }) => {
|
onSuccess: (_, { projectSlug, envSlug, requestedBy, bypassReason }) => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: accessApprovalKeys.getAccessApprovalRequests(
|
queryKey: accessApprovalKeys.getAccessApprovalRequests(
|
||||||
projectSlug,
|
projectSlug,
|
||||||
envSlug,
|
envSlug,
|
||||||
requestedBy,
|
requestedBy,
|
||||||
envName,
|
|
||||||
bypassReason
|
bypassReason
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,13 +23,8 @@ export const accessApprovalKeys = {
|
|||||||
projectSlug: string,
|
projectSlug: string,
|
||||||
envSlug?: string,
|
envSlug?: string,
|
||||||
requestedBy?: string,
|
requestedBy?: string,
|
||||||
envName?: string,
|
|
||||||
bypassReason?: string
|
bypassReason?: string
|
||||||
) =>
|
) => [{ projectSlug, envSlug, requestedBy, bypassReason }, "access-approvals-requests"] as const,
|
||||||
[
|
|
||||||
{ projectSlug, envSlug, requestedBy, envName, bypassReason },
|
|
||||||
"access-approvals-requests"
|
|
||||||
] as const,
|
|
||||||
getAccessApprovalRequestCount: (projectSlug: string) =>
|
getAccessApprovalRequestCount: (projectSlug: string) =>
|
||||||
[{ projectSlug }, "access-approval-request-count"] as const
|
[{ projectSlug }, "access-approval-request-count"] as const
|
||||||
};
|
};
|
||||||
|
|||||||
+98
-123
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
/* eslint-disable no-nested-ternary */
|
||||||
/* eslint-disable react/jsx-no-useless-fragment */
|
/* eslint-disable react/jsx-no-useless-fragment */
|
||||||
import { useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
faCheck,
|
faCheck,
|
||||||
faCheckCircle,
|
faCheckCircle,
|
||||||
@@ -150,56 +150,105 @@ export const AccessApprovalRequest = ({
|
|||||||
return requests;
|
return requests;
|
||||||
}, [requests, statusFilter, requestedByFilter, envFilter]);
|
}, [requests, statusFilter, requestedByFilter, envFilter]);
|
||||||
|
|
||||||
const generateRequestDetails = (request: TAccessApprovalRequest) => {
|
const generateRequestDetails = useCallback(
|
||||||
const isReviewedByUser = request.reviewers.findIndex(({ member }) => member === user.id) !== -1;
|
(request: TAccessApprovalRequest) => {
|
||||||
const isRejectedByAnyone = request.reviewers.some(
|
const isReviewedByUser =
|
||||||
({ status }) => status === ApprovalStatus.REJECTED
|
request.reviewers.findIndex(({ member }) => member === user.id) !== -1;
|
||||||
);
|
const isRejectedByAnyone = request.reviewers.some(
|
||||||
const isApprover = request.policy.approvers.indexOf(user.id || "") !== -1;
|
({ status }) => status === ApprovalStatus.REJECTED
|
||||||
const isAccepted = request.isApproved;
|
);
|
||||||
const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft;
|
const isApprover = request.policy.approvers.indexOf(user.id || "") !== -1;
|
||||||
const isRequestedByCurrentUser = request.requestedByUserId === user.id;
|
const isAccepted = request.isApproved;
|
||||||
const isSelfApproveAllowed = request.policy.allowedSelfApprovals;
|
const isSoftEnforcement = request.policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
const userReviewStatus = request.reviewers.find(({ member }) => member === user.id)?.status;
|
const isRequestedByCurrentUser = request.requestedByUserId === user.id;
|
||||||
|
const isSelfApproveAllowed = request.policy.allowedSelfApprovals;
|
||||||
|
const userReviewStatus = request.reviewers.find(({ member }) => member === user.id)?.status;
|
||||||
|
|
||||||
let displayData: { label: string; type: "primary" | "danger" | "success" } = {
|
let displayData: { label: string; type: "primary" | "danger" | "success" } = {
|
||||||
label: "",
|
label: "",
|
||||||
type: "primary"
|
|
||||||
};
|
|
||||||
|
|
||||||
const isExpired =
|
|
||||||
request.privilege &&
|
|
||||||
request.isApproved &&
|
|
||||||
new Date() > new Date(request.privilege.temporaryAccessEndTime || ("" as string));
|
|
||||||
|
|
||||||
if (isExpired) displayData = { label: "Access Expired", type: "danger" };
|
|
||||||
else if (isAccepted) displayData = { label: "Access Granted", type: "success" };
|
|
||||||
else if (isRejectedByAnyone) displayData = { label: "Rejected", type: "danger" };
|
|
||||||
else if (userReviewStatus === ApprovalStatus.APPROVED) {
|
|
||||||
displayData = {
|
|
||||||
label: `Pending ${request.policy.approvals - request.reviewers.length} review${
|
|
||||||
request.policy.approvals - request.reviewers.length > 1 ? "s" : ""
|
|
||||||
}`,
|
|
||||||
type: "primary"
|
|
||||||
};
|
|
||||||
} else if (!isReviewedByUser)
|
|
||||||
displayData = {
|
|
||||||
label: "Review Required",
|
|
||||||
type: "primary"
|
type: "primary"
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
const isExpired =
|
||||||
displayData,
|
request.privilege &&
|
||||||
isReviewedByUser,
|
request.isApproved &&
|
||||||
isRejectedByAnyone,
|
new Date() > new Date(request.privilege.temporaryAccessEndTime || ("" as string));
|
||||||
isApprover,
|
|
||||||
userReviewStatus,
|
if (isExpired) displayData = { label: "Access Expired", type: "danger" };
|
||||||
isAccepted,
|
else if (isAccepted) displayData = { label: "Access Granted", type: "success" };
|
||||||
isSoftEnforcement,
|
else if (isRejectedByAnyone) displayData = { label: "Rejected", type: "danger" };
|
||||||
isRequestedByCurrentUser,
|
else if (userReviewStatus === ApprovalStatus.APPROVED) {
|
||||||
isSelfApproveAllowed
|
displayData = {
|
||||||
};
|
label: `Pending ${request.policy.approvals - request.reviewers.length} review${
|
||||||
};
|
request.policy.approvals - request.reviewers.length > 1 ? "s" : ""
|
||||||
|
}`,
|
||||||
|
type: "primary"
|
||||||
|
};
|
||||||
|
} else if (!isReviewedByUser)
|
||||||
|
displayData = {
|
||||||
|
label: "Review Required",
|
||||||
|
type: "primary"
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayData,
|
||||||
|
isReviewedByUser,
|
||||||
|
isRejectedByAnyone,
|
||||||
|
isApprover,
|
||||||
|
userReviewStatus,
|
||||||
|
isAccepted,
|
||||||
|
isSoftEnforcement,
|
||||||
|
isRequestedByCurrentUser,
|
||||||
|
isSelfApproveAllowed
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelectRequest = useCallback(
|
||||||
|
(request: TAccessApprovalRequest) => {
|
||||||
|
const details = generateRequestDetails(request);
|
||||||
|
|
||||||
|
// Whether the request has already been approved / rejected / reviewed
|
||||||
|
const isInactive =
|
||||||
|
details.isAccepted || details.isReviewedByUser || details.isRejectedByAnyone;
|
||||||
|
|
||||||
|
// Whether the current user can bypass policy
|
||||||
|
const canBypass =
|
||||||
|
details.isSoftEnforcement &&
|
||||||
|
details.isRequestedByCurrentUser &&
|
||||||
|
canBypassApprovalPermission;
|
||||||
|
|
||||||
|
// Whether the current user can approve
|
||||||
|
const canApprove =
|
||||||
|
details.isApprover && (!details.isRequestedByCurrentUser || details.isSelfApproveAllowed);
|
||||||
|
|
||||||
|
if (isInactive || (!canApprove && !canBypass)) return;
|
||||||
|
|
||||||
|
if (membersGroupById?.[request.requestedByUserId].user || details.isRequestedByCurrentUser) {
|
||||||
|
setSelectedRequest({
|
||||||
|
...request,
|
||||||
|
user:
|
||||||
|
details.isRequestedByCurrentUser || !membersGroupById?.[request.requestedByUserId].user
|
||||||
|
? user
|
||||||
|
: membersGroupById?.[request.requestedByUserId].user,
|
||||||
|
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
||||||
|
isSelfApproveAllowed: details.isSelfApproveAllowed,
|
||||||
|
isApprover: details.isApprover
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("reviewRequest");
|
||||||
|
},
|
||||||
|
[
|
||||||
|
generateRequestDetails,
|
||||||
|
canBypassApprovalPermission,
|
||||||
|
membersGroupById,
|
||||||
|
user,
|
||||||
|
setSelectedRequest,
|
||||||
|
handlePopUpOpen
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -351,84 +400,10 @@ export const AccessApprovalRequest = ({
|
|||||||
className="flex w-full cursor-pointer px-8 py-4 hover:bg-mineshaft-700 aria-disabled:opacity-80"
|
className="flex w-full cursor-pointer px-8 py-4 hover:bg-mineshaft-700 aria-disabled:opacity-80"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
onClick={() => handleSelectRequest(request)}
|
||||||
// Whether the request has already been approved / rejected / reviewed
|
|
||||||
const isInactive =
|
|
||||||
details.isAccepted ||
|
|
||||||
details.isReviewedByUser ||
|
|
||||||
details.isRejectedByAnyone;
|
|
||||||
|
|
||||||
// Whether the current user can bypass policy
|
|
||||||
const canBypass =
|
|
||||||
details.isSoftEnforcement &&
|
|
||||||
details.isRequestedByCurrentUser &&
|
|
||||||
canBypassApprovalPermission;
|
|
||||||
|
|
||||||
// Whether the current user can approve
|
|
||||||
const canApprove =
|
|
||||||
details.isApprover &&
|
|
||||||
(!details.isRequestedByCurrentUser || details.isSelfApproveAllowed);
|
|
||||||
|
|
||||||
if (isInactive || (!canApprove && !canBypass)) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
membersGroupById?.[request.requestedByUserId].user ||
|
|
||||||
details.isRequestedByCurrentUser
|
|
||||||
) {
|
|
||||||
setSelectedRequest({
|
|
||||||
...request,
|
|
||||||
user:
|
|
||||||
details.isRequestedByCurrentUser ||
|
|
||||||
!membersGroupById?.[request.requestedByUserId].user
|
|
||||||
? user
|
|
||||||
: membersGroupById?.[request.requestedByUserId].user,
|
|
||||||
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
|
||||||
isSelfApproveAllowed: details.isSelfApproveAllowed,
|
|
||||||
isApprover: details.isApprover
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("reviewRequest");
|
|
||||||
}}
|
|
||||||
onKeyDown={(evt) => {
|
onKeyDown={(evt) => {
|
||||||
// Whether the request has already been approved / rejected / reviewed
|
|
||||||
const isInactive =
|
|
||||||
details.isAccepted ||
|
|
||||||
details.isReviewedByUser ||
|
|
||||||
details.isRejectedByAnyone;
|
|
||||||
|
|
||||||
// Whether the current user can bypass policy
|
|
||||||
const canBypass =
|
|
||||||
details.isSoftEnforcement &&
|
|
||||||
details.isRequestedByCurrentUser &&
|
|
||||||
canBypassApprovalPermission;
|
|
||||||
|
|
||||||
// Whether the current user can approve
|
|
||||||
const canApprove =
|
|
||||||
details.isApprover &&
|
|
||||||
(!details.isRequestedByCurrentUser || details.isSelfApproveAllowed);
|
|
||||||
|
|
||||||
if (isInactive || (!canApprove && !canBypass)) return;
|
|
||||||
|
|
||||||
if (evt.key === "Enter") {
|
if (evt.key === "Enter") {
|
||||||
if (
|
handleSelectRequest(request);
|
||||||
membersGroupById?.[request.requestedByUserId].user ||
|
|
||||||
details.isRequestedByCurrentUser
|
|
||||||
) {
|
|
||||||
setSelectedRequest({
|
|
||||||
...request,
|
|
||||||
user:
|
|
||||||
details.isRequestedByCurrentUser ||
|
|
||||||
!membersGroupById?.[request.requestedByUserId].user
|
|
||||||
? user
|
|
||||||
: membersGroupById?.[request.requestedByUserId].user,
|
|
||||||
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
|
||||||
isSelfApproveAllowed: details.isSelfApproveAllowed,
|
|
||||||
isApprover: details.isApprover
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("reviewRequest");
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
-2
@@ -104,7 +104,6 @@ export const ReviewAccessRequestModal = ({
|
|||||||
requestId: request.id,
|
requestId: request.id,
|
||||||
status,
|
status,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
envName: accessDetails.env,
|
|
||||||
envSlug: selectedEnvSlug,
|
envSlug: selectedEnvSlug,
|
||||||
requestedBy: selectedRequester,
|
requestedBy: selectedRequester,
|
||||||
bypassReason: bypassApproval ? bypassReason : undefined
|
bypassReason: bypassApproval ? bypassReason : undefined
|
||||||
@@ -129,7 +128,6 @@ export const ReviewAccessRequestModal = ({
|
|||||||
bypassReason,
|
bypassReason,
|
||||||
reviewAccessRequest,
|
reviewAccessRequest,
|
||||||
request,
|
request,
|
||||||
accessDetails.env,
|
|
||||||
selectedEnvSlug,
|
selectedEnvSlug,
|
||||||
selectedRequester,
|
selectedRequester,
|
||||||
onOpenChange
|
onOpenChange
|
||||||
|
|||||||
Reference in New Issue
Block a user