Fix merge conflicts

This commit is contained in:
Tuan Dang
2024-05-07 14:28:41 -07:00
100 changed files with 6475 additions and 531 deletions
@@ -0,0 +1,13 @@
export {
useCreateAccessApprovalPolicy,
useCreateAccessRequest,
useDeleteAccessApprovalPolicy,
useReviewAccessRequest,
useUpdateAccessApprovalPolicy
} from "./mutation";
export {
useGetAccessApprovalPolicies,
useGetAccessApprovalRequests,
useGetAccessPolicyApprovalCount,
useGetAccessRequestsCount
} from "./queries";
@@ -0,0 +1,123 @@
import { packRules } from "@casl/ability/extra";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { accessApprovalKeys } from "./queries";
import {
TAccessApproval,
TCreateAccessPolicyDTO,
TCreateAccessRequestDTO,
TDeleteSecretPolicyDTO,
TUpdateAccessPolicyDTO
} from "./types";
export const useCreateAccessApprovalPolicy = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TCreateAccessPolicyDTO>({
mutationFn: async ({ environment, projectSlug, approvals, approvers, name, secretPath }) => {
const { data } = await apiRequest.post("/api/v1/access-approvals/policies", {
environment,
projectSlug,
approvals,
approvers,
secretPath,
name
});
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
}
});
};
export const useUpdateAccessApprovalPolicy = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TUpdateAccessPolicyDTO>({
mutationFn: async ({ id, approvers, approvals, name, secretPath }) => {
const { data } = await apiRequest.patch(`/api/v1/access-approvals/policies/${id}`, {
approvals,
approvers,
secretPath,
name
});
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
}
});
};
export const useDeleteAccessApprovalPolicy = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TDeleteSecretPolicyDTO>({
mutationFn: async ({ id }) => {
const { data } = await apiRequest.delete(`/api/v1/access-approvals/policies/${id}`);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalPolicies(projectSlug));
}
});
};
export const useCreateAccessRequest = () => {
const queryClient = useQueryClient();
return useMutation<{}, {}, TCreateAccessRequestDTO>({
mutationFn: async ({ projectSlug, ...request }) => {
const { data } = await apiRequest.post<TAccessApproval>(
"/api/v1/access-approvals/requests",
{
...request,
permissions: request.permissions ? packRules(request.permissions) : undefined
},
{
params: {
projectSlug
}
}
);
return data;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalRequestCount(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-approvals/requests/${requestId}/review`,
{
status
}
);
return data;
},
onSuccess: (_, { projectSlug, envSlug, requestedBy }) => {
queryClient.invalidateQueries(
accessApprovalKeys.getAccessApprovalRequests(projectSlug, envSlug, requestedBy)
);
queryClient.invalidateQueries(accessApprovalKeys.getAccessApprovalRequestCount(projectSlug));
}
});
};
@@ -0,0 +1,159 @@
import { PackRule, unpackRules } from "@casl/ability/extra";
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { TProjectPermission } from "../roles/types";
import {
TAccessApprovalPolicy,
TAccessApprovalRequest,
TAccessRequestCount,
TGetAccessApprovalRequestsDTO,
TGetAccessPolicyApprovalCountDTO
} from "./types";
export const accessApprovalKeys = {
getAccessApprovalPolicies: (projectSlug: string) =>
[{ projectSlug }, "access-approval-policies"] as const,
getAccessApprovalPolicyOfABoard: (workspaceId: string, environment: string) =>
[{ workspaceId, environment }, "access-approval-policy"] as const,
getAccessApprovalRequests: (projectSlug: string, envSlug?: string, requestedBy?: string) =>
[{ projectSlug, envSlug, requestedBy }, "access-approvals-requests"] as const,
getAccessApprovalRequestCount: (projectSlug: string) =>
[{ projectSlug }, "access-approval-request-count"] as const
};
export const fetchPolicyApprovalCount = async ({
projectSlug,
envSlug
}: TGetAccessPolicyApprovalCountDTO) => {
const { data } = await apiRequest.get<{ count: number }>(
"/api/v1/access-approvals/policies/count",
{
params: { projectSlug, envSlug }
}
);
return data.count;
};
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[] }>(
"/api/v1/access-approvals/policies",
{ params: { projectSlug } }
);
return data.approvals;
};
const fetchApprovalRequests = async ({
projectSlug,
envSlug,
authorProjectMembershipId
}: TGetAccessApprovalRequestsDTO) => {
const { data } = await apiRequest.get<{ requests: TAccessApprovalRequest[] }>(
"/api/v1/access-approvals/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-approvals/requests/count",
{ params: { projectSlug } }
);
return data;
};
export const useGetAccessRequestsCount = ({
projectSlug,
options = {}
}: 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<
TAccessApprovalPolicy[],
unknown,
TAccessApprovalPolicy[],
ReturnType<typeof accessApprovalKeys.getAccessApprovalPolicies>
>;
}) =>
useQuery({
queryKey: accessApprovalKeys.getAccessApprovalPolicies(projectSlug),
queryFn: () => fetchApprovalPolicies({ projectSlug, envSlug, authorProjectMembershipId }),
...options,
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)
});
@@ -0,0 +1,139 @@
import { TProjectPermission } from "../roles/types";
import { WorkspaceEnv } from "../workspace/types";
export type TAccessApprovalPolicy = {
id: string;
name: string;
approvals: number;
secretPath: string;
envId: string;
workspace: string;
environment: WorkspaceEnv;
projectId: string;
approvers: string[];
};
export type TAccessApprovalRequest = {
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 = {
projectSlug: string;
} & Omit<TProjectUserPrivilege, "id" | "createdAt" | "updatedAt" | "slug" | "projectMembershipId">;
export type TGetAccessApprovalRequestsDTO = {
projectSlug: string;
envSlug?: string;
authorProjectMembershipId?: string;
};
export type TGetAccessPolicyApprovalCountDTO = {
projectSlug: string;
envSlug: string;
};
export type TGetSecretApprovalPolicyOfBoardDTO = {
workspaceId: string;
environment: string;
secretPath: string;
};
export type TCreateAccessPolicyDTO = {
projectSlug: string;
name?: string;
environment: string;
approvers?: string[];
approvals?: number;
secretPath?: string;
};
export type TUpdateAccessPolicyDTO = {
id: string;
name?: string;
approvers?: string[];
secretPath?: string;
environment?: string;
approvals?: number;
// for invalidating list
projectSlug: string;
};
export type TDeleteSecretPolicyDTO = {
id: string;
// for invalidating list
projectSlug: string;
};
@@ -2,5 +2,6 @@ import { IdentityAuthMethod } from "./enums";
export const identityAuthToNameMap: { [I in IdentityAuthMethod]: string } = {
[IdentityAuthMethod.UNIVERSAL_AUTH]: "Universal Auth",
[IdentityAuthMethod.GCP_IAM_AUTH]: "GCP IAM Auth"
[IdentityAuthMethod.GCP_IAM_AUTH]: "GCP IAM Auth",
[IdentityAuthMethod.AWS_IAM_AUTH]: "AWS IAM Auth"
};
+2 -1
View File
@@ -1,4 +1,5 @@
export enum IdentityAuthMethod {
UNIVERSAL_AUTH = "universal-auth",
GCP_IAM_AUTH = "gcp-iam-auth"
GCP_IAM_AUTH = "gcp-iam-auth",
AWS_IAM_AUTH = "aws-iam-auth"
}
+7 -2
View File
@@ -1,6 +1,7 @@
export { identityAuthToNameMap } from "./constants";
export { IdentityAuthMethod } from "./enums";
export {
useAddIdentityAwsIamAuth,
useAddIdentityGcpIamAuth,
useAddIdentityUniversalAuth,
useCreateIdentity,
@@ -8,9 +9,13 @@ export {
useDeleteIdentity,
useRevokeIdentityUniversalAuthClientSecret,
useUpdateIdentity,
useUpdateIdentityAwsIamAuth,
useUpdateIdentityGcpIamAuth,
useUpdateIdentityUniversalAuth} from "./mutations";
useUpdateIdentityUniversalAuth
} from "./mutations";
export {
useGetIdentityAwsIamAuth,
useGetIdentityGcpIamAuth,
useGetIdentityUniversalAuth,
useGetIdentityUniversalAuthClientSecrets} from "./queries";
useGetIdentityUniversalAuthClientSecrets
} from "./queries";
@@ -5,6 +5,7 @@ import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries";
import { identitiesKeys } from "./queries";
import {
AddIdentityAwsIamAuthDTO,
AddIdentityGcpIamAuthDTO,
AddIdentityUniversalAuthDTO,
ClientSecretData,
@@ -14,11 +15,14 @@ import {
DeleteIdentityDTO,
DeleteIdentityUniversalAuthClientSecretDTO,
Identity,
IdentityAwsIamAuth,
IdentityGcpIamAuth,
IdentityUniversalAuth,
UpdateIdentityAwsIamAuthDTO,
UpdateIdentityDTO,
UpdateIdentityGcpIamAuthDTO,
UpdateIdentityUniversalAuthDTO} from "./types";
UpdateIdentityUniversalAuthDTO
} from "./types";
export const useCreateIdentity = () => {
const queryClient = useQueryClient();
@@ -206,6 +210,42 @@ export const useAddIdentityGcpIamAuth = () => {
});
};
export const useAddIdentityAwsIamAuth = () => {
const queryClient = useQueryClient();
return useMutation<IdentityAwsIamAuth, {}, AddIdentityAwsIamAuthDTO>({
mutationFn: async ({
identityId,
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
accessTokenTTL,
accessTokenMaxTTL,
accessTokenNumUsesLimit,
accessTokenTrustedIps
}) => {
const {
data: { identityAwsIamAuth }
} = await apiRequest.post<{ identityAwsIamAuth: IdentityAwsIamAuth }>(
`/api/v1/auth/aws-iam-auth/identities/${identityId}`,
{
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
accessTokenTTL,
accessTokenMaxTTL,
accessTokenNumUsesLimit,
accessTokenTrustedIps
}
);
return identityAwsIamAuth;
},
onSuccess: (_, { organizationId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
}
});
};
export const useUpdateIdentityGcpIamAuth = () => {
const queryClient = useQueryClient();
return useMutation<IdentityGcpIamAuth, {}, UpdateIdentityGcpIamAuthDTO>({
@@ -231,6 +271,7 @@ export const useUpdateIdentityGcpIamAuth = () => {
accessTokenTrustedIps
}
);
return identityGcpIamAuth;
},
onSuccess: (_, { organizationId }) => {
@@ -238,3 +279,39 @@ export const useUpdateIdentityGcpIamAuth = () => {
}
});
};
export const useUpdateIdentityAwsIamAuth = () => {
const queryClient = useQueryClient();
return useMutation<IdentityAwsIamAuth, {}, UpdateIdentityAwsIamAuthDTO>({
mutationFn: async ({
identityId,
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
accessTokenTTL,
accessTokenMaxTTL,
accessTokenNumUsesLimit,
accessTokenTrustedIps
}) => {
const {
data: { identityAwsIamAuth }
} = await apiRequest.patch<{ identityAwsIamAuth: IdentityAwsIamAuth }>(
`/api/v1/auth/aws-iam-auth/identities/${identityId}`,
{
stsEndpoint,
allowedPrincipalArns,
allowedAccountIds,
accessTokenTTL,
accessTokenMaxTTL,
accessTokenNumUsesLimit,
accessTokenTrustedIps
}
);
return identityAwsIamAuth;
},
onSuccess: (_, { organizationId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
}
});
};
+25 -8
View File
@@ -2,28 +2,32 @@ import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { ClientSecretData, IdentityGcpIamAuth,IdentityUniversalAuth } from "./types";
import {
ClientSecretData,
IdentityAwsIamAuth,
IdentityGcpIamAuth,
IdentityUniversalAuth
} from "./types";
export const identitiesKeys = {
getIdentityUniversalAuth: (identityId: string) =>
[{ identityId }, "identity-universal-auth"] as const,
getIdentityUniversalAuthClientSecrets: (identityId: string) =>
[{ identityId }, "identity-universal-auth-client-secrets"] as const,
getIdentityGcpIamAuth: (identityId: string) => [{ identityId }, "identity-gcp-iam-auth"] as const
getIdentityGcpIamAuth: (identityId: string) => [{ identityId }, "identity-gcp-iam-auth"] as const,
getIdentityAwsIamAuth: (identityId: string) => [{ identityId }, "identity-aws-iam-auth"] as const
};
export const useGetIdentityUniversalAuth = (identityId: string) => {
return useQuery({
enabled: Boolean(identityId),
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId),
queryFn: async () => {
if (identityId === "") throw new Error("Identity ID is required");
const {
data: { identityUniversalAuth }
} = await apiRequest.get<{ identityUniversalAuth: IdentityUniversalAuth }>(
`/api/v1/auth/universal-auth/identities/${identityId}`
);
return identityUniversalAuth;
}
});
@@ -31,16 +35,14 @@ export const useGetIdentityUniversalAuth = (identityId: string) => {
export const useGetIdentityUniversalAuthClientSecrets = (identityId: string) => {
return useQuery({
enabled: Boolean(identityId),
queryKey: identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId),
queryFn: async () => {
if (identityId === "") return [];
const {
data: { clientSecretData }
} = await apiRequest.get<{ clientSecretData: ClientSecretData[] }>(
`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets`
);
return clientSecretData;
}
});
@@ -60,3 +62,18 @@ export const useGetIdentityGcpIamAuth = (identityId: string) => {
}
});
};
export const useGetIdentityAwsIamAuth = (identityId: string) => {
return useQuery({
enabled: Boolean(identityId),
queryKey: identitiesKeys.getIdentityAwsIamAuth(identityId),
queryFn: async () => {
const {
data: { identityAwsIamAuth }
} = await apiRequest.get<{ identityAwsIamAuth: IdentityAwsIamAuth }>(
`/api/v1/auth/aws-iam-auth/identities/${identityId}`
);
return identityAwsIamAuth;
}
});
};
@@ -149,6 +149,45 @@ export type UpdateIdentityGcpIamAuthDTO = {
}[];
};
export type IdentityAwsIamAuth = {
identityId: string;
stsEndpoint: string;
allowedPrincipalArns: string;
allowedAccountIds: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: IdentityTrustedIp[];
};
export type AddIdentityAwsIamAuthDTO = {
organizationId: string;
identityId: string;
stsEndpoint: string;
allowedPrincipalArns: string;
allowedAccountIds: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: {
ipAddress: string;
}[];
};
export type UpdateIdentityAwsIamAuthDTO = {
organizationId: string;
identityId: string;
stsEndpoint?: string;
allowedPrincipalArns?: string;
allowedAccountIds?: string;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: {
ipAddress: string;
}[];
};
export type CreateIdentityUniversalAuthClientSecretDTO = {
identityId: string;
description?: string;
+1
View File
@@ -1,3 +1,4 @@
export * from "./accessApproval";
export * from "./admin";
export * from "./apiKeys";
export * from "./auditLogs";
@@ -94,7 +94,21 @@ export const useGetFoldersByEnv = ({
[(folders || []).map((folder) => folder.data)]
);
return { folders, folderNames, isFolderPresentInEnv };
const getFolderByNameAndEnv = useCallback(
(name: string, env: string) => {
const selectedEnvIndex = environments.indexOf(env);
if (selectedEnvIndex !== -1) {
return folders?.[selectedEnvIndex]?.data?.find(
({ name: folderName }) => folderName === name
);
}
return undefined;
},
[(folders || []).map((folder) => folder.data)]
);
return { folders, folderNames, isFolderPresentInEnv, getFolderByNameAndEnv };
};
export const useCreateFolder = () => {
+9 -8
View File
@@ -1,5 +1,6 @@
import { ZodIssue } from "zod";
export type { TAccessApprovalPolicy } from "./accessApproval/types";
export type { TAuditLogStream } from "./auditLogStreams/types";
export type { GetAuthTokenAPI } from "./auth/types";
export type { IncidentContact } from "./incidentContacts/types";
@@ -49,13 +50,13 @@ export enum ApiErrorTypes {
export type TApiErrors =
| {
error: ApiErrorTypes.ValidationError;
message: ZodIssue[];
statusCode: 403;
}
error: ApiErrorTypes.ValidationError;
message: ZodIssue[];
statusCode: 403;
}
| { error: ApiErrorTypes.ForbiddenError; message: string; statusCode: 401 }
| {
statusCode: 400;
message: string;
error: ApiErrorTypes.BadRequestError;
};
statusCode: 400;
message: string;
error: ApiErrorTypes.BadRequestError;
};