diff --git a/backend/src/ee/routes/v1/access-approval-request-router.ts b/backend/src/ee/routes/v1/access-approval-request-router.ts index 7a70d6374..a7e8e79cc 100644 --- a/backend/src/ee/routes/v1/access-approval-request-router.ts +++ b/backend/src/ee/routes/v1/access-approval-request-router.ts @@ -116,6 +116,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv approvals: z.number(), approvers: z .object({ + isOrgMembershipActive: z.boolean(), userId: z.string().nullable().optional(), sequence: z.number().nullable().optional(), approvalsRequired: z.number().nullable().optional(), @@ -132,6 +133,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv }), reviewers: z .object({ + isOrgMembershipActive: z.boolean(), userId: z.string(), status: z.string() }) diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index 3d07af562..956baef59 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -294,12 +294,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv 200: z.object({ approval: SecretApprovalRequestsSchema.merge( z.object({ - // secretPath: z.string(), policy: z.object({ id: z.string(), name: z.string(), approvals: z.number(), - approvers: approvalRequestUser.array(), + approvers: approvalRequestUser.extend({ isOrgMembershipActive: z.boolean() }).array(), bypassers: approvalRequestUser.array(), secretPath: z.string().optional().nullable(), enforcementLevel: z.string(), @@ -309,7 +308,9 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv environment: z.string(), statusChangedByUser: approvalRequestUser.optional(), committerUser: approvalRequestUser.nullish(), - reviewers: approvalRequestUser.extend({ status: z.string(), comment: z.string().optional() }).array(), + reviewers: approvalRequestUser + .extend({ status: z.string(), comment: z.string().optional(), isOrgMembershipActive: z.boolean() }) + .array(), secretPath: z.string(), commits: secretRawSchema .omit({ _id: true, environment: true, workspace: true, type: true, version: true, secretValue: true }) diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts index 9872df067..f930ceaf7 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts @@ -5,6 +5,7 @@ import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests, + TOrgMemberships, TUserGroupMembership, TUsers } from "@app/db/schemas"; @@ -143,6 +144,7 @@ export interface TAccessApprovalRequestDALFactory extends Omit( + db(TableName.OrgMembership).as("approverOrgMembership"), + `${TableName.AccessApprovalPolicyApprover}.approverUserId`, + `approverOrgMembership.userId` + ) + + .leftJoin( + db(TableName.OrgMembership).as("approverGroupOrgMembership"), + `${TableName.Users}.id`, + `approverGroupOrgMembership.userId` + ) + + .leftJoin( + db(TableName.OrgMembership).as("reviewerOrgMembership"), + `${TableName.AccessApprovalRequestReviewer}.reviewerUserId`, + `reviewerOrgMembership.userId` + ) + .leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`) .select(selectAllTableCols(TableName.AccessApprovalRequest)) @@ -297,7 +322,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR db.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"), db.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"), db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"), - db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt") + db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt"), + + // Added: + db.ref("isActive").withSchema("approverOrgMembership").as("approverIsOrgMembershipActive"), + db.ref("isActive").withSchema("approverGroupOrgMembership").as("approverGroupIsOrgMembershipActive"), + db.ref("isActive").withSchema("reviewerOrgMembership").as("reviewerIsOrgMembershipActive") ) .select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover)) .select(db.ref("sequence").withSchema(TableName.AccessApprovalPolicyApprover).as("approverSequence")) @@ -392,17 +422,26 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR { key: "reviewerUserId", label: "reviewers" as const, - mapper: ({ reviewerUserId: userId, reviewerStatus: status }) => (userId ? { userId, status } : undefined) + mapper: ({ reviewerUserId: userId, reviewerStatus: status, reviewerIsOrgMembershipActive }) => + userId ? { userId, status, isOrgMembershipActive: reviewerIsOrgMembershipActive } : undefined }, { key: "approverUserId", label: "approvers" as const, - mapper: ({ approverUserId, approverSequence, approvalsRequired, approverUsername, approverEmail }) => ({ + mapper: ({ + approverUserId, + approverSequence, + approvalsRequired, + approverUsername, + approverEmail, + approverIsOrgMembershipActive + }) => ({ userId: approverUserId, sequence: approverSequence, approvalsRequired, email: approverEmail, - username: approverUsername + username: approverUsername, + isOrgMembershipActive: approverIsOrgMembershipActive }) }, { @@ -413,13 +452,15 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR approverSequence, approvalsRequired, approverGroupEmail, - approverGroupUsername + approverGroupUsername, + approverGroupIsOrgMembershipActive }) => ({ userId: approverGroupUserId, sequence: approverSequence, approvalsRequired, email: approverGroupEmail, - username: approverGroupUsername + username: approverGroupUsername, + isOrgMembershipActive: approverGroupIsOrgMembershipActive }) }, { key: "bypasserUserId", label: "bypassers" as const, mapper: ({ bypasserUserId }) => bypasserUserId }, diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts index 9066aec8f..0d318c6c4 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts @@ -64,6 +64,7 @@ export interface TAccessApprovalRequestServiceFactory { approvalsRequired: number | null | undefined; email: string | null | undefined; username: string; + isOrgMembershipActive: boolean; } | { userId: string; @@ -71,6 +72,7 @@ export interface TAccessApprovalRequestServiceFactory { approvalsRequired: number | null | undefined; email: string | null | undefined; username: string; + isOrgMembershipActive: boolean; } )[]; bypassers: string[]; @@ -82,6 +84,7 @@ export interface TAccessApprovalRequestServiceFactory { allowedSelfApprovals: boolean; envId: string; deletedAt: Date | null | undefined; + isOrgMembershipActive: boolean; }; projectId: string; environment: string; @@ -121,6 +124,7 @@ export interface TAccessApprovalRequestServiceFactory { reviewers: { userId: string; status: string; + isOrgMembershipActive: boolean; }[]; approvers: ( | { @@ -129,6 +133,7 @@ export interface TAccessApprovalRequestServiceFactory { approvalsRequired: number | null | undefined; email: string | null | undefined; username: string; + isOrgMembershipActive: boolean; } | { userId: string; @@ -136,6 +141,7 @@ export interface TAccessApprovalRequestServiceFactory { approvalsRequired: number | null | undefined; email: string | null | undefined; username: string; + isOrgMembershipActive: boolean; } )[]; bypassers: string[]; diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index bd3949a7e..be300c0e4 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -31,16 +31,16 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ auditLogsRetentionDays: 0, auditLogStreams: false, auditLogStreamLimit: 3, - samlSSO: false, + samlSSO: true, hsm: false, oidcSSO: false, - scim: false, + scim: true, ldap: false, - groups: false, + groups: true, status: null, trial_end: null, has_used_trial: true, - secretApproval: false, + secretApproval: true, secretRotation: false, caCrl: false, instanceUserManagement: false, diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts index 49f31bdf6..49512768c 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts @@ -4,6 +4,7 @@ import { TDbClient } from "@app/db"; import { SecretApprovalRequestsSchema, TableName, + TOrgMemberships, TSecretApprovalRequests, TSecretApprovalRequestsSecrets, TUserGroupMembership, @@ -107,11 +108,32 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalRequestReviewer}.reviewerUserId`, `secretApprovalReviewerUser.id` ) + + .leftJoin( + db(TableName.OrgMembership).as("approverOrgMembership"), + `${TableName.SecretApprovalPolicyApprover}.approverUserId`, + `approverOrgMembership.userId` + ) + + .leftJoin( + db(TableName.OrgMembership).as("approverGroupOrgMembership"), + `secretApprovalPolicyGroupApproverUser.id`, + `approverGroupOrgMembership.userId` + ) + + .leftJoin( + db(TableName.OrgMembership).as("reviewerOrgMembership"), + `${TableName.SecretApprovalRequestReviewer}.reviewerUserId`, + `reviewerOrgMembership.userId` + ) + .select(selectAllTableCols(TableName.SecretApprovalRequest)) .select( tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover), tx.ref("userId").withSchema("approverUserGroupMembership").as("approverGroupUserId"), tx.ref("email").withSchema("secretApprovalPolicyApproverUser").as("approverEmail"), + tx.ref("isActive").withSchema("approverOrgMembership").as("approverIsOrgMembershipActive"), + tx.ref("isActive").withSchema("approverGroupOrgMembership").as("approverGroupIsOrgMembershipActive"), tx.ref("email").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupEmail"), tx.ref("username").withSchema("secretApprovalPolicyApproverUser").as("approverUsername"), tx.ref("username").withSchema("secretApprovalPolicyGroupApproverUser").as("approverGroupUsername"), @@ -148,6 +170,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { tx.ref("username").withSchema("secretApprovalReviewerUser").as("reviewerUsername"), tx.ref("firstName").withSchema("secretApprovalReviewerUser").as("reviewerFirstName"), tx.ref("lastName").withSchema("secretApprovalReviewerUser").as("reviewerLastName"), + tx.ref("isActive").withSchema("reviewerOrgMembership").as("reviewerIsOrgMembershipActive"), tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"), tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"), tx.ref("projectId").withSchema(TableName.Environment), @@ -211,9 +234,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { reviewerLastName: lastName, reviewerUsername: username, reviewerFirstName: firstName, - reviewerComment: comment + reviewerComment: comment, + reviewerIsOrgMembershipActive: isOrgMembershipActive }) => - userId ? { userId, status, email, firstName, lastName, username, comment: comment ?? "" } : undefined + userId + ? { + userId, + status, + email, + firstName, + lastName, + username, + comment: comment ?? "", + isOrgMembershipActive + } + : undefined }, { key: "approverUserId", @@ -223,13 +258,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { approverEmail: email, approverUsername: username, approverLastName: lastName, - approverFirstName: firstName + approverFirstName: firstName, + approverIsOrgMembershipActive: isOrgMembershipActive }) => ({ userId, email, firstName, lastName, - username + username, + isOrgMembershipActive }) }, { @@ -240,13 +277,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { approverGroupEmail: email, approverGroupUsername: username, approverGroupLastName: lastName, - approverGroupFirstName: firstName + approverGroupFirstName: firstName, + approverGroupIsOrgMembershipActive: isOrgMembershipActive }) => ({ userId, email, firstName, lastName, - username + username, + isOrgMembershipActive }) }, { diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 1d1ca0a00..2cdeeb094 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -258,6 +258,7 @@ export const secretApprovalRequestServiceFactory = ({ if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const secretApprovalRequest = await secretApprovalRequestDAL.findById(id); + if (!secretApprovalRequest) throw new NotFoundError({ message: `Secret approval request with ID '${id}' not found` }); diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 7a5a9341f..5e7dce76e 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -108,7 +108,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { firstName: true, lastName: true, id: true - }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })), + }) + .merge(UserEncryptionKeysSchema.pick({ publicKey: true })) + .extend({ + isOrgMembershipActive: z.boolean() + }), project: SanitizedProjectSchema.pick({ name: true, id: true }), roles: z.array( z.object({ diff --git a/backend/src/services/group-project/group-project-dal.ts b/backend/src/services/group-project/group-project-dal.ts index a776f7245..263838a9d 100644 --- a/backend/src/services/group-project/group-project-dal.ts +++ b/backend/src/services/group-project/group-project-dal.ts @@ -156,6 +156,7 @@ export const groupProjectDALFactory = (db: TDbClient) => { `${TableName.GroupProjectMembershipRole}.customRoleId`, `${TableName.ProjectRoles}.id` ) + .join(TableName.OrgMembership, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) .select( db.ref("id").withSchema(TableName.UserGroupMembership), db.ref("createdAt").withSchema(TableName.UserGroupMembership), @@ -176,7 +177,8 @@ export const groupProjectDALFactory = (db: TDbClient) => { db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole), - db.ref("name").as("projectName").withSchema(TableName.Project) + db.ref("name").as("projectName").withSchema(TableName.Project), + db.ref("isActive").withSchema(TableName.OrgMembership) ) .where({ isGhost: false }); @@ -192,7 +194,8 @@ export const groupProjectDALFactory = (db: TDbClient) => { id, userId, projectName, - createdAt + createdAt, + isActive }) => ({ isGroupMember: true, id, @@ -202,7 +205,7 @@ export const groupProjectDALFactory = (db: TDbClient) => { id: projectId, name: projectName }, - user: { email, username, firstName, lastName, id: userId, publicKey, isGhost }, + user: { email, username, firstName, lastName, id: userId, publicKey, isGhost, isOrgMembershipActive: isActive }, createdAt }), key: "id", diff --git a/backend/src/services/project-membership/project-membership-dal.ts b/backend/src/services/project-membership/project-membership-dal.ts index 7dfd1f65c..9bc7e9aeb 100644 --- a/backend/src/services/project-membership/project-membership-dal.ts +++ b/backend/src/services/project-membership/project-membership-dal.ts @@ -21,6 +21,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => { .where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId }) .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) + .join(TableName.OrgMembership, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) .where((qb) => { if (filter.usernames) { void qb.whereIn("username", filter.usernames); @@ -90,7 +91,8 @@ export const projectMembershipDALFactory = (db: TDbClient) => { db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), - db.ref("name").as("projectName").withSchema(TableName.Project) + db.ref("name").as("projectName").withSchema(TableName.Project), + db.ref("isActive").withSchema(TableName.OrgMembership) ) .where({ isGhost: false }) .orderBy(`${TableName.Users}.username` as "username"); @@ -107,12 +109,22 @@ export const projectMembershipDALFactory = (db: TDbClient) => { id, userId, projectName, - createdAt + createdAt, + isActive }) => ({ id, userId, projectId, - user: { email, username, firstName, lastName, id: userId, publicKey, isGhost }, + user: { + email, + username, + firstName, + lastName, + id: userId, + publicKey, + isGhost, + isOrgMembershipActive: isActive + }, project: { id: projectId, name: projectName diff --git a/backend/src/services/project-membership/project-membership-service.ts b/backend/src/services/project-membership/project-membership-service.ts index b9e502922..7cf665141 100644 --- a/backend/src/services/project-membership/project-membership-service.ts +++ b/backend/src/services/project-membership/project-membership-service.ts @@ -97,7 +97,6 @@ export const projectMembershipServiceFactory = ({ const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId, { roles }); - // projectMembers[0].project if (includeGroupMembers) { const groupMembers = await groupProjectDAL.findAllProjectGroupMembers(projectId); const allMembers = [ diff --git a/frontend/src/hooks/api/accessApproval/types.ts b/frontend/src/hooks/api/accessApproval/types.ts index bc569165b..dde4ed70e 100644 --- a/frontend/src/hooks/api/accessApproval/types.ts +++ b/frontend/src/hooks/api/accessApproval/types.ts @@ -35,6 +35,7 @@ export type Approver = { type: ApproverType; sequence?: number; approvalsRequired?: number; + isOrgMembershipActive: boolean; }; export type Bypasser = { @@ -81,6 +82,7 @@ export type TAccessApprovalRequest = { name: string; approvals: number; approvers: { + isOrgMembershipActive: boolean; userId: string; sequence?: number; approvalsRequired?: number; @@ -96,6 +98,7 @@ export type TAccessApprovalRequest = { }; reviewers: { + isOrgMembershipActive: boolean; userId: string; status: string; }[]; @@ -166,7 +169,7 @@ export type TCreateAccessPolicyDTO = { projectSlug: string; name?: string; environments: string[]; - approvers?: Approver[]; + approvers?: Omit[]; bypassers?: Bypasser[]; approvals?: number; secretPath: string; @@ -178,7 +181,7 @@ export type TCreateAccessPolicyDTO = { export type TUpdateAccessPolicyDTO = { id: string; name?: string; - approvers?: Approver[]; + approvers?: Omit[]; bypassers?: Bypasser[]; secretPath?: string; environments?: string[]; diff --git a/frontend/src/hooks/api/secretApproval/types.ts b/frontend/src/hooks/api/secretApproval/types.ts index 8fd86624d..1785ffa8a 100644 --- a/frontend/src/hooks/api/secretApproval/types.ts +++ b/frontend/src/hooks/api/secretApproval/types.ts @@ -20,6 +20,7 @@ export enum ApproverType { } export type Approver = { + isOrgMembershipActive: boolean; id: string; type: ApproverType; }; @@ -49,7 +50,7 @@ export type TCreateSecretPolicyDTO = { name?: string; environments: string[]; secretPath: string; - approvers?: Approver[]; + approvers?: Omit[]; bypassers?: Bypasser[]; approvals?: number; enforcementLevel: EnforcementLevel; @@ -59,7 +60,7 @@ export type TCreateSecretPolicyDTO = { export type TUpdateSecretPolicyDTO = { id: string; name?: string; - approvers?: Approver[]; + approvers?: Omit[]; bypassers?: Bypasser[]; secretPath?: string; approvals?: number; diff --git a/frontend/src/hooks/api/secretApprovalRequest/types.ts b/frontend/src/hooks/api/secretApprovalRequest/types.ts index caf1df320..0eef3fa3d 100644 --- a/frontend/src/hooks/api/secretApprovalRequest/types.ts +++ b/frontend/src/hooks/api/secretApprovalRequest/types.ts @@ -53,6 +53,7 @@ export type TSecretApprovalRequest = { firstName: string; lastName: string; username: string; + isOrgMembershipActive: boolean; }[]; workspace: string; environment: string; @@ -62,6 +63,7 @@ export type TSecretApprovalRequest = { status: "open" | "close"; policy: Omit & { approvers: { + isOrgMembershipActive: boolean; userId: string; email: string; firstName: string; diff --git a/frontend/src/hooks/api/users/types.ts b/frontend/src/hooks/api/users/types.ts index 3af283b7e..0a43e850e 100644 --- a/frontend/src/hooks/api/users/types.ts +++ b/frontend/src/hooks/api/users/types.ts @@ -83,6 +83,7 @@ export type TProjectMembership = { export type TWorkspaceUser = { id: string; user: { + isOrgMembershipActive: boolean; email: string; username: string; firstName: string; diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx index e9e9c21c4..3e46f59de 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/AccessApprovalRequest/components/ReviewAccessModal.tsx @@ -3,7 +3,10 @@ import { faBan, faCheck, faHourglass, - faTriangleExclamation + faQuestionCircle, + faTriangleExclamation, + faUser, + faUserSlash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import ms from "ms"; @@ -33,7 +36,7 @@ import { EnforcementLevel } from "@app/hooks/api/policies/enums"; import { ApprovalStatus, TWorkspaceUser } from "@app/hooks/api/types"; import { groupBy } from "@app/lib/fn/array"; -const getReviewedStatusSymbol = (status?: ApprovalStatus) => { +const getReviewedStatusSymbol = (status?: ApprovalStatus, isOrgMembershipActive?: boolean) => { if (status === ApprovalStatus.APPROVED) return ( @@ -46,6 +49,16 @@ const getReviewedStatusSymbol = (status?: ApprovalStatus) => { ); + + if (!isOrgMembershipActive) { + return ( + // Can't do a tooltip here because nested tooltips doesn't work properly as of yet. + // TODO(daniel): Fix nested tooltips in the future. + + + + ); + } return ( @@ -81,6 +94,7 @@ export const ReviewAccessRequestModal = ({ }) => { const [isLoading, setIsLoading] = useState<"approved" | "rejected" | null>(null); const [bypassApproval, setBypassApproval] = useState(false); + const [bypassReason, setBypassReason] = useState(""); const { currentWorkspace } = useWorkspace(); const { data: groupMemberships = [] } = useListWorkspaceGroups(currentWorkspace?.id || ""); @@ -215,7 +229,10 @@ export const ReviewAccessRequestModal = ({ const approvers = approversBySequence?.map((approverChain) => { const reviewers = request.policy.approvers .filter((el) => (el.sequence || 1) === approverChain.sequence) - .map((el) => ({ ...el, status: reviewesGroupById?.[el.userId]?.[0]?.status })); + .map((el) => ({ + ...el, + status: reviewesGroupById?.[el.userId]?.[0]?.status + })); const hasApproved = reviewers.filter((el) => el.status === "approved").length >= (approverChain?.approvals || 1); @@ -383,12 +400,31 @@ export const ReviewAccessRequestModal = ({ )}
- - {approver?.user - ?.map( - (el) => approverSequence?.membersGroupById?.[el.id]?.[0]?.user?.username - ) - .join(", ")} + + {Boolean(approver.user.length) && ( +
+ {approver?.user?.map((el) => { + const member = approverSequence?.membersGroupById?.[el.id]?.[0]; + if (!member) return null; + + return member.user.isOrgMembershipActive ? ( + {member.user.username} + ) : ( + + {member.user.username}{" "} + + +
+ (Inactive){" "} + +
+
+
+
+ ); + })} +
+ )}
{approver?.group @@ -413,8 +449,18 @@ export const ReviewAccessRequestModal = ({ key={`reviewer-${idx + 1}`} className="flex items-center gap-2 px-2 py-2 text-sm" > -
{el.username}
- {getReviewedStatusSymbol(el?.status as ApprovalStatus)} +
+ {el.username} +
+ {getReviewedStatusSymbol( + el?.status as ApprovalStatus, + el.isOrgMembershipActive + )}
))} diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx index b19ace7ea..8e25397dc 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/ApprovalPolicyList/components/ApprovalPolicyRow.tsx @@ -3,6 +3,7 @@ import { faClipboardCheck, faEdit, faEllipsisV, + faQuestionCircle, faTrash, faUser, faUserGroup @@ -19,6 +20,7 @@ import { GenericFieldLabel, IconButton, Td, + Tooltip, Tr } from "@app/components/v2"; import { Badge } from "@app/components/v2/Badge"; @@ -86,10 +88,9 @@ export const ApprovalPolicyRow = ({ return entityInSameSequence?.map((el) => { return { sequence: el.sequence || policy.approvals, - userLabels: members - ?.filter((member) => el.user.find((i) => i.id === member.user.id)) - .map((member) => getMemberLabel(member)) - .join(", "), + + users: members.filter((member) => el.user.find((i) => i.id === member.user.id)), + groupLabels: groups ?.filter(({ group }) => el.group.find((i) => i.id === group.id)) .map(({ group }) => group.name) @@ -212,7 +213,27 @@ export const ApprovalPolicyRow = ({ )}
- {el.userLabels} + {Boolean(el.users.length) && ( +
+ {el.users.map((u, i) => { + return u.user.isOrgMembershipActive ? ( + {getMemberLabel(u)} + ) : ( + + {getMemberLabel(u)}{" "} + + +
+ (Inactive){" "} + +
+
+
+
+ ); + })} +
+ )}
{el.groupLabels} diff --git a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/SecretApprovalRequest/components/SecretApprovalRequestChanges.tsx b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/SecretApprovalRequest/components/SecretApprovalRequestChanges.tsx index 0f2b00299..f7090f43b 100644 --- a/frontend/src/pages/secret-manager/SecretApprovalsPage/components/SecretApprovalRequest/components/SecretApprovalRequestChanges.tsx +++ b/frontend/src/pages/secret-manager/SecretApprovalsPage/components/SecretApprovalRequest/components/SecretApprovalRequestChanges.tsx @@ -8,7 +8,8 @@ import { faCodeBranch, faComment, faFolder, - faHourglass + faHourglass, + faUserSlash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -85,6 +86,7 @@ const getReviewedStatusSymbol = (status?: ApprovalStatus) => { return ; if (status === ApprovalStatus.REJECTED) return ; + return ; }; @@ -162,15 +164,21 @@ export const SecretApprovalRequestChanges = ({ secretApprovalRequestDetails.policy.bypassers.some(({ userId }) => userId === userSession.id); const reviewedUsers = secretApprovalRequestDetails?.reviewers?.reduce< - Record + Record >( (prev, curr) => ({ ...prev, - [curr.userId]: { status: curr.status, comment: curr.comment } + [curr.userId]: { + status: curr.status, + comment: curr.comment, + isOrgMembershipActive: curr.isOrgMembershipActive + } }), {} ); + console.log("reviewedUsers", reviewedUsers); + const handleSecretApprovalStatusUpdate = async (status: ApprovalStatus, comment: string) => { try { await updateSecretApprovalRequestStatus({ @@ -524,7 +532,7 @@ export const SecretApprovalRequestChanges = ({
-
Reviewers
+
Reviewerssss
{secretApprovalRequestDetails?.policy?.approvers .filter( @@ -533,26 +541,50 @@ export const SecretApprovalRequestChanges = ({ ) .map((requiredApprover) => { const reviewer = reviewedUsers?.[requiredApprover.userId]; + const isOrgMembershipActive = requiredApprover.isOrgMembershipActive; + + console.log("isOrgMembershipActive", isOrgMembershipActive); + return (
- -
-
{requiredApprover?.email}
- * -
-
-
+ +
+
{requiredApprover?.email}
+ * +
+
+ + {!isOrgMembershipActive && ( + + + + )} +
+
{reviewer?.comment && ( )} - - {getReviewedStatusSymbol(reviewer?.status)} - +
+ + Status:{" "} + + {reviewer?.status || ApprovalStatus.PENDING} + + + } + > + {getReviewedStatusSymbol(reviewer?.status)} + +
); @@ -578,20 +622,43 @@ export const SecretApprovalRequestChanges = ({ ) .map((reviewer) => { const status = reviewedUsers?.[reviewer.userId].status; + const isOrgMembershipActive = reviewer.isOrgMembershipActive; return (
-
- - {reviewer?.email} +
+ +
+ {reviewer?.email} +
- * + {!isOrgMembershipActive && ( + + + + )}
+
{reviewer.comment && ( - + )} - + + Status:{" "} + {status || ApprovalStatus.PENDING} + + } + > {getReviewedStatusSymbol(status)}