diff --git a/frontend/src/const/routes.ts b/frontend/src/const/routes.ts
index 32555eea7..ee922f9e6 100644
--- a/frontend/src/const/routes.ts
+++ b/frontend/src/const/routes.ts
@@ -366,10 +366,6 @@ export const ROUTE_PATHS = Object.freeze({
PamSessionByIDPage: setRoute(
"/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId",
"/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId"
- ),
- ApprovalRequestDetailPage: setRoute(
- "/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId",
- "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId"
)
},
Public: {
diff --git a/frontend/src/hooks/api/approvalGrants/index.tsx b/frontend/src/hooks/api/approvalGrants/index.tsx
new file mode 100644
index 000000000..140fb4bac
--- /dev/null
+++ b/frontend/src/hooks/api/approvalGrants/index.tsx
@@ -0,0 +1,10 @@
+export { useRevokeApprovalGrant } from "./mutations";
+export { approvalGrantQuery } from "./queries";
+export {
+ ApprovalGrantStatus,
+ type PamAccessGrantAttributes,
+ type TApprovalGrant,
+ type TGetApprovalGrantByIdDTO,
+ type TListApprovalGrantsDTO,
+ type TRevokeApprovalGrantDTO
+} from "./types";
diff --git a/frontend/src/hooks/api/approvalGrants/mutations.tsx b/frontend/src/hooks/api/approvalGrants/mutations.tsx
new file mode 100644
index 000000000..b28ff45f9
--- /dev/null
+++ b/frontend/src/hooks/api/approvalGrants/mutations.tsx
@@ -0,0 +1,23 @@
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+
+import { apiRequest } from "@app/config/request";
+
+import { approvalGrantQuery } from "./queries";
+import { TApprovalGrant, TRevokeApprovalGrantDTO } from "./types";
+
+export const useRevokeApprovalGrant = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async ({ policyType, grantId, revocationReason }: TRevokeApprovalGrantDTO) => {
+ const { data } = await apiRequest.post<{ grant: TApprovalGrant }>(
+ `/api/v1/approval-policies/${policyType}/grants/${grantId}/revoke`,
+ { revocationReason }
+ );
+ return data.grant;
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: approvalGrantQuery.allKey() });
+ }
+ });
+};
diff --git a/frontend/src/hooks/api/approvalGrants/queries.tsx b/frontend/src/hooks/api/approvalGrants/queries.tsx
new file mode 100644
index 000000000..ca9334ca7
--- /dev/null
+++ b/frontend/src/hooks/api/approvalGrants/queries.tsx
@@ -0,0 +1,37 @@
+import { queryOptions } from "@tanstack/react-query";
+
+import { apiRequest } from "@app/config/request";
+
+import { TApprovalGrant, TGetApprovalGrantByIdDTO, TListApprovalGrantsDTO } from "./types";
+
+export const approvalGrantQuery = {
+ allKey: () => ["approval-grants"] as const,
+ getByIdKey: (params: TGetApprovalGrantByIdDTO) =>
+ [...approvalGrantQuery.allKey(), "by-id", params] as const,
+ listKey: (params: TListApprovalGrantsDTO) =>
+ [...approvalGrantQuery.allKey(), "list", params] as const,
+ getById: (params: TGetApprovalGrantByIdDTO) =>
+ queryOptions({
+ queryKey: approvalGrantQuery.getByIdKey(params),
+ queryFn: async () => {
+ const { data } = await apiRequest.get<{ grant: TApprovalGrant }>(
+ `/api/v1/approval-policies/${params.policyType}/grants/${params.grantId}`
+ );
+ return data.grant;
+ }
+ }),
+ list: (params: TListApprovalGrantsDTO) =>
+ queryOptions({
+ queryKey: approvalGrantQuery.listKey(params),
+ queryFn: async () => {
+ const { data } = await apiRequest.get<{
+ grants: TApprovalGrant[];
+ }>(`/api/v1/approval-policies/${params.policyType}/grants`, {
+ params: {
+ projectId: params.projectId
+ }
+ });
+ return data.grants;
+ }
+ })
+};
diff --git a/frontend/src/hooks/api/approvalGrants/types.ts b/frontend/src/hooks/api/approvalGrants/types.ts
new file mode 100644
index 000000000..852529259
--- /dev/null
+++ b/frontend/src/hooks/api/approvalGrants/types.ts
@@ -0,0 +1,46 @@
+import { ApprovalPolicyType } from "../approvalPolicies";
+
+export enum ApprovalGrantStatus {
+ Active = "active",
+ Expired = "expired",
+ Revoked = "revoked"
+}
+
+// PAM Access Grant Attributes
+export type PamAccessGrantAttributes = {
+ accountPath: string;
+ accessDuration: string;
+};
+
+// Base Grant Type
+export type TApprovalGrant = {
+ id: string;
+ projectId: string;
+ requestId: string | null;
+ granteeUserId: string | null;
+ revokedByUserId: string | null;
+ revocationReason: string | null;
+ status: ApprovalGrantStatus;
+ type: ApprovalPolicyType;
+ attributes: PamAccessGrantAttributes;
+ createdAt: string;
+ expiresAt: string | null;
+ revokedAt: string | null;
+};
+
+// DTOs
+export type TListApprovalGrantsDTO = {
+ policyType: ApprovalPolicyType;
+ projectId: string;
+};
+
+export type TGetApprovalGrantByIdDTO = {
+ policyType: ApprovalPolicyType;
+ grantId: string;
+};
+
+export type TRevokeApprovalGrantDTO = {
+ policyType: ApprovalPolicyType;
+ grantId: string;
+ revocationReason?: string;
+};
diff --git a/frontend/src/hooks/api/approvalPolicies/types.ts b/frontend/src/hooks/api/approvalPolicies/types.ts
index 3843cc583..8fb461814 100644
--- a/frontend/src/hooks/api/approvalPolicies/types.ts
+++ b/frontend/src/hooks/api/approvalPolicies/types.ts
@@ -18,14 +18,13 @@ export type ApprovalPolicyStep = {
};
export type PamAccessPolicyConditions = {
- resourceIds: string[];
accountPaths: string[];
}[];
export type PamAccessPolicyConstraints = {
- requestDurationSeconds: {
- min: number;
- max: number;
+ accessDuration: {
+ min: string;
+ max: string;
};
};
@@ -33,7 +32,7 @@ export type TApprovalPolicy = {
id: string;
projectId: string;
name: string;
- maxRequestTtlSeconds?: number | null;
+ maxRequestTtl?: string | null;
type: ApprovalPolicyType;
conditions: {
version: number;
@@ -52,7 +51,7 @@ export type TCreateApprovalPolicyDTO = {
policyType: ApprovalPolicyType;
projectId: string;
name: string;
- maxRequestTtlSeconds?: number | null;
+ maxRequestTtl?: string | null;
conditions: PamAccessPolicyConditions;
constraints: PamAccessPolicyConstraints;
steps: ApprovalPolicyStep[];
@@ -62,7 +61,7 @@ export type TUpdateApprovalPolicyDTO = {
policyType: ApprovalPolicyType;
policyId: string;
name?: string;
- maxRequestTtlSeconds?: number | null;
+ maxRequestTtl?: string | null;
conditions?: PamAccessPolicyConditions;
constraints?: PamAccessPolicyConstraints;
steps?: ApprovalPolicyStep[];
diff --git a/frontend/src/hooks/api/approvalRequests/index.tsx b/frontend/src/hooks/api/approvalRequests/index.tsx
index c6db021b0..2b146836a 100644
--- a/frontend/src/hooks/api/approvalRequests/index.tsx
+++ b/frontend/src/hooks/api/approvalRequests/index.tsx
@@ -1,5 +1,6 @@
export {
useApproveApprovalRequest,
+ useCancelApprovalRequest,
useCreateApprovalRequest,
useRejectApprovalRequest
} from "./mutations";
diff --git a/frontend/src/hooks/api/approvalRequests/mutations.tsx b/frontend/src/hooks/api/approvalRequests/mutations.tsx
index 3afcd290a..81e14b311 100644
--- a/frontend/src/hooks/api/approvalRequests/mutations.tsx
+++ b/frontend/src/hooks/api/approvalRequests/mutations.tsx
@@ -6,6 +6,7 @@ import { approvalRequestQuery } from "./queries";
import {
TApprovalRequest,
TApproveApprovalRequestDTO,
+ TCancelApprovalRequestDTO,
TCreateApprovalRequestDTO,
TRejectApprovalRequestDTO
} from "./types";
@@ -57,3 +58,18 @@ export const useRejectApprovalRequest = () => {
}
});
};
+
+export const useCancelApprovalRequest = () => {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: async ({ policyType, requestId }: TCancelApprovalRequestDTO) => {
+ const { data } = await apiRequest.post<{ request: TApprovalRequest }>(
+ `/api/v1/approval-policies/${policyType}/requests/${requestId}/cancel`
+ );
+ return data.request;
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: approvalRequestQuery.allKey() });
+ }
+ });
+};
diff --git a/frontend/src/hooks/api/approvalRequests/types.ts b/frontend/src/hooks/api/approvalRequests/types.ts
index d57728353..f59214b3e 100644
--- a/frontend/src/hooks/api/approvalRequests/types.ts
+++ b/frontend/src/hooks/api/approvalRequests/types.ts
@@ -49,9 +49,8 @@ export type ApprovalRequestStep = {
};
export type PamAccessRequestData = {
- resourceId: string;
accountPath: string;
- requestDurationSeconds: number;
+ accessDuration: number;
};
export type TApprovalRequest = {
@@ -78,7 +77,7 @@ export type TCreateApprovalRequestDTO = {
policyType: ApprovalPolicyType;
projectId: string;
justification?: string | null;
- expiresAt?: Date | null;
+ requestDuration?: string | null;
requestData: PamAccessRequestData;
};
@@ -103,3 +102,8 @@ export type TRejectApprovalRequestDTO = {
requestId: string;
comment?: string;
};
+
+export type TCancelApprovalRequestDTO = {
+ policyType: ApprovalPolicyType;
+ requestId: string;
+};
diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx
index 33eacd18d..acbf1d9f5 100644
--- a/frontend/src/hooks/api/index.tsx
+++ b/frontend/src/hooks/api/index.tsx
@@ -1,6 +1,9 @@
export * from "./accessApproval";
export * from "./admin";
export * from "./apiKeys";
+export * from "./approvalGrants";
+export * from "./approvalPolicies";
+export * from "./approvalRequests";
export * from "./assumePrivileges";
export * from "./auditLogs";
export * from "./auditLogStreams";
diff --git a/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx b/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx
index eb95eb2d0..2d12b8c68 100644
--- a/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx
+++ b/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx
@@ -2,7 +2,7 @@ import { Helmet } from "react-helmet";
import { faBan, faChevronLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useQuery } from "@tanstack/react-query";
-import { Link, useNavigate, useParams } from "@tanstack/react-router";
+import { Link, useParams } from "@tanstack/react-router";
import { ContentLoader, EmptyState, PageHeader } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
@@ -61,7 +61,7 @@ const PageContent = () => {
diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx index aec25cb60..85eb9f1d9 100644 --- a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx @@ -63,16 +63,6 @@ const getStatusBadgeColor = (status: ApprovalRequestStatus) => { } }; -const formatDuration = (seconds: number) => { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - - if (hours > 0) { - return `${hours}h${minutes > 0 ? ` ${minutes}m` : ""}`; - } - return `${minutes}m`; -}; - const checkIfUserNeedsToApprove = ( request: TApprovalRequest, userId: string, @@ -131,7 +121,6 @@ export const RequestsTable = () => { request.requesterName?.toLowerCase().includes(search.toLowerCase()) || request.requesterEmail?.toLowerCase().includes(search.toLowerCase()) || request.justification?.toLowerCase().includes(search.toLowerCase()) || - request.requestData.requestData.resourceId.toLowerCase().includes(search.toLowerCase()) || request.requestData.requestData.accountPath.toLowerCase().includes(search.toLowerCase()) ); } @@ -272,7 +261,7 @@ export const RequestsTable = () => { {!isRequestsLoading && paginatedRequests.map((request) => { const needsApproval = checkIfUserNeedsToApprove(request, userId, userGroups); - const { accountPath, requestDurationSeconds } = request.requestData.requestData; + const { accountPath, accessDuration } = request.requestData.requestData; return (
- {step.resourceIds.join(", ")} -
-