mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 17:26:19 +00:00
merge + final tweaks
This commit is contained in:
+15
-11
@@ -23,6 +23,7 @@ import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-poli
|
|||||||
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
||||||
import { TGroupDALFactory } from "../group/group-dal";
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { ProjectPermissionApprovalActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
|
||||||
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
||||||
@@ -340,17 +341,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const { membership, hasRole, permission } = await permissionService.getProjectPermission({
|
||||||
!policy.allowedSelfApprovals &&
|
|
||||||
actorId === accessApprovalRequest.requestedByUserId &&
|
|
||||||
policy.enforcementLevel !== EnforcementLevel.Soft
|
|
||||||
) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
@@ -363,6 +354,19 @@ 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 (
|
||||||
|
!policy.allowedSelfApprovals &&
|
||||||
|
actorId === accessApprovalRequest.requestedByUserId &&
|
||||||
|
!(
|
||||||
|
policy.enforcementLevel === EnforcementLevel.Soft &&
|
||||||
|
permission.can(ProjectPermissionApprovalActions.AllowAccessBypass, ProjectPermissionSub.SecretApproval)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
||||||
|
|||||||
@@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
environmentsUsed: 0,
|
environmentsUsed: 0,
|
||||||
identityLimit: null,
|
identityLimit: null,
|
||||||
identitiesUsed: 0,
|
identitiesUsed: 0,
|
||||||
dynamicSecret: true,
|
dynamicSecret: false,
|
||||||
secretVersioning: true,
|
secretVersioning: true,
|
||||||
pitRecovery: true,
|
pitRecovery: false,
|
||||||
ipAllowlisting: true,
|
ipAllowlisting: false,
|
||||||
rbac: true,
|
rbac: false,
|
||||||
githubOrgSync: true,
|
githubOrgSync: false,
|
||||||
customRateLimits: false,
|
customRateLimits: false,
|
||||||
customAlerts: true,
|
customAlerts: false,
|
||||||
secretAccessInsights: true,
|
secretAccessInsights: false,
|
||||||
auditLogs: true,
|
auditLogs: false,
|
||||||
auditLogsRetentionDays: 3,
|
auditLogsRetentionDays: 0,
|
||||||
auditLogStreams: true,
|
auditLogStreams: false,
|
||||||
auditLogStreamLimit: 3,
|
auditLogStreamLimit: 3,
|
||||||
samlSSO: true,
|
samlSSO: false,
|
||||||
hsm: true,
|
hsm: false,
|
||||||
oidcSSO: true,
|
oidcSSO: false,
|
||||||
scim: true,
|
scim: false,
|
||||||
ldap: true,
|
ldap: false,
|
||||||
groups: true,
|
groups: false,
|
||||||
status: null,
|
status: null,
|
||||||
trial_end: null,
|
trial_end: null,
|
||||||
has_used_trial: true,
|
has_used_trial: true,
|
||||||
secretApproval: true,
|
secretApproval: false,
|
||||||
secretRotation: true,
|
secretRotation: false,
|
||||||
caCrl: true,
|
caCrl: false,
|
||||||
instanceUserManagement: true,
|
instanceUserManagement: false,
|
||||||
externalKms: true,
|
externalKms: false,
|
||||||
rateLimits: {
|
rateLimits: {
|
||||||
readLimit: 60,
|
readLimit: 60,
|
||||||
writeLimit: 200,
|
writeLimit: 200,
|
||||||
@@ -52,10 +52,10 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
},
|
},
|
||||||
pkiEst: true,
|
pkiEst: true,
|
||||||
enforceMfa: true,
|
enforceMfa: true,
|
||||||
projectTemplates: true,
|
projectTemplates: false,
|
||||||
kmip: true,
|
kmip: false,
|
||||||
gateway: true,
|
gateway: false,
|
||||||
sshHostGroups: true
|
sshHostGroups: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ const buildAdminPermissionRules = () => {
|
|||||||
ProjectPermissionApprovalActions.Edit,
|
ProjectPermissionApprovalActions.Edit,
|
||||||
ProjectPermissionApprovalActions.Create,
|
ProjectPermissionApprovalActions.Create,
|
||||||
ProjectPermissionApprovalActions.Delete,
|
ProjectPermissionApprovalActions.Delete,
|
||||||
ProjectPermissionApprovalActions.AllowChangeBypass
|
ProjectPermissionApprovalActions.AllowChangeBypass,
|
||||||
|
ProjectPermissionApprovalActions.AllowAccessBypass
|
||||||
],
|
],
|
||||||
ProjectPermissionSub.SecretApproval
|
ProjectPermissionSub.SecretApproval
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export enum ProjectPermissionApprovalActions {
|
|||||||
Create = "create",
|
Create = "create",
|
||||||
Edit = "edit",
|
Edit = "edit",
|
||||||
Delete = "delete",
|
Delete = "delete",
|
||||||
AllowChangeBypass = "allow-change-bypass"
|
AllowChangeBypass = "allow-change-bypass",
|
||||||
|
AllowAccessBypass = "allow-access-bypass"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProjectPermissionCmekActions {
|
export enum ProjectPermissionCmekActions {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ title: "Access Requests"
|
|||||||
description: "Learn how to request access to sensitive resources in Infisical."
|
description: "Learn how to request access to sensitive resources in Infisical."
|
||||||
---
|
---
|
||||||
|
|
||||||
In certain situations, developers need to expand their access to a certain new project or a sensitive environment. For those use cases, it is helpful to utilize Infisical's **Access Requests** functionality.
|
In certain situations, developers need to expand their access to a certain new project or a sensitive environment. For those use cases, it is helpful to utilize Infisical's **Access Requests** functionality.
|
||||||
|
|
||||||
This functionality works in the following way:
|
This functionality works in the following way:
|
||||||
1. A project administrator sets up an access policy that assigns access managers (also known as eligible approvers) to a certain sensitive folder or environment.
|
1. A project administrator sets up an access policy that assigns access managers (also known as eligible approvers) to a certain sensitive folder or environment.
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
@@ -19,9 +19,8 @@ This functionality works in the following way:
|
|||||||

|

|
||||||
|
|
||||||
<Info>
|
<Info>
|
||||||
If the access request matches with a policy that has a **Soft** enforcement level, the requester may bypass the policy and get access to the resource without full approval.
|
If the access request matches with a policy that has allows break-glass approval bypasses, the requester may bypass the policy and get access to the resource without full approval.
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
5. As soon as the request is approved, developer is able to access the sought resources.
|
5. As soon as the request is approved, developer is able to access the sought resources.
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ First, you would need to create a set of policies for a certain environment. In
|
|||||||
|
|
||||||
The enforcement level determines how strict the policy is. A **Hard** enforcement level means that any change that matches the policy will need full approval prior merging. A **Soft** enforcement level allows for break glass functionality on the request. If a change request is bypassed, the approvers will be notified via email.
|
The enforcement level determines how strict the policy is. A **Hard** enforcement level means that any change that matches the policy will need full approval prior merging. A **Soft** enforcement level allows for break glass functionality on the request. If a change request is bypassed, the approvers will be notified via email.
|
||||||
|
|
||||||
|
You can use the **Soft** enforcement level by enabling "Bypass Approvals".
|
||||||
|
|
||||||
### Self approvals
|
### Self approvals
|
||||||
|
|
||||||
If the **Self Approvals** option is enabled, users who are designated as approvers on the policy can approve requests that they themselves have submitted.
|
If the **Self Approvals** option is enabled, users who are designated as approvers on the policy can approve requests that they themselves have submitted.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 129 KiB |
@@ -185,6 +185,7 @@ Supports conditions and permission inversion
|
|||||||
| `edit` | Modify approval policies |
|
| `edit` | Modify approval policies |
|
||||||
| `delete` | Remove approval policies |
|
| `delete` | Remove approval policies |
|
||||||
| `allow-change-bypass` | Allow request creators to bypass policy in break-glass situations |
|
| `allow-change-bypass` | Allow request creators to bypass policy in break-glass situations |
|
||||||
|
| `allow-access-bypass` | Allow request creators to bypass policy in break-glass situations |
|
||||||
|
|
||||||
#### Subject: `secret-rotation`
|
#### Subject: `secret-rotation`
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export enum ProjectPermissionApprovalActions {
|
|||||||
Create = "create",
|
Create = "create",
|
||||||
Edit = "edit",
|
Edit = "edit",
|
||||||
Delete = "delete",
|
Delete = "delete",
|
||||||
AllowChangeBypass = "allow-change-bypass"
|
AllowChangeBypass = "allow-change-bypass",
|
||||||
|
AllowAccessBypass = "allow-access-bypass"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProjectPermissionDynamicSecretActions {
|
export enum ProjectPermissionDynamicSecretActions {
|
||||||
|
|||||||
+7
-2
@@ -58,7 +58,8 @@ const ApprovalPolicyActionSchema = z.object({
|
|||||||
[ProjectPermissionApprovalActions.Edit]: z.boolean().optional(),
|
[ProjectPermissionApprovalActions.Edit]: z.boolean().optional(),
|
||||||
[ProjectPermissionApprovalActions.Delete]: z.boolean().optional(),
|
[ProjectPermissionApprovalActions.Delete]: z.boolean().optional(),
|
||||||
[ProjectPermissionApprovalActions.Create]: z.boolean().optional(),
|
[ProjectPermissionApprovalActions.Create]: z.boolean().optional(),
|
||||||
[ProjectPermissionApprovalActions.AllowChangeBypass]: z.boolean().optional()
|
[ProjectPermissionApprovalActions.AllowChangeBypass]: z.boolean().optional(),
|
||||||
|
[ProjectPermissionApprovalActions.AllowAccessBypass]: z.boolean().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
const CmekPolicyActionSchema = z.object({
|
const CmekPolicyActionSchema = z.object({
|
||||||
@@ -578,6 +579,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
|||||||
const canEdit = action.includes(ProjectPermissionApprovalActions.Edit);
|
const canEdit = action.includes(ProjectPermissionApprovalActions.Edit);
|
||||||
const canRead = action.includes(ProjectPermissionApprovalActions.Read);
|
const canRead = action.includes(ProjectPermissionApprovalActions.Read);
|
||||||
const canChangeBypass = action.includes(ProjectPermissionApprovalActions.AllowChangeBypass);
|
const canChangeBypass = action.includes(ProjectPermissionApprovalActions.AllowChangeBypass);
|
||||||
|
const canAccessBypass = action.includes(ProjectPermissionApprovalActions.AllowAccessBypass);
|
||||||
|
|
||||||
if (!formVal[subject]) formVal[subject] = [{}];
|
if (!formVal[subject]) formVal[subject] = [{}];
|
||||||
|
|
||||||
@@ -588,6 +590,8 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
|||||||
if (canRead) formVal[subject]![0][ProjectPermissionApprovalActions.Read] = true;
|
if (canRead) formVal[subject]![0][ProjectPermissionApprovalActions.Read] = true;
|
||||||
if (canChangeBypass)
|
if (canChangeBypass)
|
||||||
formVal[subject]![0][ProjectPermissionApprovalActions.AllowChangeBypass] = true;
|
formVal[subject]![0][ProjectPermissionApprovalActions.AllowChangeBypass] = true;
|
||||||
|
if (canAccessBypass)
|
||||||
|
formVal[subject]![0][ProjectPermissionApprovalActions.AllowAccessBypass] = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1212,7 +1216,8 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
|||||||
{ label: "Create", value: ProjectPermissionApprovalActions.Create },
|
{ label: "Create", value: ProjectPermissionApprovalActions.Create },
|
||||||
{ label: "Modify", value: ProjectPermissionApprovalActions.Edit },
|
{ label: "Modify", value: ProjectPermissionApprovalActions.Edit },
|
||||||
{ label: "Remove", value: ProjectPermissionApprovalActions.Delete },
|
{ label: "Remove", value: ProjectPermissionApprovalActions.Delete },
|
||||||
{ label: "Allow Change Bypass", value: ProjectPermissionApprovalActions.AllowChangeBypass }
|
{ label: "Allow Change Bypass", value: ProjectPermissionApprovalActions.AllowChangeBypass },
|
||||||
|
{ label: "Allow Access Bypass", value: ProjectPermissionApprovalActions.AllowAccessBypass }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[ProjectPermissionSub.SecretRotation]: {
|
[ProjectPermissionSub.SecretRotation]: {
|
||||||
|
|||||||
+27
-8
@@ -25,6 +25,7 @@ import {
|
|||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { Badge } from "@app/components/v2/Badge";
|
import { Badge } from "@app/components/v2/Badge";
|
||||||
import {
|
import {
|
||||||
|
ProjectPermissionApprovalActions,
|
||||||
ProjectPermissionMemberActions,
|
ProjectPermissionMemberActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useProjectPermission,
|
useProjectPermission,
|
||||||
@@ -100,6 +101,11 @@ export const AccessApprovalRequest = ({
|
|||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
const canBypassApprovalPermission = permission.can(
|
||||||
|
ProjectPermissionApprovalActions.AllowAccessBypass,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
const { data: members } = useGetWorkspaceUsers(projectId, true);
|
const { data: members } = useGetWorkspaceUsers(projectId, true);
|
||||||
const membersGroupById = members?.reduce<Record<string, TWorkspaceUser>>(
|
const membersGroupById = members?.reduce<Record<string, TWorkspaceUser>>(
|
||||||
(prev, curr) => ({ ...prev, [curr.user.id]: curr }),
|
(prev, curr) => ({ ...prev, [curr.user.id]: curr }),
|
||||||
@@ -350,7 +356,11 @@ export const AccessApprovalRequest = ({
|
|||||||
details.isReviewedByUser ||
|
details.isReviewedByUser ||
|
||||||
details.isRejectedByAnyone ||
|
details.isRejectedByAnyone ||
|
||||||
(!details.isApprover &&
|
(!details.isApprover &&
|
||||||
!(details.isSoftEnforcement && details.isRequestedByCurrentUser))
|
!(
|
||||||
|
details.isSoftEnforcement &&
|
||||||
|
details.isRequestedByCurrentUser &&
|
||||||
|
canBypassApprovalPermission
|
||||||
|
))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -360,9 +370,11 @@ export const AccessApprovalRequest = ({
|
|||||||
) {
|
) {
|
||||||
setSelectedRequest({
|
setSelectedRequest({
|
||||||
...request,
|
...request,
|
||||||
user: details.isRequestedByCurrentUser
|
user:
|
||||||
? user
|
details.isRequestedByCurrentUser ||
|
||||||
: membersGroupById?.[request.requestedByUserId].user!,
|
!membersGroupById?.[request.requestedByUserId].user
|
||||||
|
? user
|
||||||
|
: membersGroupById?.[request.requestedByUserId].user,
|
||||||
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
||||||
isApprover: details.isApprover
|
isApprover: details.isApprover
|
||||||
});
|
});
|
||||||
@@ -376,7 +388,11 @@ export const AccessApprovalRequest = ({
|
|||||||
details.isReviewedByUser ||
|
details.isReviewedByUser ||
|
||||||
details.isRejectedByAnyone ||
|
details.isRejectedByAnyone ||
|
||||||
(!details.isApprover &&
|
(!details.isApprover &&
|
||||||
!(details.isSoftEnforcement && details.isRequestedByCurrentUser))
|
!(
|
||||||
|
details.isSoftEnforcement &&
|
||||||
|
details.isRequestedByCurrentUser &&
|
||||||
|
canBypassApprovalPermission
|
||||||
|
))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -387,9 +403,11 @@ export const AccessApprovalRequest = ({
|
|||||||
) {
|
) {
|
||||||
setSelectedRequest({
|
setSelectedRequest({
|
||||||
...request,
|
...request,
|
||||||
user: details.isRequestedByCurrentUser
|
user:
|
||||||
? user
|
details.isRequestedByCurrentUser ||
|
||||||
: membersGroupById?.[request.requestedByUserId].user!,
|
!membersGroupById?.[request.requestedByUserId].user
|
||||||
|
? user
|
||||||
|
: membersGroupById?.[request.requestedByUserId].user,
|
||||||
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
isRequestedByCurrentUser: details.isRequestedByCurrentUser,
|
||||||
isApprover: details.isApprover
|
isApprover: details.isApprover
|
||||||
});
|
});
|
||||||
@@ -463,6 +481,7 @@ export const AccessApprovalRequest = ({
|
|||||||
setSelectedRequest(null);
|
setSelectedRequest(null);
|
||||||
refetchRequests();
|
refetchRequests();
|
||||||
}}
|
}}
|
||||||
|
canBypassApprovalPermission={canBypassApprovalPermission}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
+46
-37
@@ -1,5 +1,8 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, Checkbox, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
import { Button, Checkbox, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
||||||
@@ -8,9 +11,6 @@ import { ProjectPermissionActions } from "@app/context";
|
|||||||
import { useReviewAccessRequest } from "@app/hooks/api";
|
import { useReviewAccessRequest } from "@app/hooks/api";
|
||||||
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
|
import { TAccessApprovalRequest } from "@app/hooks/api/accessApproval/types";
|
||||||
import { EnforcementLevel } from "@app/hooks/api/policies/enums";
|
import { EnforcementLevel } from "@app/hooks/api/policies/enums";
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
|
|
||||||
export const ReviewAccessRequestModal = ({
|
export const ReviewAccessRequestModal = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
@@ -18,7 +18,8 @@ export const ReviewAccessRequestModal = ({
|
|||||||
request,
|
request,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
selectedRequester,
|
selectedRequester,
|
||||||
selectedEnvSlug
|
selectedEnvSlug,
|
||||||
|
canBypassApprovalPermission
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
@@ -30,6 +31,7 @@ export const ReviewAccessRequestModal = ({
|
|||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
selectedRequester: string | undefined;
|
selectedRequester: string | undefined;
|
||||||
selectedEnvSlug: string | undefined;
|
selectedEnvSlug: string | undefined;
|
||||||
|
canBypassApprovalPermission: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null);
|
const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null);
|
||||||
const [bypassApproval, setBypassApproval] = useState(false);
|
const [bypassApproval, setBypassApproval] = useState(false);
|
||||||
@@ -182,7 +184,11 @@ export const ReviewAccessRequestModal = ({
|
|||||||
<Button
|
<Button
|
||||||
isLoading={isLoading === "approved"}
|
isLoading={isLoading === "approved"}
|
||||||
isDisabled={
|
isDisabled={
|
||||||
!!isLoading || (!request.isApprover && !bypassApproval && isSoftEnforcement)
|
!!isLoading ||
|
||||||
|
(!request.isApprover &&
|
||||||
|
!bypassApproval &&
|
||||||
|
isSoftEnforcement &&
|
||||||
|
canBypassApprovalPermission)
|
||||||
}
|
}
|
||||||
onClick={() => handleReview("approved")}
|
onClick={() => handleReview("approved")}
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
@@ -202,39 +208,42 @@ export const ReviewAccessRequestModal = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isSoftEnforcement && request.isRequestedByCurrentUser && !request.isApprover && (
|
{isSoftEnforcement &&
|
||||||
<div className="mt-2 flex flex-col space-y-2">
|
request.isRequestedByCurrentUser &&
|
||||||
<Checkbox
|
!request.isApprover &&
|
||||||
onCheckedChange={(checked) => setBypassApproval(checked === true)}
|
canBypassApprovalPermission && (
|
||||||
isChecked={bypassApproval}
|
<div className="mt-2 flex flex-col space-y-2">
|
||||||
id="byPassApproval"
|
<Checkbox
|
||||||
checkIndicatorBg="text-white"
|
onCheckedChange={(checked) => setBypassApproval(checked === true)}
|
||||||
className={twMerge(
|
isChecked={bypassApproval}
|
||||||
"mr-2",
|
id="byPassApproval"
|
||||||
bypassApproval ? "border-red bg-red hover:bg-red-600" : ""
|
checkIndicatorBg="text-white"
|
||||||
)}
|
className={twMerge(
|
||||||
>
|
"mr-2",
|
||||||
<span className="text-xs text-red">
|
bypassApproval ? "border-red bg-red hover:bg-red-600" : ""
|
||||||
Approve without waiting for requirements to be met (bypass policy protection)
|
)}
|
||||||
</span>
|
|
||||||
</Checkbox>
|
|
||||||
{bypassApproval && (
|
|
||||||
<FormControl
|
|
||||||
label="Reason for bypass"
|
|
||||||
className="mt-2"
|
|
||||||
isRequired
|
|
||||||
tooltipText="Enter a reason for bypassing the secret change policy"
|
|
||||||
>
|
>
|
||||||
<Input
|
<span className="text-xs text-red">
|
||||||
value={bypassReason}
|
Approve without waiting for requirements to be met (bypass policy protection)
|
||||||
onChange={(e) => setBypassReason(e.currentTarget.value)}
|
</span>
|
||||||
placeholder="Enter reason for bypass (min 10 chars)"
|
</Checkbox>
|
||||||
leftIcon={<FontAwesomeIcon icon={faTriangleExclamation} />}
|
{bypassApproval && (
|
||||||
/>
|
<FormControl
|
||||||
</FormControl>
|
label="Reason for bypass"
|
||||||
)}
|
className="mt-2"
|
||||||
</div>
|
isRequired
|
||||||
)}
|
tooltipText="Enter a reason for bypassing the secret change policy"
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={bypassReason}
|
||||||
|
onChange={(e) => setBypassReason(e.currentTarget.value)}
|
||||||
|
placeholder="Enter reason for bypass (min 10 chars)"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faTriangleExclamation} />}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
Reference in New Issue
Block a user