mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Feat: Request access
This commit is contained in:
@@ -1,26 +1,34 @@
|
|||||||
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { accessApprovalKeys } from "./queries";
|
import { accessApprovalKeys } from "./queries";
|
||||||
import { TCreateAccessPolicyDTO, TDeleteSecretPolicyDTO, TUpdateAccessPolicyDTO } from "./types";
|
import {
|
||||||
|
TAccessApproval,
|
||||||
|
TCreateAccessPolicyDTO,
|
||||||
|
TCreateAccessRequestDTO,
|
||||||
|
TDeleteSecretPolicyDTO,
|
||||||
|
TUpdateAccessPolicyDTO
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const useCreateAccessApprovalPolicy = () => {
|
export const useCreateAccessApprovalPolicy = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, TCreateAccessPolicyDTO>({
|
return useMutation<{}, {}, TCreateAccessPolicyDTO>({
|
||||||
mutationFn: async ({ environment, workspaceId, approvals, approvers, name }) => {
|
mutationFn: async ({ environment, projectSlug, approvals, approvers, name, secretPath }) => {
|
||||||
const { data } = await apiRequest.post("/api/v1/access-approvals", {
|
const { data } = await apiRequest.post("/api/v1/access-approvals", {
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
projectSlug,
|
||||||
approvals,
|
approvals,
|
||||||
approvers,
|
approvers,
|
||||||
|
secretPath,
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(workspaceId));
|
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -29,16 +37,17 @@ export const useUpdateAccessApprovalPolicy = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{}, {}, TUpdateAccessPolicyDTO>({
|
return useMutation<{}, {}, TUpdateAccessPolicyDTO>({
|
||||||
mutationFn: async ({ id, approvers, approvals, name }) => {
|
mutationFn: async ({ id, approvers, approvals, name, secretPath }) => {
|
||||||
const { data } = await apiRequest.patch(`/api/v1/access-approvals/${id}`, {
|
const { data } = await apiRequest.patch(`/api/v1/access-approvals/${id}`, {
|
||||||
approvals,
|
approvals,
|
||||||
approvers,
|
approvers,
|
||||||
|
secretPath,
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(workspaceId));
|
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -51,8 +60,66 @@ export const useDeleteAccessApprovalPolicy = () => {
|
|||||||
const { data } = await apiRequest.delete(`/api/v1/access-approvals/${id}`);
|
const { data } = await apiRequest.delete(`/api/v1/access-approvals/${id}`);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess: (_, { workspaceId }) => {
|
onSuccess: (_, { projectSlug }) => {
|
||||||
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(workspaceId));
|
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateAccessRequest = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<{}, {}, TCreateAccessRequestDTO>({
|
||||||
|
mutationFn: async ({ envSlug, projectSlug, secretPath, ...privilege }) => {
|
||||||
|
const { data } = await apiRequest.post<TAccessApproval>(
|
||||||
|
"/api/v1/access-approval-requests",
|
||||||
|
{
|
||||||
|
...privilege,
|
||||||
|
permissions: privilege.permissions ? packRules(privilege.permissions) : undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
envSlug,
|
||||||
|
projectSlug,
|
||||||
|
secretPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { projectSlug }) => {
|
||||||
|
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalRequests(projectSlug));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useReviewAccessRequest = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
requestId: string;
|
||||||
|
status: "approved" | "rejected";
|
||||||
|
projectSlug: string;
|
||||||
|
envSlug?: string;
|
||||||
|
requestedBy?: string;
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
mutationFn: async ({ requestId, status }) => {
|
||||||
|
const { data } = await apiRequest.post(
|
||||||
|
`/api/v1/access-approval-requests/${requestId}/review`,
|
||||||
|
{
|
||||||
|
status
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
onSuccess: (_, { projectSlug, envSlug, requestedBy }) => {
|
||||||
|
queryClient.invalidateQueries(
|
||||||
|
accessApprovalKeys.getAccessApprovalRequests(projectSlug, envSlug, requestedBy)
|
||||||
|
);
|
||||||
|
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalRequestCount(projectSlug));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,30 +1,125 @@
|
|||||||
|
import { PackRule, unpackRules } from "@casl/ability/extra";
|
||||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { TAccessApprovalPolicy, TGetSecretApprovalPoliciesDTO } from "./types";
|
import { TProjectPermission } from "../roles/types";
|
||||||
|
import {
|
||||||
|
TAccessApprovalPolicy,
|
||||||
|
TAccessApprovalRequest,
|
||||||
|
TAccessRequestCount,
|
||||||
|
TGetAccessApprovalRequestsDTO,
|
||||||
|
TGetAccessPolicyApprovalCountDTO
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
export const accessApprovalKeys = {
|
export const accessApprovalKeys = {
|
||||||
getAccessApprovalPolicies: (workspaceId: string) =>
|
getAccessApprovalPolicies: (projectSlug: string) =>
|
||||||
[{ workspaceId }, "access-approval-policies"] as const,
|
[{ projectSlug }, "access-approval-policies"] as const,
|
||||||
getAccessApprovalPolicyOfABoard: (workspaceId: string, environment: string) => [
|
getAccessApprovalPolicyOfABoard: (workspaceId: string, environment: string) =>
|
||||||
{ workspaceId, environment },
|
[{ workspaceId, environment }, "access-approval-policy"] as const,
|
||||||
"access-approval-policy"
|
|
||||||
]
|
getAccessApprovalRequests: (projectSlug: string, envSlug?: string, requestedBy?: string) =>
|
||||||
|
[{ projectSlug, envSlug, requestedBy }, "access-approval-requests"] as const,
|
||||||
|
getAccessApprovalRequestCount: (projectSlug: string) =>
|
||||||
|
[{ projectSlug }, "access-approval-request-count"] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchApprovalPolicies = async (workspaceId: string) => {
|
export const fetchPolicyApprovalCount = async ({
|
||||||
|
projectSlug,
|
||||||
|
envSlug
|
||||||
|
}: TGetAccessPolicyApprovalCountDTO) => {
|
||||||
|
const { data } = await apiRequest.get<{ policyCount: number }>(
|
||||||
|
"/api/v1/access-approvals/policy-count",
|
||||||
|
{
|
||||||
|
params: { projectSlug, envSlug }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data.policyCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetAccessPolicyApprovalCount = ({
|
||||||
|
projectSlug,
|
||||||
|
envSlug,
|
||||||
|
options = {}
|
||||||
|
}: TGetAccessPolicyApprovalCountDTO & {
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
number,
|
||||||
|
unknown,
|
||||||
|
number,
|
||||||
|
ReturnType<typeof accessApprovalKeys.getAccessApprovalPolicies>
|
||||||
|
>;
|
||||||
|
}) =>
|
||||||
|
useQuery({
|
||||||
|
queryFn: () => fetchPolicyApprovalCount({ projectSlug, envSlug }),
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(projectSlug) && (options?.enabled ?? true)
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchApprovalPolicies = async ({ projectSlug }: TGetAccessApprovalRequestsDTO) => {
|
||||||
const { data } = await apiRequest.get<{ approvals: TAccessApprovalPolicy[] }>(
|
const { data } = await apiRequest.get<{ approvals: TAccessApprovalPolicy[] }>(
|
||||||
"/api/v1/access-approvals",
|
"/api/v1/access-approvals",
|
||||||
{ params: { workspaceId } }
|
{ params: { projectSlug } }
|
||||||
);
|
);
|
||||||
return data.approvals;
|
return data.approvals;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetAccessApprovalPolicies = ({
|
const fetchApprovalRequests = async ({
|
||||||
workspaceId,
|
projectSlug,
|
||||||
|
envSlug,
|
||||||
|
authorProjectMembershipId
|
||||||
|
}: TGetAccessApprovalRequestsDTO) => {
|
||||||
|
const { data } = await apiRequest.get<{ requests: TAccessApprovalRequest[] }>(
|
||||||
|
"/api/v1/access-approval-requests",
|
||||||
|
{ params: { projectSlug, envSlug, authorProjectMembershipId } }
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.requests.map((request) => ({
|
||||||
|
...request,
|
||||||
|
|
||||||
|
privilege: request.privilege
|
||||||
|
? {
|
||||||
|
...request.privilege,
|
||||||
|
permissions: unpackRules(
|
||||||
|
request.privilege.permissions as unknown as PackRule<TProjectPermission>[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
permissions: unpackRules(request.permissions as unknown as PackRule<TProjectPermission>[])
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchAccessRequestsCount = async (projectSlug: string) => {
|
||||||
|
const { data } = await apiRequest.get<TAccessRequestCount>(
|
||||||
|
"/api/v1/access-approval-requests/count",
|
||||||
|
{ params: { projectSlug } }
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetAccessRequestsCount = ({
|
||||||
|
projectSlug,
|
||||||
options = {}
|
options = {}
|
||||||
}: TGetSecretApprovalPoliciesDTO & {
|
}: TGetAccessApprovalRequestsDTO & {
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
TAccessRequestCount,
|
||||||
|
unknown,
|
||||||
|
{ pendingCount: number; finalizedCount: number },
|
||||||
|
ReturnType<typeof accessApprovalKeys.getAccessApprovalRequestCount>
|
||||||
|
>;
|
||||||
|
}) =>
|
||||||
|
useQuery({
|
||||||
|
queryKey: accessApprovalKeys.getAccessApprovalRequestCount(projectSlug),
|
||||||
|
queryFn: () => fetchAccessRequestsCount(projectSlug),
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(projectSlug) && (options?.enabled ?? true)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useGetAccessApprovalPolicies = ({
|
||||||
|
projectSlug,
|
||||||
|
envSlug,
|
||||||
|
authorProjectMembershipId,
|
||||||
|
options = {}
|
||||||
|
}: TGetAccessApprovalRequestsDTO & {
|
||||||
options?: UseQueryOptions<
|
options?: UseQueryOptions<
|
||||||
TAccessApprovalPolicy[],
|
TAccessApprovalPolicy[],
|
||||||
unknown,
|
unknown,
|
||||||
@@ -33,8 +128,32 @@ export const useGetAccessApprovalPolicies = ({
|
|||||||
>;
|
>;
|
||||||
}) =>
|
}) =>
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: accessApprovalKeys.getAccessApprovalPolicies(workspaceId),
|
queryKey: accessApprovalKeys.getAccessApprovalPolicies(projectSlug),
|
||||||
queryFn: () => fetchApprovalPolicies(workspaceId),
|
queryFn: () => fetchApprovalPolicies({ projectSlug, envSlug, authorProjectMembershipId }),
|
||||||
...options,
|
...options,
|
||||||
enabled: Boolean(workspaceId) && (options?.enabled ?? true)
|
enabled: Boolean(projectSlug) && (options?.enabled ?? true)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useGetAccessApprovalRequests = ({
|
||||||
|
projectSlug,
|
||||||
|
envSlug,
|
||||||
|
authorProjectMembershipId,
|
||||||
|
options = {}
|
||||||
|
}: TGetAccessApprovalRequestsDTO & {
|
||||||
|
options?: UseQueryOptions<
|
||||||
|
TAccessApprovalRequest[],
|
||||||
|
unknown,
|
||||||
|
TAccessApprovalRequest[],
|
||||||
|
ReturnType<typeof accessApprovalKeys.getAccessApprovalRequests>
|
||||||
|
>;
|
||||||
|
}) =>
|
||||||
|
useQuery({
|
||||||
|
queryKey: accessApprovalKeys.getAccessApprovalRequests(
|
||||||
|
projectSlug,
|
||||||
|
envSlug,
|
||||||
|
authorProjectMembershipId
|
||||||
|
),
|
||||||
|
queryFn: () => fetchApprovalRequests({ projectSlug, envSlug, authorProjectMembershipId }),
|
||||||
|
...options,
|
||||||
|
enabled: Boolean(projectSlug) && (options?.enabled ?? true)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
import { TProjectPermission } from "../roles/types";
|
||||||
import { WorkspaceEnv } from "../workspace/types";
|
import { WorkspaceEnv } from "../workspace/types";
|
||||||
|
|
||||||
export type TAccessApprovalPolicy = {
|
export type TAccessApprovalPolicy = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
approvals: number;
|
approvals: number;
|
||||||
|
secretPath: string;
|
||||||
envId: string;
|
envId: string;
|
||||||
workspace: string;
|
workspace: string;
|
||||||
environment: WorkspaceEnv;
|
environment: WorkspaceEnv;
|
||||||
@@ -11,8 +13,99 @@ export type TAccessApprovalPolicy = {
|
|||||||
approvers: string[];
|
approvers: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecretApprovalPoliciesDTO = {
|
export type TAccessApprovalRequest = {
|
||||||
workspaceId: string;
|
id: string;
|
||||||
|
policyId: string;
|
||||||
|
privilegeId: string | null;
|
||||||
|
requestedBy: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
isTemporary: boolean;
|
||||||
|
temporaryRange: string | null | undefined;
|
||||||
|
|
||||||
|
permissions: TProjectPermission[] | null;
|
||||||
|
|
||||||
|
// Computed
|
||||||
|
environmentName: string;
|
||||||
|
isApproved: boolean;
|
||||||
|
|
||||||
|
privilege: {
|
||||||
|
membershipId: string;
|
||||||
|
isTemporary: boolean;
|
||||||
|
temporaryMode?: string | null;
|
||||||
|
temporaryRange?: string | null;
|
||||||
|
temporaryAccessStartTime?: Date | null;
|
||||||
|
temporaryAccessEndTime?: Date | null;
|
||||||
|
permissions: TProjectPermission[];
|
||||||
|
isApproved: boolean;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
policy: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
approvals: number;
|
||||||
|
approvers: string[];
|
||||||
|
secretPath?: string | null;
|
||||||
|
envId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
reviewers: {
|
||||||
|
member: string;
|
||||||
|
status: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TAccessApproval = {
|
||||||
|
id: string;
|
||||||
|
policyId: string;
|
||||||
|
privilegeId: string;
|
||||||
|
requestedBy: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TAccessRequestCount = {
|
||||||
|
pendingCount: number;
|
||||||
|
finalizedCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TProjectUserPrivilege = {
|
||||||
|
projectMembershipId: string;
|
||||||
|
slug: string;
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
permissions?: TProjectPermission[];
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
isTemporary: true;
|
||||||
|
temporaryMode: string;
|
||||||
|
temporaryRange: string;
|
||||||
|
temporaryAccessStartTime: string;
|
||||||
|
temporaryAccessEndTime?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
isTemporary: false;
|
||||||
|
temporaryMode?: null;
|
||||||
|
temporaryRange?: null;
|
||||||
|
temporaryAccessStartTime?: null;
|
||||||
|
temporaryAccessEndTime?: null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export type TCreateAccessRequestDTO = {
|
||||||
|
envSlug: string;
|
||||||
|
projectSlug: string;
|
||||||
|
secretPath: string;
|
||||||
|
} & Omit<TProjectUserPrivilege, "id" | "createdAt" | "updatedAt" | "slug">;
|
||||||
|
|
||||||
|
export type TGetAccessApprovalRequestsDTO = {
|
||||||
|
projectSlug: string;
|
||||||
|
envSlug?: string;
|
||||||
|
authorProjectMembershipId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetAccessPolicyApprovalCountDTO = {
|
||||||
|
projectSlug: string;
|
||||||
|
envSlug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetSecretApprovalPolicyOfBoardDTO = {
|
export type TGetSecretApprovalPolicyOfBoardDTO = {
|
||||||
@@ -22,24 +115,27 @@ export type TGetSecretApprovalPolicyOfBoardDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateAccessPolicyDTO = {
|
export type TCreateAccessPolicyDTO = {
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
approvers?: string[];
|
approvers?: string[];
|
||||||
approvals?: number;
|
approvals?: number;
|
||||||
|
secretPath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateAccessPolicyDTO = {
|
export type TUpdateAccessPolicyDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
approvers?: string[];
|
approvers?: string[];
|
||||||
|
secretPath?: string;
|
||||||
|
environment?: string;
|
||||||
approvals?: number;
|
approvals?: number;
|
||||||
// for invalidating list
|
// for invalidating list
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteSecretPolicyDTO = {
|
export type TDeleteSecretPolicyDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
// for invalidating list
|
// for invalidating list
|
||||||
workspaceId: string;
|
projectSlug: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user