mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 10:28:21 +00:00
Merge branch 'main' of https://github.com/Infisical/infisical into feat/adds-GETtokenAuthTokenById-api-endpoint
This commit is contained in:
@@ -67,7 +67,8 @@ export const createNotification = (
|
||||
...toastProps,
|
||||
autoClose: toastProps.autoClose || 15000,
|
||||
theme: "dark",
|
||||
type: myProps?.type || "info"
|
||||
type: myProps?.type || "info",
|
||||
className: `pointer-events-auto ${toastProps.className}`
|
||||
});
|
||||
|
||||
export const NotificationContainer = () => (
|
||||
|
||||
@@ -9,7 +9,7 @@ import { cva, type VariantProps } from "cva";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
const alertVariants = cva(
|
||||
"w-full bg-mineshaft-800 rounded-lg border border-bunker-400 px-4 py-3 text-sm flex items-center gap-x-4",
|
||||
"w-full bg-mineshaft-800 rounded-lg border border-bunker-400 px-4 py-3 text-sm flex items-center gap-x-3",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -28,6 +28,7 @@ type AlertProps = {
|
||||
title?: string;
|
||||
hideTitle?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
iconClassName?: string;
|
||||
};
|
||||
|
||||
const variantTitleMap = {
|
||||
@@ -45,36 +46,41 @@ const variantIconMap = {
|
||||
const Alert = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> & AlertProps
|
||||
>(({ className, variant, title, icon, hideTitle = false, children, ...props }, ref) => {
|
||||
const defaultTitle = title ?? variantTitleMap[variant ?? "default"];
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={twMerge(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
>
|
||||
<div>
|
||||
{typeof icon !== "undefined" ? (
|
||||
<>{icon} </>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
className="text-lg text-primary"
|
||||
icon={variantIconMap[variant ?? "default"]}
|
||||
/>
|
||||
)}
|
||||
>(
|
||||
(
|
||||
{ className, variant, title, icon, hideTitle = false, children, iconClassName, ...props },
|
||||
ref
|
||||
) => {
|
||||
const defaultTitle = title ?? variantTitleMap[variant ?? "default"];
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={twMerge(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
>
|
||||
<div>
|
||||
{typeof icon !== "undefined" ? (
|
||||
<>{icon} </>
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
className={twMerge("text-lg text-primary", iconClassName)}
|
||||
icon={variantIconMap[variant ?? "default"]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-1">
|
||||
{hideTitle ? null : (
|
||||
<h5 className="leading-6 font-medium tracking-tight" {...props}>
|
||||
{defaultTitle}
|
||||
</h5>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-1">
|
||||
{hideTitle ? null : (
|
||||
<h5 className="leading-6 font-medium tracking-tight" {...props}>
|
||||
{defaultTitle}
|
||||
</h5>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
}
|
||||
);
|
||||
Alert.displayName = "Alert";
|
||||
|
||||
const AlertDescription = forwardRef<
|
||||
|
||||
@@ -55,6 +55,19 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
className={twMerge(drawerContentVariation({ direction, className }))}
|
||||
onPointerDownOutside={(e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const toastElement = target.closest('[class*="Toastify"]');
|
||||
if (toastElement) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
props.onPointerDownOutside?.(e);
|
||||
}}
|
||||
>
|
||||
<Card isRounded={false} className="dark h-full w-full">
|
||||
{title && (
|
||||
|
||||
@@ -2,10 +2,15 @@ import { forwardRef, TextareaHTMLAttributes, useCallback, useMemo, useRef, useSt
|
||||
import { faFolder, faKey, faLayerGroup, faSearch } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
import { useProject } from "@app/context";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { useProject, useProjectPermission } from "@app/context";
|
||||
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { useDebounce, useToggle } from "@app/hooks";
|
||||
import { useGetProjectFolders, useGetProjectSecrets } from "@app/hooks/api";
|
||||
import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission";
|
||||
|
||||
import { SecretInput } from "../SecretInput";
|
||||
|
||||
@@ -80,6 +85,8 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
) => {
|
||||
const { currentProject } = useProject();
|
||||
const projectId = currentProject?.id || "";
|
||||
const navigate = useNavigate({ from: ROUTE_PATHS.SecretManager.SecretDashboardPage.path });
|
||||
const { permission } = useProjectPermission();
|
||||
|
||||
const [debouncedValue] = useDebounce(value, 100);
|
||||
|
||||
@@ -307,6 +314,120 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleClickSegment = useCallback(
|
||||
(segment: string, allSegments: string[]) => {
|
||||
if (!projectId) {
|
||||
createNotification({
|
||||
text: "Project ID is not set",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (allSegments.length === 0) {
|
||||
createNotification({
|
||||
text: "Invalid secret reference",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (allSegments.length === 1) {
|
||||
const canReadSecretValue = hasSecretReadValueOrDescribePermission(
|
||||
permission,
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
{
|
||||
environment: propEnvironment ?? "*",
|
||||
secretPath: propSecretPath ?? "/",
|
||||
secretName: segment,
|
||||
secretTags: ["*"]
|
||||
}
|
||||
);
|
||||
|
||||
if (!canReadSecretValue) {
|
||||
createNotification({
|
||||
text: "You do not have permission to access this secret",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigate({
|
||||
search: (prev) => ({
|
||||
...prev,
|
||||
search: segment,
|
||||
filterBy: "secret",
|
||||
tags: ""
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const environmentSlug = allSegments[0];
|
||||
const secretName = allSegments[allSegments.length - 1];
|
||||
let folderPath = "/";
|
||||
|
||||
if (allSegments.length > 2) {
|
||||
const pathSegments = allSegments.slice(1, -1);
|
||||
for (let i = 0; i < pathSegments.length; i += 1) {
|
||||
if (!pathSegments[i]) {
|
||||
createNotification({
|
||||
text: "Invalid secret reference",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const pathSegment = pathSegments[i];
|
||||
folderPath += `${pathSegment}`;
|
||||
if (pathSegment === segment) {
|
||||
folderPath += "/";
|
||||
break;
|
||||
}
|
||||
folderPath += "/";
|
||||
}
|
||||
}
|
||||
|
||||
// Only validate secret permission, users can always view environments and folders
|
||||
if (segment === secretName) {
|
||||
const canReadSecretValue = hasSecretReadValueOrDescribePermission(
|
||||
permission,
|
||||
ProjectPermissionSecretActions.ReadValue,
|
||||
{
|
||||
environment: environmentSlug,
|
||||
secretPath: folderPath,
|
||||
secretName,
|
||||
secretTags: ["*"]
|
||||
}
|
||||
);
|
||||
|
||||
if (!canReadSecretValue) {
|
||||
createNotification({
|
||||
text: "You do not have permission to access this secret",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
navigate({
|
||||
to: ROUTE_PATHS.SecretManager.SecretDashboardPage.path,
|
||||
params: {
|
||||
projectId,
|
||||
envSlug: environmentSlug
|
||||
},
|
||||
search: (prev) => ({
|
||||
...prev,
|
||||
secretPath: segment === environmentSlug ? "/" : folderPath,
|
||||
search: segment === secretName ? secretName : prev.search,
|
||||
filterBy: segment === secretName ? "secret" : prev.filterBy,
|
||||
tags: ""
|
||||
})
|
||||
});
|
||||
},
|
||||
[navigate, projectId, permission, propEnvironment, propSecretPath]
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover.Root open={isPopupOpen} onOpenChange={handlePopUpOpen}>
|
||||
<Popover.Trigger asChild>
|
||||
@@ -329,6 +450,7 @@ export const InfisicalSecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
}}
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
containerClassName={containerClassName}
|
||||
onClickSegment={handleClickSegment}
|
||||
/>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content
|
||||
|
||||
@@ -15,6 +15,7 @@ export type ModalContentProps = Omit<DialogPrimitive.DialogContentProps, "title"
|
||||
onClose?: () => void;
|
||||
overlayClassName?: string;
|
||||
showCloseButton?: boolean;
|
||||
closeOnOutsideClick?: boolean;
|
||||
};
|
||||
|
||||
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
@@ -29,6 +30,7 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
bodyClassName,
|
||||
onClose,
|
||||
showCloseButton = true,
|
||||
closeOnOutsideClick = true,
|
||||
...props
|
||||
},
|
||||
forwardedRef
|
||||
@@ -38,7 +40,23 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
className={twMerge("animate-fade-in fixed inset-0 z-30 h-full w-full", overlayClassName)}
|
||||
style={{ backgroundColor: "rgba(0, 0, 0, 0.7)" }}
|
||||
/>
|
||||
<DialogPrimitive.Content {...props} ref={forwardedRef}>
|
||||
<DialogPrimitive.Content
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
onPointerDownOutside={(e) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const toastElement = target.closest('[class*="Toastify"]');
|
||||
if (toastElement) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeOnOutsideClick && onClose) {
|
||||
onClose();
|
||||
}
|
||||
props.onPointerDownOutside?.(e);
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
isRounded
|
||||
className={twMerge(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable react/no-danger */
|
||||
import { forwardRef, TextareaHTMLAttributes } from "react";
|
||||
import { forwardRef, TextareaHTMLAttributes, useEffect, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { HIDDEN_SECRET_VALUE } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem";
|
||||
|
||||
const REGEX = /(\${([a-zA-Z0-9-_.]+)})/g;
|
||||
const REGEX = /(\${([a-zA-Z0-9-_. ]+)})/g;
|
||||
|
||||
const syntaxHighlight = (
|
||||
content?: string | null,
|
||||
@@ -13,6 +13,10 @@ const syntaxHighlight = (
|
||||
isImport?: boolean,
|
||||
isLoadingValue?: boolean,
|
||||
isErrorLoadingValue?: boolean,
|
||||
onHoverPart?: (part: string) => void,
|
||||
hoveredPart?: string,
|
||||
isCmdOrCtrlPressed?: boolean,
|
||||
onClickSegment?: (segment: string, allSegments: string[]) => void,
|
||||
placeholder?: string
|
||||
) => {
|
||||
if (isLoadingValue) return HIDDEN_SECRET_VALUE;
|
||||
@@ -29,10 +33,57 @@ const syntaxHighlight = (
|
||||
const isInterpolationSyntax = el.startsWith("${") && el.endsWith("}");
|
||||
if (isInterpolationSyntax) {
|
||||
skipNext = true;
|
||||
const part = el;
|
||||
const innerContent = el.slice(2, -1); // Remove ${ and }
|
||||
const parts = innerContent.split(".");
|
||||
|
||||
return (
|
||||
<span className="ph-no-capture text-yellow" key={`secret-value-${i + 1}`}>
|
||||
<span className="ph-no-capture relative z-10 text-yellow" key={`secret-value-${i + 1}`}>
|
||||
${
|
||||
<span className="ph-no-capture text-yellow-200/80">{el.slice(2, -1)}</span>
|
||||
{parts.map((segment, segmentIndex) => {
|
||||
const segmentKey = `${part}-segment-${segmentIndex}`;
|
||||
const isHovered = hoveredPart === segmentKey;
|
||||
const shouldShowHoverStyle = isHovered && isCmdOrCtrlPressed;
|
||||
|
||||
return (
|
||||
<span key={segmentKey}>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={isCmdOrCtrlPressed ? 0 : -1}
|
||||
className={`ph-no-capture text-yellow-200/80 ${
|
||||
isCmdOrCtrlPressed ? "pointer-events-auto" : "pointer-events-none"
|
||||
} ${shouldShowHoverStyle ? "cursor-pointer underline decoration-yellow-400" : ""}`}
|
||||
onMouseEnter={() => onHoverPart?.(segmentKey)}
|
||||
onMouseLeave={() => onHoverPart?.("")}
|
||||
onMouseDown={(e) => {
|
||||
if (isCmdOrCtrlPressed) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (isCmdOrCtrlPressed) {
|
||||
e.preventDefault();
|
||||
onClickSegment?.(segment, parts);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (isCmdOrCtrlPressed && (e.key === "Enter" || e.key === " ")) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onClickSegment?.(segment, parts);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{segment}
|
||||
</span>
|
||||
{segmentIndex < parts.length - 1 && (
|
||||
<span className="ph-no-capture pointer-events-none text-yellow-200/80">.</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
}
|
||||
</span>
|
||||
);
|
||||
@@ -62,6 +113,7 @@ type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
||||
canEditButNotView?: boolean;
|
||||
isLoadingValue?: boolean;
|
||||
isErrorLoadingValue?: boolean;
|
||||
onClickSegment?: (segment: string, allSegments: string[]) => void;
|
||||
};
|
||||
|
||||
const commonClassName = "font-mono text-sm caret-white border-none outline-hidden w-full break-all";
|
||||
@@ -81,12 +133,43 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
canEditButNotView,
|
||||
isLoadingValue,
|
||||
isErrorLoadingValue,
|
||||
onClickSegment,
|
||||
placeholder,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [isSecretFocused, setIsSecretFocused] = useToggle();
|
||||
const [hoveredPart, setHoveredPart] = useState<string | undefined>();
|
||||
const [isCmdOrCtrlPressed, setIsCmdOrCtrlPressed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
setIsCmdOrCtrlPressed(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: KeyboardEvent) => {
|
||||
if (!e.metaKey && !e.ctrlKey) {
|
||||
setIsCmdOrCtrlPressed(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsCmdOrCtrlPressed(false);
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
window.addEventListener("blur", handleBlur);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
window.removeEventListener("blur", handleBlur);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -94,7 +177,7 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
style={{ maxHeight: `${21 * 7}px` }}
|
||||
>
|
||||
<div className="relative overflow-hidden">
|
||||
<pre aria-hidden className="m-0">
|
||||
<pre aria-hidden className="pointer-events-none relative z-10 m-0">
|
||||
<code className={`inline-block w-full ${commonClassName}`}>
|
||||
<span
|
||||
className={twMerge(
|
||||
@@ -108,6 +191,12 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
isImport,
|
||||
isLoadingValue,
|
||||
isErrorLoadingValue,
|
||||
(part) => {
|
||||
setHoveredPart(part);
|
||||
},
|
||||
hoveredPart,
|
||||
isCmdOrCtrlPressed,
|
||||
onClickSegment,
|
||||
placeholder
|
||||
)}
|
||||
</span>
|
||||
@@ -138,6 +227,9 @@ export const SecretInput = forwardRef<HTMLTextAreaElement, Props>(
|
||||
onBlur?.(evt);
|
||||
setIsSecretFocused.off();
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHoveredPart(undefined);
|
||||
}}
|
||||
value={value || ""}
|
||||
{...props}
|
||||
readOnly={isReadOnly || isLoadingValue || isErrorLoadingValue}
|
||||
|
||||
@@ -26,9 +26,6 @@ export const envConfig = {
|
||||
import.meta.env.VITE_TELEMETRY_CAPTURING_ENABLED === true
|
||||
);
|
||||
},
|
||||
get ACME_FEATURE_ENABLED() {
|
||||
return window?.__INFISICAL_RUNTIME_ENV__?.ACME_FEATURE_ENABLED ?? false;
|
||||
},
|
||||
|
||||
get PLATFORM_VERSION() {
|
||||
return import.meta.env.VITE_INFISICAL_PLATFORM_VERSION;
|
||||
|
||||
@@ -78,7 +78,11 @@ export enum ProjectPermissionIdentityActions {
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
GrantPrivileges = "grant-privileges",
|
||||
AssumePrivileges = "assume-privileges"
|
||||
AssumePrivileges = "assume-privileges",
|
||||
RevokeAuth = "revoke-auth",
|
||||
CreateToken = "create-token",
|
||||
GetToken = "get-token",
|
||||
DeleteToken = "delete-token"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionMemberActions {
|
||||
|
||||
Vendored
-1
@@ -7,7 +7,6 @@ declare global {
|
||||
POSTHOG_API_KEY?: string;
|
||||
INTERCOM_ID?: string;
|
||||
TELEMETRY_CAPTURING_ENABLED: string;
|
||||
ACME_FEATURE_ENABLED?: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +251,17 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.UPDATE_ORG_ROLE]: "Update Org Role",
|
||||
[EventType.DELETE_ORG_ROLE]: "Delete Org Role",
|
||||
|
||||
[EventType.CREATE_SUB_ORGANIZATION]: "Create Sub Organization",
|
||||
[EventType.UPDATE_SUB_ORGANIZATION]: "Update Sub Organization",
|
||||
|
||||
[EventType.CREATE_IDENTITY_ORG_MEMBERSHIP]: "Create Identity Org Membership",
|
||||
[EventType.UPDATE_IDENTITY_ORG_MEMBERSHIP]: "Update Identity Org Membership",
|
||||
[EventType.DELETE_IDENTITY_ORG_MEMBERSHIP]: "Delete Identity Org Membership",
|
||||
|
||||
[EventType.CREATE_IDENTITY_PROJECT_MEMBERSHIP]: "Create Identity Project Membership",
|
||||
[EventType.UPDATE_IDENTITY_PROJECT_MEMBERSHIP]: "Update Identity Project Membership",
|
||||
[EventType.DELETE_IDENTITY_PROJECT_MEMBERSHIP]: "Delete Identity Project Membership",
|
||||
|
||||
[EventType.PAM_SESSION_START]: "PAM Session Start",
|
||||
[EventType.PAM_SESSION_LOGS_UPDATE]: "PAM Session Logs Update",
|
||||
[EventType.PAM_SESSION_END]: "PAM Session End",
|
||||
|
||||
@@ -243,6 +243,17 @@ export enum EventType {
|
||||
UPDATE_ORG_ROLE = "update-org-role",
|
||||
DELETE_ORG_ROLE = "delete-org-role",
|
||||
|
||||
CREATE_SUB_ORGANIZATION = "create-sub-organization",
|
||||
UPDATE_SUB_ORGANIZATION = "update-sub-organization",
|
||||
|
||||
CREATE_IDENTITY_ORG_MEMBERSHIP = "create-identity-org-membership",
|
||||
UPDATE_IDENTITY_ORG_MEMBERSHIP = "update-identity-org-membership",
|
||||
DELETE_IDENTITY_ORG_MEMBERSHIP = "delete-identity-org-membership",
|
||||
|
||||
CREATE_IDENTITY_PROJECT_MEMBERSHIP = "create-identity-project-membership",
|
||||
UPDATE_IDENTITY_PROJECT_MEMBERSHIP = "update-identity-project-membership",
|
||||
DELETE_IDENTITY_PROJECT_MEMBERSHIP = "delete-identity-project-membership",
|
||||
|
||||
PAM_SESSION_START = "pam-session-start",
|
||||
PAM_SESSION_LOGS_UPDATE = "pam-session-logs-update",
|
||||
PAM_SESSION_END = "pam-session-end",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { CertStatus } from "./enums";
|
||||
export {
|
||||
useDeleteCert,
|
||||
useDownloadCertPkcs12,
|
||||
useImportCertificate,
|
||||
useRenewCertificate,
|
||||
useRevokeCert,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { projectKeys } from "../projects";
|
||||
import {
|
||||
TCertificate,
|
||||
TDeleteCertDTO,
|
||||
TDownloadPkcs12DTO,
|
||||
TImportCertificateDTO,
|
||||
TImportCertificateResponse,
|
||||
TRenewCertificateDTO,
|
||||
@@ -134,3 +135,42 @@ export const useUpdateRenewalConfig = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDownloadCertPkcs12 = () => {
|
||||
return useMutation<void, object, TDownloadPkcs12DTO>({
|
||||
mutationFn: async ({ serialNumber, projectSlug, password, alias }) => {
|
||||
try {
|
||||
const response = await apiRequest.post(
|
||||
`/api/v1/pki/certificates/${serialNumber}/pkcs12`,
|
||||
{
|
||||
password,
|
||||
alias
|
||||
},
|
||||
{
|
||||
params: { projectSlug },
|
||||
responseType: "arraybuffer"
|
||||
}
|
||||
);
|
||||
|
||||
// Create blob and trigger download
|
||||
const blob = new Blob([response.data], { type: "application/octet-stream" });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `certificate-${serialNumber}.p12`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error: any) {
|
||||
if (error.response?.data instanceof ArrayBuffer) {
|
||||
const decoder = new TextDecoder();
|
||||
const errorText = decoder.decode(error.response.data);
|
||||
const errorData = JSON.parse(errorText);
|
||||
throw new Error(errorData.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -72,3 +72,10 @@ export type TUpdateRenewalConfigDTO = {
|
||||
enableAutoRenewal?: boolean;
|
||||
projectSlug: string;
|
||||
};
|
||||
|
||||
export type TDownloadPkcs12DTO = {
|
||||
serialNumber: string;
|
||||
projectSlug: string;
|
||||
password: string;
|
||||
alias: string;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { projectIdentityQuery, projectKeys } from "@app/hooks/api";
|
||||
|
||||
import { organizationKeys } from "../organization/queries";
|
||||
import { subscriptionQueryKeys } from "../subscriptions/queries";
|
||||
import { identitiesKeys } from "./queries";
|
||||
import {
|
||||
AddIdentityAliCloudAuthDTO,
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
ClearIdentityLdapAuthLockoutsDTO,
|
||||
ClearIdentityUniversalAuthLockoutsDTO,
|
||||
ClientSecretData,
|
||||
CreateIdentityDTO,
|
||||
CreateIdentityUniversalAuthClientSecretDTO,
|
||||
CreateIdentityUniversalAuthClientSecretRes,
|
||||
CreateTokenIdentityTokenAuthDTO,
|
||||
@@ -29,7 +28,6 @@ import {
|
||||
DeleteIdentityAliCloudAuthDTO,
|
||||
DeleteIdentityAwsAuthDTO,
|
||||
DeleteIdentityAzureAuthDTO,
|
||||
DeleteIdentityDTO,
|
||||
DeleteIdentityGcpAuthDTO,
|
||||
DeleteIdentityJwtAuthDTO,
|
||||
DeleteIdentityKubernetesAuthDTO,
|
||||
@@ -40,7 +38,6 @@ import {
|
||||
DeleteIdentityTokenAuthDTO,
|
||||
DeleteIdentityUniversalAuthClientSecretDTO,
|
||||
DeleteIdentityUniversalAuthDTO,
|
||||
Identity,
|
||||
IdentityAccessToken,
|
||||
IdentityAliCloudAuth,
|
||||
IdentityAwsAuth,
|
||||
@@ -59,7 +56,6 @@ import {
|
||||
UpdateIdentityAliCloudAuthDTO,
|
||||
UpdateIdentityAwsAuthDTO,
|
||||
UpdateIdentityAzureAuthDTO,
|
||||
UpdateIdentityDTO,
|
||||
UpdateIdentityGcpAuthDTO,
|
||||
UpdateIdentityJwtAuthDTO,
|
||||
UpdateIdentityKubernetesAuthDTO,
|
||||
@@ -72,73 +68,6 @@ import {
|
||||
UpdateTokenIdentityTokenAuthDTO
|
||||
} from "./types";
|
||||
|
||||
export const useCreateIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, object, CreateIdentityDTO>({
|
||||
mutationFn: async (body) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.post("/api/v1/identities/", body);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, object, UpdateIdentityDTO>({
|
||||
mutationFn: async ({ identityId, name, role, hasDeleteProtection, metadata }) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
|
||||
name,
|
||||
role,
|
||||
hasDeleteProtection,
|
||||
metadata
|
||||
});
|
||||
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, object, DeleteIdentityDTO>({
|
||||
mutationFn: async ({ identityId }) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.delete(`/api/v1/identities/${identityId}`);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: move these to /auth
|
||||
|
||||
export const useAddIdentityUniversalAuth = () => {
|
||||
@@ -171,10 +100,20 @@ export const useAddIdentityUniversalAuth = () => {
|
||||
});
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
|
||||
@@ -215,10 +154,20 @@ export const useUpdateIdentityUniversalAuth = () => {
|
||||
});
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
|
||||
@@ -236,10 +185,20 @@ export const useDeleteIdentityUniversalAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/universal-auth/identities/${identityId}`);
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
|
||||
@@ -344,10 +303,20 @@ export const useAddIdentityGcpAuth = () => {
|
||||
|
||||
return identityGcpAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
|
||||
}
|
||||
@@ -386,10 +355,20 @@ export const useUpdateIdentityGcpAuth = () => {
|
||||
|
||||
return identityGcpAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
|
||||
}
|
||||
@@ -405,10 +384,20 @@ export const useDeleteIdentityGcpAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/gcp-auth/identities/${identityId}`);
|
||||
return identityGcpAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityGcpAuth(identityId) });
|
||||
}
|
||||
@@ -445,10 +434,20 @@ export const useAddIdentityAwsAuth = () => {
|
||||
|
||||
return identityAwsAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
|
||||
}
|
||||
@@ -485,10 +484,20 @@ export const useUpdateIdentityAwsAuth = () => {
|
||||
|
||||
return identityAwsAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
|
||||
}
|
||||
@@ -504,10 +513,20 @@ export const useDeleteIdentityAwsAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/aws-auth/identities/${identityId}`);
|
||||
return identityAwsAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAwsAuth(identityId) });
|
||||
}
|
||||
@@ -542,10 +561,20 @@ export const useAddIdentityOciAuth = () => {
|
||||
|
||||
return identityOciAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
|
||||
}
|
||||
@@ -580,10 +609,20 @@ export const useUpdateIdentityOciAuth = () => {
|
||||
|
||||
return identityOciAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
|
||||
}
|
||||
@@ -599,10 +638,20 @@ export const useDeleteIdentityOciAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/oci-auth/identities/${identityId}`);
|
||||
return identityOciAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOciAuth(identityId) });
|
||||
}
|
||||
@@ -635,10 +684,20 @@ export const useAddIdentityAliCloudAuth = () => {
|
||||
|
||||
return identityAliCloudAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
|
||||
@@ -673,10 +732,20 @@ export const useUpdateIdentityAliCloudAuth = () => {
|
||||
|
||||
return identityAliCloudAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
|
||||
@@ -694,10 +763,20 @@ export const useDeleteIdentityAliCloudAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/alicloud-auth/identities/${identityId}`);
|
||||
return identityAliCloudAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityAliCloudAuth(identityId)
|
||||
@@ -734,10 +813,20 @@ export const useAddIdentityTlsCertAuth = () => {
|
||||
|
||||
return identityTlsCertAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
|
||||
@@ -774,10 +863,20 @@ export const useUpdateIdentityTlsCertAuth = () => {
|
||||
|
||||
return identityTlsCertAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
|
||||
@@ -795,10 +894,20 @@ export const useDeleteIdentityTlsCertAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/tls-cert-auth/identities/${identityId}`);
|
||||
return identityTlsCertAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityTlsCertAuth(identityId)
|
||||
@@ -845,10 +954,20 @@ export const useUpdateIdentityOidcAuth = () => {
|
||||
|
||||
return identityOidcAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
|
||||
}
|
||||
@@ -893,10 +1012,20 @@ export const useAddIdentityOidcAuth = () => {
|
||||
|
||||
return identityOidcAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
|
||||
}
|
||||
@@ -912,10 +1041,20 @@ export const useDeleteIdentityOidcAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/oidc-auth/identities/${identityId}`);
|
||||
return identityOidcAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityOidcAuth(identityId) });
|
||||
}
|
||||
@@ -961,10 +1100,20 @@ export const useUpdateIdentityJwtAuth = () => {
|
||||
|
||||
return identityJwtAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
|
||||
}
|
||||
@@ -1011,10 +1160,20 @@ export const useAddIdentityJwtAuth = () => {
|
||||
|
||||
return identityJwtAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
|
||||
}
|
||||
@@ -1030,10 +1189,20 @@ export const useDeleteIdentityJwtAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/jwt-auth/identities/${identityId}`);
|
||||
return identityJwtAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityJwtAuth(identityId) });
|
||||
}
|
||||
@@ -1070,10 +1239,20 @@ export const useAddIdentityAzureAuth = () => {
|
||||
|
||||
return identityAzureAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
|
||||
@@ -1122,10 +1301,20 @@ export const useAddIdentityKubernetesAuth = () => {
|
||||
|
||||
return identityKubernetesAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
|
||||
}
|
||||
@@ -1162,10 +1351,20 @@ export const useUpdateIdentityAzureAuth = () => {
|
||||
|
||||
return identityAzureAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
|
||||
}
|
||||
@@ -1181,10 +1380,20 @@ export const useDeleteIdentityAzureAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/azure-auth/identities/${identityId}`);
|
||||
return identityAzureAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityAzureAuth(identityId) });
|
||||
}
|
||||
@@ -1231,10 +1440,20 @@ export const useUpdateIdentityKubernetesAuth = () => {
|
||||
|
||||
return identityKubernetesAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
|
||||
@@ -1252,10 +1471,20 @@ export const useDeleteIdentityKubernetesAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/kubernetes-auth/identities/${identityId}`);
|
||||
return identityKubernetesAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityKubernetesAuth(identityId)
|
||||
@@ -1288,10 +1517,20 @@ export const useAddIdentityTokenAuth = () => {
|
||||
|
||||
return identityTokenAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
|
||||
@@ -1324,10 +1563,20 @@ export const useUpdateIdentityTokenAuth = () => {
|
||||
|
||||
return identityTokenAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
|
||||
@@ -1345,10 +1594,20 @@ export const useDeleteIdentityTokenAuth = () => {
|
||||
} = await apiRequest.delete(`/api/v1/auth/token-auth/identities/${identityId}`);
|
||||
return identityTokenAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityTokenAuth(identityId) });
|
||||
}
|
||||
@@ -1462,10 +1721,20 @@ export const useAddIdentityLdapAuth = () => {
|
||||
);
|
||||
return data.identityLdapAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
|
||||
@@ -1519,10 +1788,20 @@ export const useUpdateIdentityLdapAuth = () => {
|
||||
);
|
||||
return data.identityLdapAuth;
|
||||
},
|
||||
onSuccess: (_, { identityId, organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { identityId, organizationId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
|
||||
@@ -1538,10 +1817,20 @@ export const useDeleteIdentityLdapAuth = () => {
|
||||
const { data } = await apiRequest.delete(`/api/v1/auth/ldap-auth/identities/${identityId}`);
|
||||
return data.identityLdapAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
onSuccess: (_, { organizationId, identityId, projectId }) => {
|
||||
if (organizationId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
}
|
||||
if (projectId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectIdentityQuery.getByIdKey({ identityId, projectId })
|
||||
});
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityLdapAuth(identityId)
|
||||
|
||||
@@ -13,10 +13,10 @@ import {
|
||||
IdentityJwtAuth,
|
||||
IdentityKubernetesAuth,
|
||||
IdentityLdapAuth,
|
||||
IdentityMembership,
|
||||
IdentityMembershipOrg,
|
||||
IdentityOciAuth,
|
||||
IdentityOidcAuth,
|
||||
IdentityProjectMembershipV1,
|
||||
IdentityTlsCertAuth,
|
||||
IdentityTokenAuth,
|
||||
IdentityUniversalAuth,
|
||||
@@ -52,7 +52,7 @@ export const identitiesKeys = {
|
||||
[{ identityId }, "identity-project-memberships"] as const
|
||||
};
|
||||
|
||||
export const useGetIdentityById = (identityId: string) => {
|
||||
export const useGetOrgIdentityMembershipById = (identityId: string) => {
|
||||
return useQuery({
|
||||
enabled: Boolean(identityId),
|
||||
queryKey: identitiesKeys.getIdentityById(identityId),
|
||||
@@ -67,7 +67,7 @@ export const useGetIdentityById = (identityId: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useSearchIdentities = (dto: TSearchIdentitiesDTO) => {
|
||||
export const useSearchOrgIdentityMemberships = (dto: TSearchIdentitiesDTO) => {
|
||||
const { limit, search, offset, orderBy, orderDirection } = dto;
|
||||
return useQuery({
|
||||
queryKey: identitiesKeys.searchIdentities(dto),
|
||||
@@ -95,7 +95,7 @@ export const useGetIdentityProjectMemberships = (identityId: string) => {
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { identityMemberships }
|
||||
} = await apiRequest.get<{ identityMemberships: IdentityMembership[] }>(
|
||||
} = await apiRequest.get<{ identityMemberships: IdentityProjectMembershipV1[] }>(
|
||||
`/api/v1/identities/${identityId}/identity-memberships`
|
||||
);
|
||||
return identityMemberships;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { TemporaryPermissionMode } from "@app/hooks/api/shared";
|
||||
|
||||
import { OrderByDirection } from "../generic/types";
|
||||
import { OrgIdentityOrderBy } from "../organization/types";
|
||||
import { Project, ProjectUserMembershipTemporaryMode } from "../projects/types";
|
||||
import { Project } from "../projects/types";
|
||||
import { TOrgRole } from "../roles/types";
|
||||
import { IdentityAuthMethod, IdentityJwtConfigurationType } from "./enums";
|
||||
|
||||
@@ -21,6 +23,8 @@ export type Identity = {
|
||||
updatedAt: string;
|
||||
isInstanceAdmin?: boolean;
|
||||
orgId: string;
|
||||
projectId?: string | null;
|
||||
metadata?: { key: string; value: string; id: string }[];
|
||||
};
|
||||
|
||||
export type IdentityAccessToken = {
|
||||
@@ -52,7 +56,7 @@ export type IdentityMembershipOrg = {
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type IdentityMembership = {
|
||||
export type IdentityProjectMembershipV1 = {
|
||||
id: string;
|
||||
identity: Identity;
|
||||
project: Pick<Project, "id" | "name" | "type">;
|
||||
@@ -74,7 +78,7 @@ export type IdentityMembership = {
|
||||
| {
|
||||
isTemporary: true;
|
||||
temporaryRange: string;
|
||||
temporaryMode: ProjectUserMembershipTemporaryMode;
|
||||
temporaryMode: TemporaryPermissionMode;
|
||||
temporaryAccessEndTime: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
@@ -82,6 +86,41 @@ export type IdentityMembership = {
|
||||
>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastLoginTime?: string;
|
||||
lastLoginAuthMethod?: IdentityAuthMethod;
|
||||
};
|
||||
|
||||
export type IdentityProjectMembershipV2 = {
|
||||
id: string;
|
||||
identity: Identity;
|
||||
roles: Array<
|
||||
{
|
||||
id: string;
|
||||
role: "owner" | "admin" | "member" | "no-access" | "custom";
|
||||
customRoleId: string;
|
||||
customRoleName: string;
|
||||
customRoleSlug: string;
|
||||
} & (
|
||||
| {
|
||||
isTemporary: false;
|
||||
temporaryRange: null;
|
||||
temporaryMode: null;
|
||||
temporaryAccessEndTime: null;
|
||||
temporaryAccessStartTime: null;
|
||||
}
|
||||
| {
|
||||
isTemporary: true;
|
||||
temporaryRange: string;
|
||||
temporaryMode: TemporaryPermissionMode;
|
||||
temporaryAccessEndTime: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
)
|
||||
>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastLoginTime?: string;
|
||||
lastLoginAuthMethod?: IdentityAuthMethod;
|
||||
};
|
||||
|
||||
export type CreateIdentityDTO = {
|
||||
@@ -122,7 +161,8 @@ export type IdentityUniversalAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityUniversalAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
clientSecretTrustedIps: {
|
||||
ipAddress: string;
|
||||
@@ -138,10 +178,11 @@ export type AddIdentityUniversalAuthDTO = {
|
||||
lockoutThreshold: number;
|
||||
lockoutDurationSeconds: number;
|
||||
lockoutCounterResetSeconds: number;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityUniversalAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
clientSecretTrustedIps?: {
|
||||
ipAddress: string;
|
||||
@@ -157,12 +198,13 @@ export type UpdateIdentityUniversalAuthDTO = {
|
||||
lockoutThreshold?: number;
|
||||
lockoutDurationSeconds?: number;
|
||||
lockoutCounterResetSeconds?: number;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityUniversalAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityGcpAuth = {
|
||||
identityId: string;
|
||||
@@ -177,7 +219,8 @@ export type IdentityGcpAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityGcpAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
type: "iam" | "gce";
|
||||
allowedServiceAccounts: string;
|
||||
@@ -189,10 +232,11 @@ export type AddIdentityGcpAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityGcpAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
type?: "iam" | "gce";
|
||||
allowedServiceAccounts?: string;
|
||||
@@ -204,12 +248,13 @@ export type UpdateIdentityGcpAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityGcpAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityOidcAuth = {
|
||||
identityId: string;
|
||||
@@ -227,7 +272,8 @@ export type IdentityOidcAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityOidcAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
oidcDiscoveryUrl: string;
|
||||
caCert: string;
|
||||
@@ -242,10 +288,11 @@ export type AddIdentityOidcAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityOidcAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
oidcDiscoveryUrl?: string;
|
||||
caCert?: string;
|
||||
@@ -260,12 +307,13 @@ export type UpdateIdentityOidcAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityOidcAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityAwsAuth = {
|
||||
identityId: string;
|
||||
@@ -280,7 +328,8 @@ export type IdentityAwsAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityAwsAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
stsEndpoint: string;
|
||||
allowedPrincipalArns: string;
|
||||
@@ -291,10 +340,11 @@ export type AddIdentityAwsAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityAwsAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
stsEndpoint?: string;
|
||||
allowedPrincipalArns?: string;
|
||||
@@ -308,9 +358,10 @@ export type UpdateIdentityAwsAuthDTO = {
|
||||
};
|
||||
|
||||
export type DeleteIdentityAwsAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityAliCloudAuth = {
|
||||
identityId: string;
|
||||
@@ -323,7 +374,8 @@ export type IdentityAliCloudAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityAliCloudAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
allowedArns: string;
|
||||
accessTokenTTL: number;
|
||||
@@ -332,10 +384,11 @@ export type AddIdentityAliCloudAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityAliCloudAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
allowedArns: string;
|
||||
accessTokenTTL?: number;
|
||||
@@ -344,12 +397,13 @@ export type UpdateIdentityAliCloudAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityAliCloudAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityOciAuth = {
|
||||
identityId: string;
|
||||
@@ -363,7 +417,8 @@ export type IdentityOciAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityOciAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
tenancyOcid: string;
|
||||
allowedUsernames?: string | null;
|
||||
@@ -373,10 +428,11 @@ export type AddIdentityOciAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityOciAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
tenancyOcid?: string;
|
||||
allowedUsernames?: string | null;
|
||||
@@ -386,12 +442,13 @@ export type UpdateIdentityOciAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityOciAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityAzureAuth = {
|
||||
identityId: string;
|
||||
@@ -405,7 +462,8 @@ export type IdentityAzureAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityAzureAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
tenantId: string;
|
||||
resource: string;
|
||||
@@ -416,10 +474,11 @@ export type AddIdentityAzureAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityAzureAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
tenantId?: string;
|
||||
resource?: string;
|
||||
@@ -430,12 +489,13 @@ export type UpdateIdentityAzureAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityAzureAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export enum IdentityKubernetesAuthTokenReviewMode {
|
||||
Api = "api",
|
||||
@@ -459,7 +519,8 @@ export type IdentityKubernetesAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityKubernetesAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
kubernetesHost: string | null;
|
||||
tokenReviewerJwt?: string;
|
||||
@@ -475,10 +536,11 @@ export type AddIdentityKubernetesAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityKubernetesAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
kubernetesHost?: string | null;
|
||||
tokenReviewerJwt?: string | null;
|
||||
@@ -494,12 +556,13 @@ export type UpdateIdentityKubernetesAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityKubernetesAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityTlsCertAuth = {
|
||||
identityId: string;
|
||||
@@ -512,7 +575,8 @@ export type IdentityTlsCertAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityTlsCertAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
caCertificate: string;
|
||||
allowedCommonNames?: string;
|
||||
@@ -522,10 +586,11 @@ export type AddIdentityTlsCertAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityTlsCertAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
caCertificate: string;
|
||||
allowedCommonNames?: string | null;
|
||||
@@ -535,12 +600,13 @@ export type UpdateIdentityTlsCertAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityTlsCertAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type CreateIdentityUniversalAuthClientSecretDTO = {
|
||||
identityId: string;
|
||||
@@ -585,7 +651,8 @@ export type IdentityTokenAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityLdapAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
templateId?: string;
|
||||
url?: string;
|
||||
@@ -609,11 +676,12 @@ export type AddIdentityLdapAuthDTO = {
|
||||
lockoutThreshold: number;
|
||||
lockoutDurationSeconds: number;
|
||||
lockoutCounterResetSeconds: number;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityLdapAuthDTO = {
|
||||
identityId: string;
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
templateId?: string;
|
||||
url?: string;
|
||||
bindDN?: string;
|
||||
@@ -636,12 +704,13 @@ export type UpdateIdentityLdapAuthDTO = {
|
||||
lockoutThreshold?: number;
|
||||
lockoutDurationSeconds?: number;
|
||||
lockoutCounterResetSeconds?: number;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityLdapAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityLdapAuth = {
|
||||
url?: string;
|
||||
@@ -673,7 +742,8 @@ export type ClearIdentityLdapAuthLockoutsDTO = {
|
||||
};
|
||||
|
||||
export type AddIdentityTokenAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
accessTokenTTL: number;
|
||||
accessTokenMaxTTL: number;
|
||||
@@ -681,10 +751,11 @@ export type AddIdentityTokenAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityTokenAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
accessTokenTTL?: number;
|
||||
accessTokenMaxTTL?: number;
|
||||
@@ -692,12 +763,13 @@ export type UpdateIdentityTokenAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityTokenAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type IdentityJwtAuth = {
|
||||
identityId: string;
|
||||
@@ -716,7 +788,8 @@ export type IdentityJwtAuth = {
|
||||
};
|
||||
|
||||
export type AddIdentityJwtAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
configurationType: string;
|
||||
jwksUrl?: string;
|
||||
@@ -732,10 +805,11 @@ export type AddIdentityJwtAuthDTO = {
|
||||
accessTokenTrustedIps: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type UpdateIdentityJwtAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
configurationType?: string;
|
||||
jwksUrl?: string;
|
||||
@@ -751,12 +825,13 @@ export type UpdateIdentityJwtAuthDTO = {
|
||||
accessTokenTrustedIps?: {
|
||||
ipAddress: string;
|
||||
}[];
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type DeleteIdentityJwtAuthDTO = {
|
||||
organizationId: string;
|
||||
organizationId?: string;
|
||||
projectId?: string;
|
||||
identityId: string;
|
||||
};
|
||||
} & ({ organizationId: string } | { projectId: string });
|
||||
|
||||
export type CreateTokenIdentityTokenAuthDTO = {
|
||||
identityId: string;
|
||||
@@ -783,8 +858,13 @@ export type RevokeTokenRes = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type TProjectIdentitiesList = {
|
||||
identityMemberships: IdentityMembership[];
|
||||
export type TProjectIdentityMembershipsList = {
|
||||
identityMemberships: IdentityProjectMembershipV1[];
|
||||
totalCount: number;
|
||||
};
|
||||
|
||||
export type TProjectIdentityMembershipsListV2 = {
|
||||
identityMemberships: IdentityProjectMembershipV2[];
|
||||
totalCount: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,10 +25,14 @@ export * from "./ldapConfig";
|
||||
export * from "./oidcConfig";
|
||||
export * from "./orgAdmin";
|
||||
export * from "./organization";
|
||||
export * from "./orgIdentity";
|
||||
export * from "./orgIdentityMembership";
|
||||
export * from "./pkiAlerts";
|
||||
export * from "./pkiCollections";
|
||||
export * from "./pkiSubscriber";
|
||||
export * from "./pkiSyncs";
|
||||
export * from "./projectIdentity";
|
||||
export * from "./projectIdentityMembership";
|
||||
export * from "./projects";
|
||||
export * from "./projectUserAdditionalPrivilege";
|
||||
export * from "./rateLimit";
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./mutations";
|
||||
export * from "./queries";
|
||||
export type * from "./types";
|
||||
@@ -0,0 +1,116 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { identitiesKeys } from "@app/hooks/api";
|
||||
import { CreateIdentityDTO, Identity, UpdateIdentityDTO } from "@app/hooks/api/identities/types";
|
||||
import { organizationKeys } from "@app/hooks/api/organization/queries";
|
||||
import { subscriptionQueryKeys } from "@app/hooks/api/subscriptions/queries";
|
||||
|
||||
import { orgIdentityQuery } from "./queries";
|
||||
import { TDeleteOrgIdentityDTO, TOrgIdentity } from "./types";
|
||||
|
||||
// TODO (scott/akhi): eventually move to the new api commented out below; the current ones use old api
|
||||
|
||||
export const useCreateOrgIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, object, CreateIdentityDTO>({
|
||||
mutationFn: async (body) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.post("/api/v1/identities/", body);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.getOrgSubsription(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateOrgIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, object, UpdateIdentityDTO>({
|
||||
mutationFn: async ({ identityId, name, role, hasDeleteProtection, metadata }) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
|
||||
name,
|
||||
role,
|
||||
hasDeleteProtection,
|
||||
metadata
|
||||
});
|
||||
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId, identityId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(organizationId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// export const useCreateOrgIdentity = () => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: async (dto: TCreateOrgIdentityDTO) => {
|
||||
// const { data } = await apiRequest.post<{ identity: TOrgIdentity }>(
|
||||
// "/api/v1/organization/identities",
|
||||
// dto
|
||||
// );
|
||||
// return data;
|
||||
// },
|
||||
// onSuccess: () => {
|
||||
// queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
|
||||
// queryClient.invalidateQueries({
|
||||
// queryKey: subscriptionQueryKeys.all()
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// export const useUpdateOrgIdentity = () => {
|
||||
// const queryClient = useQueryClient();
|
||||
// return useMutation({
|
||||
// mutationFn: async ({ identityId, ...updates }: TUpdateOrgIdentityDTO) => {
|
||||
// const { data } = await apiRequest.patch<{ identity: TOrgIdentity }>(
|
||||
// `/api/v1/organization/identities/${identityId}`,
|
||||
// updates
|
||||
// );
|
||||
// return data;
|
||||
// },
|
||||
// onSuccess: () => {
|
||||
// queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useDeleteOrgIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ identityId }: TDeleteOrgIdentityDTO) => {
|
||||
const { data } = await apiRequest.delete<{ identity: TOrgIdentity }>(
|
||||
`/api/v1/identities/${identityId}`
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { orgId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: organizationKeys.getOrgIdentityMemberships(orgId)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentitiesRoot });
|
||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(orgId) });
|
||||
queryClient.invalidateQueries({ queryKey: orgIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.all()
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TGetOrgIdentityByIdDTO, TListOrgIdentitiesDTO, TOrgIdentity } from "./types";
|
||||
|
||||
export const orgIdentityQuery = {
|
||||
allKey: () => ["organization-identities"] as const,
|
||||
getByIdKey: (params: TGetOrgIdentityByIdDTO) =>
|
||||
[...orgIdentityQuery.allKey(), "by-id", params] as const,
|
||||
listKey: (params?: TListOrgIdentitiesDTO) =>
|
||||
[...orgIdentityQuery.allKey(), "list", params] as const,
|
||||
getById: (params: TGetOrgIdentityByIdDTO) =>
|
||||
queryOptions({
|
||||
queryKey: orgIdentityQuery.getByIdKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{ identity: TOrgIdentity }>(
|
||||
`/api/v1/organization/identities/${params.identityId}`
|
||||
);
|
||||
return data.identity;
|
||||
}
|
||||
}),
|
||||
list: (params: TListOrgIdentitiesDTO = {}) =>
|
||||
queryOptions({
|
||||
queryKey: orgIdentityQuery.listKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{
|
||||
identities: TOrgIdentity[];
|
||||
totalCount: number;
|
||||
}>("/api/v1/organization/identities", {
|
||||
params: {
|
||||
offset: params.offset,
|
||||
limit: params.limit,
|
||||
search: params.search
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { TIdentity, TMetadata } from "@app/hooks/api/shared";
|
||||
|
||||
export type TOrgIdentity = TIdentity;
|
||||
|
||||
export type TCreateOrgIdentityDTO = {
|
||||
name: string;
|
||||
hasDeleteProtection?: boolean;
|
||||
metadata?: TMetadata;
|
||||
};
|
||||
|
||||
export type TUpdateOrgIdentityDTO = {
|
||||
identityId: string;
|
||||
name?: string;
|
||||
hasDeleteProtection?: boolean;
|
||||
metadata?: TMetadata;
|
||||
};
|
||||
|
||||
export type TGetOrgIdentityByIdDTO = {
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export type TListOrgIdentitiesDTO = {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
search?: string;
|
||||
};
|
||||
|
||||
export type TDeleteOrgIdentityDTO = {
|
||||
identityId: string;
|
||||
orgId: string;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import {
|
||||
TAvailableOrganizationIdentities,
|
||||
TListAvailableOrganizationIdentitiesDTO,
|
||||
TListOrgIdentityMembershipsDTO
|
||||
} from "./types";
|
||||
|
||||
export const orgIdentityMembershipQuery = {
|
||||
allKey: () => ["organization-identity-memberships"] as const,
|
||||
listAvailableKey: (params?: TListOrgIdentityMembershipsDTO) =>
|
||||
[...orgIdentityMembershipQuery.allKey(), "list-available", params] as const,
|
||||
listAvailable: (params: TListAvailableOrganizationIdentitiesDTO = {}) =>
|
||||
queryOptions({
|
||||
queryKey: orgIdentityMembershipQuery.listAvailableKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{
|
||||
identities: TAvailableOrganizationIdentities;
|
||||
}>("/api/v1/organization/available-identities", {
|
||||
params: {
|
||||
offset: params.offset,
|
||||
limit: params.limit,
|
||||
identityName: params.identityName
|
||||
}
|
||||
});
|
||||
return data.identities;
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -1,6 +1,4 @@
|
||||
export enum TemporaryPermissionMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
import { TRoles } from "@app/hooks/api/shared";
|
||||
|
||||
export type TOrgIdentityMembership = {
|
||||
id: string;
|
||||
@@ -12,21 +10,24 @@ export type TOrgIdentityMembership = {
|
||||
|
||||
export type TCreateOrgIdentityMembershipDTO = {
|
||||
identityId: string;
|
||||
roles: Array<
|
||||
| {
|
||||
role: string;
|
||||
isTemporary?: false;
|
||||
}
|
||||
| {
|
||||
role: string;
|
||||
isTemporary: true;
|
||||
temporaryMode: TemporaryPermissionMode;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
>;
|
||||
roles: TRoles;
|
||||
};
|
||||
|
||||
export type TDeleteOrgIdentityMembershipDTO = {
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export type TListOrgIdentityMembershipsDTO = {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
identityName?: string;
|
||||
roles?: string[];
|
||||
};
|
||||
|
||||
export type TListAvailableOrganizationIdentitiesDTO = {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
identityName?: string;
|
||||
};
|
||||
|
||||
export type TAvailableOrganizationIdentities = Array<{ id: string; name: string }>;
|
||||
|
||||
@@ -6,7 +6,6 @@ export {
|
||||
useDeleteOrgById,
|
||||
useDeleteOrgPmtMethod,
|
||||
useDeleteOrgTaxId,
|
||||
useGetAvailableOrgIdentities,
|
||||
useGetIdentityMembershipOrgs,
|
||||
useGetOrganizationGroups,
|
||||
useGetOrganizations,
|
||||
|
||||
@@ -579,19 +579,6 @@ export const useGetOrgIntegrationAuths = <TData = IntegrationAuth[],>(
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAvailableOrgIdentities = (enabled = true) =>
|
||||
useQuery({
|
||||
queryKey: organizationKeys.getAvailableIdentities(),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{ identities: { name: string; id: string }[] }>(
|
||||
"/api/v1/organization/identities/available"
|
||||
);
|
||||
|
||||
return data.identities;
|
||||
},
|
||||
enabled
|
||||
});
|
||||
|
||||
export const useGetAvailableOrgUsers = (enabled = true) =>
|
||||
useQuery({
|
||||
queryKey: organizationKeys.getAvailableUsers(),
|
||||
|
||||
@@ -3,7 +3,19 @@ export enum PamResourceType {
|
||||
MySQL = "mysql",
|
||||
RDP = "rdp",
|
||||
SSH = "ssh",
|
||||
Kubernetes = "kubernetes"
|
||||
Kubernetes = "kubernetes",
|
||||
OracleDB = "oracledb",
|
||||
SQLite = "sqlite",
|
||||
MsSQL = "mssql",
|
||||
MCP = "mcp",
|
||||
Redis = "redis",
|
||||
MongoDB = "mongodb",
|
||||
WebApp = "webapp",
|
||||
Cassandra = "cassandra",
|
||||
CockroachDB = "cockroachdb",
|
||||
Elasticsearch = "elasticsearch",
|
||||
Snowflake = "snowflake",
|
||||
DynamoDB = "dynamodb"
|
||||
}
|
||||
|
||||
export enum PamSessionStatus {
|
||||
|
||||
@@ -8,5 +8,17 @@ export const PAM_RESOURCE_TYPE_MAP: Record<
|
||||
[PamResourceType.MySQL]: { name: "MySQL", image: "MySql.png" },
|
||||
[PamResourceType.RDP]: { name: "RDP", image: "RDP.png" },
|
||||
[PamResourceType.SSH]: { name: "SSH", image: "SSH.png" },
|
||||
[PamResourceType.Kubernetes]: { name: "Kubernetes", image: "Kubernetes.png" }
|
||||
[PamResourceType.Kubernetes]: { name: "Kubernetes", image: "Kubernetes.png" },
|
||||
[PamResourceType.OracleDB]: { name: "OracleDB", image: "Oracle.png", size: 55 },
|
||||
[PamResourceType.SQLite]: { name: "SQLite", image: "SQLite.png" },
|
||||
[PamResourceType.MsSQL]: { name: "Microsoft SQL Server", image: "MsSql.png" },
|
||||
[PamResourceType.MCP]: { name: "MCP", image: "MCP.png" },
|
||||
[PamResourceType.Redis]: { name: "Redis", image: "Redis.png" },
|
||||
[PamResourceType.MongoDB]: { name: "MongoDB", image: "MongoDB.png" },
|
||||
[PamResourceType.WebApp]: { name: "Web Application", image: "Web.png" },
|
||||
[PamResourceType.Cassandra]: { name: "Cassandra", image: "Cassandra.png", size: 55 },
|
||||
[PamResourceType.CockroachDB]: { name: "CockroachDB", image: "CockroachDB.png" },
|
||||
[PamResourceType.Elasticsearch]: { name: "Elasticsearch", image: "Elastic.png" },
|
||||
[PamResourceType.Snowflake]: { name: "Snowflake", image: "Snowflake.png" },
|
||||
[PamResourceType.DynamoDB]: { name: "DynamoDB", image: "DynamoDB.png", size: 55 }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export {
|
||||
useCreateProjectIdentity,
|
||||
useDeleteProjectIdentity,
|
||||
useUpdateProjectIdentity
|
||||
} from "./mutations";
|
||||
export { projectIdentityQuery } from "./queries";
|
||||
export type {
|
||||
TCreateProjectIdentityDTO,
|
||||
TDeleteProjectIdentityDTO,
|
||||
TGetProjectIdentityByIdDTO,
|
||||
TListProjectIdentitiesDTO,
|
||||
TProjectIdentity,
|
||||
TProjectIdentityMetadata,
|
||||
TUpdateProjectIdentityDTO
|
||||
} from "./types";
|
||||
@@ -0,0 +1,76 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { identitiesKeys, projectKeys } from "@app/hooks/api";
|
||||
import { subscriptionQueryKeys } from "@app/hooks/api/subscriptions/queries";
|
||||
|
||||
import { projectIdentityQuery } from "./queries";
|
||||
import {
|
||||
TCreateProjectIdentityDTO,
|
||||
TDeleteProjectIdentityDTO,
|
||||
TProjectIdentity,
|
||||
TUpdateProjectIdentityDTO
|
||||
} from "./types";
|
||||
|
||||
export const useCreateProjectIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ projectId, ...dto }: TCreateProjectIdentityDTO) => {
|
||||
const { data } = await apiRequest.post<{ identity: TProjectIdentity }>(
|
||||
`/api/v1/projects/${projectId}/identities`,
|
||||
dto
|
||||
);
|
||||
return data.identity;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.all()
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateProjectIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ projectId, identityId, ...updates }: TUpdateProjectIdentityDTO) => {
|
||||
const { data } = await apiRequest.patch<{ identity: TProjectIdentity }>(
|
||||
`/api/v1/projects/${projectId}/identities/${identityId}`,
|
||||
updates
|
||||
);
|
||||
return data.identity;
|
||||
},
|
||||
onSuccess: (_, { projectId, identityId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteProjectIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ projectId, identityId }: TDeleteProjectIdentityDTO) => {
|
||||
const { data } = await apiRequest.delete<{ identity: TProjectIdentity }>(
|
||||
`/api/v1/projects/${projectId}/identities/${identityId}`
|
||||
);
|
||||
return data.identity;
|
||||
},
|
||||
onSuccess: (_, { projectId, identityId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: subscriptionQueryKeys.all()
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TGetProjectIdentityByIdDTO, TListProjectIdentitiesDTO, TProjectIdentity } from "./types";
|
||||
|
||||
export const projectIdentityQuery = {
|
||||
allKey: () => ["project-identities"] as const,
|
||||
getByIdKey: (params: TGetProjectIdentityByIdDTO) =>
|
||||
[...projectIdentityQuery.allKey(), "by-id", params] as const,
|
||||
listKey: (params: TListProjectIdentitiesDTO) =>
|
||||
[...projectIdentityQuery.allKey(), "list", params] as const,
|
||||
getById: (params: TGetProjectIdentityByIdDTO) =>
|
||||
queryOptions({
|
||||
queryKey: projectIdentityQuery.getByIdKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{ identity: TProjectIdentity }>(
|
||||
`/api/v1/projects/${params.projectId}/identities/${params.identityId}`
|
||||
);
|
||||
return data.identity;
|
||||
}
|
||||
}),
|
||||
list: (params: TListProjectIdentitiesDTO) =>
|
||||
queryOptions({
|
||||
queryKey: projectIdentityQuery.listKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{
|
||||
identities: TProjectIdentity[];
|
||||
totalCount: number;
|
||||
}>(`/api/v1/projects/${params.projectId}/identities`, {
|
||||
params: {
|
||||
offset: params.offset,
|
||||
limit: params.limit,
|
||||
search: params.search
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { TIdentity, TMetadata } from "@app/hooks/api/shared";
|
||||
|
||||
export type TProjectIdentityMetadata = {
|
||||
key: string;
|
||||
value: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type TProjectIdentity = TIdentity;
|
||||
|
||||
export type TCreateProjectIdentityDTO = {
|
||||
projectId: string;
|
||||
name: string;
|
||||
hasDeleteProtection?: boolean;
|
||||
metadata?: TMetadata[];
|
||||
};
|
||||
|
||||
export type TUpdateProjectIdentityDTO = {
|
||||
projectId: string;
|
||||
identityId: string;
|
||||
name?: string;
|
||||
hasDeleteProtection?: boolean;
|
||||
metadata?: TMetadata[];
|
||||
};
|
||||
|
||||
export type TGetProjectIdentityByIdDTO = {
|
||||
projectId: string;
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export type TListProjectIdentitiesDTO = {
|
||||
projectId: string;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
search?: string;
|
||||
};
|
||||
|
||||
export type TDeleteProjectIdentityDTO = {
|
||||
projectId: string;
|
||||
identityId: string;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./mutations";
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,93 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { identitiesKeys, projectKeys } from "@app/hooks/api";
|
||||
import { projectIdentityQuery } from "@app/hooks/api/projectIdentity";
|
||||
|
||||
import {
|
||||
TCreateProjectIdentityMembershipDTO,
|
||||
TDeleteProjectIdentityMembershipDTO,
|
||||
TProjectIdentityMembership,
|
||||
TUpdateProjectIdentityMembershipDTO
|
||||
} from "./types";
|
||||
|
||||
export const useCreateProjectIdentityMembership = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ identityId, projectId, role }: TCreateProjectIdentityMembershipDTO) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.post<{ identityMembership: TProjectIdentityMembership }>(
|
||||
`/api/v1/projects/${projectId}/memberships/identities/${identityId}`,
|
||||
{
|
||||
role
|
||||
}
|
||||
);
|
||||
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { identityId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateProjectIdentityMembership = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
projectId,
|
||||
identityId,
|
||||
...updates
|
||||
}: TUpdateProjectIdentityMembershipDTO) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.patch<{ identityMembership: TProjectIdentityMembership }>(
|
||||
`/api/v1/projects/${projectId}/memberships/identities/${identityId}`,
|
||||
updates
|
||||
);
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { projectId, identityId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteProjectIdentityMembership = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ identityId, projectId }: TDeleteProjectIdentityMembershipDTO) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.delete<{ identityMembership: TProjectIdentityMembership }>(
|
||||
`/api/v1/projects/${projectId}/memberships/identities/${identityId}`
|
||||
);
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { identityId, projectId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: projectIdentityQuery.allKey() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
import { queryOptions, useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import {
|
||||
projectKeys,
|
||||
TAvailableProjectIdentities,
|
||||
TListAvailableProjectIdentitiesDTO
|
||||
} from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import {
|
||||
IdentityProjectMembershipV1,
|
||||
TProjectIdentityMembershipsListV2
|
||||
} from "@app/hooks/api/identities/types";
|
||||
import { ProjectIdentityOrderBy, TListProjectIdentitiesDTO } from "@app/hooks/api/projects/types";
|
||||
|
||||
export const projectIdentityMembershipQuery = {
|
||||
allKey: () => ["project-identity-memberships"] as const,
|
||||
listAvailableKey: (params?: TListAvailableProjectIdentitiesDTO) =>
|
||||
[...projectIdentityMembershipQuery.allKey(), "list-available", params] as const,
|
||||
listAvailable: (params: TListAvailableProjectIdentitiesDTO) =>
|
||||
queryOptions({
|
||||
queryKey: projectIdentityMembershipQuery.listAvailableKey(params),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<{
|
||||
identities: TAvailableProjectIdentities;
|
||||
}>(`/api/v1/projects/${params.projectId}/memberships/available-identities`, {
|
||||
params: {
|
||||
offset: params.offset,
|
||||
limit: params.limit,
|
||||
identityName: params.identityName
|
||||
}
|
||||
});
|
||||
return data.identities;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// TODO (scott/akhi): move to new projectIdentityMembershipQuery structure
|
||||
|
||||
export const useListProjectIdentityMemberships = (
|
||||
{
|
||||
projectId,
|
||||
offset = 0,
|
||||
limit = 100,
|
||||
orderBy = ProjectIdentityOrderBy.Name,
|
||||
orderDirection = OrderByDirection.ASC,
|
||||
search = ""
|
||||
}: TListProjectIdentitiesDTO,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TProjectIdentityMembershipsListV2,
|
||||
unknown,
|
||||
TProjectIdentityMembershipsListV2,
|
||||
ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: projectKeys.getProjectIdentityMembershipsWithParams({
|
||||
projectId,
|
||||
offset,
|
||||
limit,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
search
|
||||
}),
|
||||
queryFn: async () => {
|
||||
const params = new URLSearchParams({
|
||||
offset: String(offset),
|
||||
limit: String(limit),
|
||||
orderBy: String(orderBy),
|
||||
orderDirection: String(orderDirection),
|
||||
search: String(search)
|
||||
});
|
||||
|
||||
const { data } = await apiRequest.get<TProjectIdentityMembershipsListV2>(
|
||||
`/api/v1/projects/${projectId}/memberships/identities`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
},
|
||||
enabled: true,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetProjectIdentityMembership = (projectId: string, identityId: string) => {
|
||||
return useQuery({
|
||||
enabled: Boolean(projectId && identityId),
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId),
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.get<{ identityMembership: IdentityProjectMembershipV1 }>(
|
||||
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
|
||||
);
|
||||
return identityMembership;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetProjectIdentityMembershipV2 = (projectId: string, identityId: string) => {
|
||||
return useQuery({
|
||||
enabled: Boolean(projectId && identityId),
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetailsV2(projectId, identityId),
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.get<{ identityMembership: IdentityProjectMembershipV1 }>(
|
||||
`/api/v1/projects/${projectId}/memberships/identities/${identityId}`
|
||||
);
|
||||
return identityMembership;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { TRoles } from "@app/hooks/api/shared";
|
||||
|
||||
export type TProjectIdentityMembership = {
|
||||
id: string;
|
||||
projectId: string;
|
||||
identityId: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
// TODO
|
||||
};
|
||||
|
||||
export type TCreateProjectIdentityMembershipDTO = {
|
||||
identityId: string;
|
||||
projectId: string;
|
||||
role?: string;
|
||||
};
|
||||
|
||||
export type TUpdateProjectIdentityMembershipDTO = {
|
||||
identityId: string;
|
||||
projectId: string;
|
||||
roles: TRoles;
|
||||
};
|
||||
|
||||
export type TDeleteProjectIdentityMembershipDTO = {
|
||||
identityId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export type TListAvailableProjectIdentitiesDTO = {
|
||||
projectId: string;
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
identityName?: string;
|
||||
};
|
||||
|
||||
export type TAvailableProjectIdentities = Array<{ id: string; name: string }>;
|
||||
@@ -8,10 +8,8 @@ export {
|
||||
useUpdateProjectSshConfig
|
||||
} from "./mutations";
|
||||
export {
|
||||
useAddIdentityToWorkspace,
|
||||
useCreateWorkspace,
|
||||
useCreateWsEnvironment,
|
||||
useDeleteIdentityFromWorkspace,
|
||||
useDeleteUserFromWorkspace,
|
||||
useDeleteWorkspace,
|
||||
useDeleteWsEnvironment,
|
||||
@@ -21,8 +19,6 @@ export {
|
||||
useGetUserWorkspaceMemberships,
|
||||
useGetWorkspaceAuthorizations,
|
||||
useGetWorkspaceById,
|
||||
useGetWorkspaceIdentityMembershipDetails,
|
||||
useGetWorkspaceIdentityMemberships,
|
||||
useGetWorkspaceIndexStatus,
|
||||
useGetWorkspaceIntegrations,
|
||||
useGetWorkspaceUserDetails,
|
||||
@@ -41,7 +37,6 @@ export {
|
||||
useListWorkspaceSshHostGroups,
|
||||
useListWorkspaceSshHosts,
|
||||
useSearchProjects,
|
||||
useUpdateIdentityWorkspaceRole,
|
||||
useUpdateProject,
|
||||
useUpdateUserWorkspaceRole,
|
||||
useUpdateWsEnvironment,
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
|
||||
import { CaStatus } from "../ca/enums";
|
||||
import { TCertificateAuthority } from "../ca/types";
|
||||
import { TCertificate } from "../certificates/types";
|
||||
import { TCertificateTemplate } from "../certificateTemplates/types";
|
||||
import { TGroupMembership } from "../groups/types";
|
||||
import { identitiesKeys } from "../identities/queries";
|
||||
import { IdentityMembership, TProjectIdentitiesList } from "../identities/types";
|
||||
import { IntegrationAuth } from "../integrationAuth/types";
|
||||
import { TIntegration } from "../integrations/types";
|
||||
import { TPkiAlert } from "../pkiAlerts/types";
|
||||
@@ -33,13 +30,10 @@ import {
|
||||
DeleteWorkspaceDTO,
|
||||
Project,
|
||||
ProjectEnv,
|
||||
ProjectIdentityOrderBy,
|
||||
ProjectType,
|
||||
TGetUpgradeProjectStatusDTO,
|
||||
TListProjectIdentitiesDTO,
|
||||
TProjectSshConfig,
|
||||
TSearchProjectsDTO,
|
||||
TUpdateWorkspaceIdentityRoleDTO,
|
||||
TUpdateWorkspaceUserRoleDTO,
|
||||
UpdateAuditLogsRetentionDTO,
|
||||
UpdateEnvironmentDTO,
|
||||
@@ -452,154 +446,6 @@ export const useUpdateUserWorkspaceRole = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useAddIdentityToWorkspace = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
projectId,
|
||||
role
|
||||
}: {
|
||||
identityId: string;
|
||||
projectId: string;
|
||||
role?: string;
|
||||
}) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.post(
|
||||
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
|
||||
{
|
||||
role
|
||||
}
|
||||
);
|
||||
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { identityId, projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateIdentityWorkspaceRole = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ identityId, projectId, roles }: TUpdateWorkspaceIdentityRoleDTO) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.patch(
|
||||
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`,
|
||||
{
|
||||
roles
|
||||
}
|
||||
);
|
||||
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { identityId, projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteIdentityFromWorkspace = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({ identityId, projectId }: { identityId: string; projectId: string }) => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.delete(
|
||||
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
|
||||
);
|
||||
return identityMembership;
|
||||
},
|
||||
onSuccess: (_, { identityId, projectId }) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: projectKeys.getProjectIdentityMemberships(projectId)
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: identitiesKeys.getIdentityProjectMemberships(identityId)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetWorkspaceIdentityMemberships = (
|
||||
{
|
||||
projectId,
|
||||
offset = 0,
|
||||
limit = 100,
|
||||
orderBy = ProjectIdentityOrderBy.Name,
|
||||
orderDirection = OrderByDirection.ASC,
|
||||
search = ""
|
||||
}: TListProjectIdentitiesDTO,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TProjectIdentitiesList,
|
||||
unknown,
|
||||
TProjectIdentitiesList,
|
||||
ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: projectKeys.getProjectIdentityMembershipsWithParams({
|
||||
projectId,
|
||||
offset,
|
||||
limit,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
search
|
||||
}),
|
||||
queryFn: async () => {
|
||||
const params = new URLSearchParams({
|
||||
offset: String(offset),
|
||||
limit: String(limit),
|
||||
orderBy: String(orderBy),
|
||||
orderDirection: String(orderDirection),
|
||||
search: String(search)
|
||||
});
|
||||
|
||||
const { data } = await apiRequest.get<TProjectIdentitiesList>(
|
||||
`/api/v1/projects/${projectId}/identity-memberships`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
},
|
||||
enabled: true,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetWorkspaceIdentityMembershipDetails = (projectId: string, identityId: string) => {
|
||||
return useQuery({
|
||||
enabled: Boolean(projectId && identityId),
|
||||
queryKey: projectKeys.getProjectIdentityMembershipDetails(projectId, identityId),
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { identityMembership }
|
||||
} = await apiRequest.get<{ identityMembership: IdentityMembership }>(
|
||||
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`
|
||||
);
|
||||
return identityMembership;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetWorkspaceGroupMembershipDetails = (projectId: string, groupId: string) => {
|
||||
return useQuery({
|
||||
enabled: Boolean(projectId && groupId),
|
||||
|
||||
@@ -23,6 +23,8 @@ export const projectKeys = {
|
||||
[{ projectId }, "project-identity-memberships"] as const,
|
||||
getProjectIdentityMembershipDetails: (projectId: string, identityId: string) =>
|
||||
[{ projectId, identityId }, "project-identity-membership-details"] as const,
|
||||
getProjectIdentityMembershipDetailsV2: (projectId: string, identityId: string) =>
|
||||
[{ projectId, identityId }, "project-identity-membership-details"] as const,
|
||||
// allows invalidation using above key without knowing params
|
||||
getProjectIdentityMembershipsWithParams: ({ projectId, ...params }: TListProjectIdentitiesDTO) =>
|
||||
[...projectKeys.getProjectIdentityMemberships(projectId), params] as const,
|
||||
|
||||
@@ -138,24 +138,6 @@ export type TUpdateWorkspaceUserRoleDTO = {
|
||||
)[];
|
||||
};
|
||||
|
||||
export type TUpdateWorkspaceIdentityRoleDTO = {
|
||||
identityId: string;
|
||||
projectId: string;
|
||||
roles: (
|
||||
| {
|
||||
role: string;
|
||||
isTemporary?: false;
|
||||
}
|
||||
| {
|
||||
role: string;
|
||||
isTemporary: true;
|
||||
temporaryMode: ProjectUserMembershipTemporaryMode;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
)[];
|
||||
};
|
||||
|
||||
export type TUpdateWorkspaceGroupRoleDTO = {
|
||||
groupId: string;
|
||||
projectId: string;
|
||||
|
||||
@@ -59,6 +59,8 @@ export type TViewSharedSecretResponse = {
|
||||
tag: string;
|
||||
accessType: SecretSharingAccessType;
|
||||
orgName?: string;
|
||||
expiresAt?: Date | string;
|
||||
expiresAfterViews?: number | null;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,37 @@
|
||||
import { IdentityAuthMethod } from "@app/hooks/api";
|
||||
|
||||
export enum TemporaryPermissionMode {
|
||||
Relative = "relative"
|
||||
}
|
||||
|
||||
export type TMetadata = {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type TIdentity = {
|
||||
id: string;
|
||||
name: string;
|
||||
orgId: string;
|
||||
projectId: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
hasDeleteProtection: boolean;
|
||||
authMethods: IdentityAuthMethod[];
|
||||
activeLockoutAuthMethods: string[];
|
||||
metadata?: Array<TMetadata & { id: string }>;
|
||||
};
|
||||
|
||||
export type TRoles = Array<
|
||||
| {
|
||||
role: string;
|
||||
isTemporary?: false;
|
||||
}
|
||||
| {
|
||||
role: string;
|
||||
isTemporary: true;
|
||||
temporaryMode: TemporaryPermissionMode;
|
||||
temporaryRange: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
>;
|
||||
@@ -7,7 +7,8 @@ import { SubscriptionPlan } from "./types";
|
||||
// import { Workspace } from './types';
|
||||
|
||||
export const subscriptionQueryKeys = {
|
||||
getOrgSubsription: (orgID: string) => ["plan", { orgID }] as const
|
||||
all: () => ["plan"] as const,
|
||||
getOrgSubsription: (orgID: string) => [...subscriptionQueryKeys.all(), { orgID }] as const
|
||||
};
|
||||
|
||||
export const fetchOrgSubscription = async (orgID: string, refreshCache: boolean = false) => {
|
||||
|
||||
@@ -48,6 +48,7 @@ export type SubscriptionPlan = {
|
||||
gateway: boolean;
|
||||
externalKms: boolean;
|
||||
pkiEst: boolean;
|
||||
pkiAcme: boolean;
|
||||
pkiLegacyTemplates: boolean;
|
||||
enforceMfa: boolean;
|
||||
enforceGoogleSSO: boolean;
|
||||
|
||||
+3
-2
@@ -2,12 +2,13 @@ import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useProject, useProjectPermission } from "@app/context";
|
||||
import { useOrganization, useProject, useProjectPermission } from "@app/context";
|
||||
import { getProjectHomePage } from "@app/helpers/project";
|
||||
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
||||
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
||||
|
||||
export const AssumePrivilegeModeBanner = () => {
|
||||
const { isSubOrganization, currentOrg } = useOrganization();
|
||||
const { currentProject } = useProject();
|
||||
const exitAssumePrivilegeMode = useRemoveAssumeProjectPrivilege();
|
||||
const { assumedPrivilegeDetails } = useProjectPermission();
|
||||
@@ -36,7 +37,7 @@ export const AssumePrivilegeModeBanner = () => {
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
const url = getProjectHomePage(currentProject.type, currentProject.environments);
|
||||
const url = `${getProjectHomePage(currentProject.type, currentProject.environments)}${isSubOrganization ? `?subOrganization=${currentOrg.slug}` : ""}`;
|
||||
window.location.href = url.replace("$projectId", currentProject.id);
|
||||
}
|
||||
}
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faDownload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
Modal,
|
||||
ModalContent,
|
||||
Select,
|
||||
SelectItem
|
||||
} from "@app/components/v2";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["certificateExport"]>;
|
||||
handlePopUpToggle: (
|
||||
popUpName: keyof UsePopUpState<["certificateExport"]>,
|
||||
state?: boolean
|
||||
) => void;
|
||||
onFormatSelected: (
|
||||
format: "pem" | "pkcs12",
|
||||
serialNumber: string,
|
||||
options?: ExportOptions
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type CertificateExportFormat = "pem" | "pkcs12";
|
||||
|
||||
export type ExportOptions = {
|
||||
pkcs12?: {
|
||||
password: string;
|
||||
alias: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const CertificateExportModal = ({ popUp, handlePopUpToggle, onFormatSelected }: Props) => {
|
||||
const [selectedFormat, setSelectedFormat] = useState<CertificateExportFormat>("pem");
|
||||
const [pkcs12Options, setPkcs12Options] = useState({
|
||||
password: "",
|
||||
alias: ""
|
||||
});
|
||||
|
||||
const serialNumber =
|
||||
(popUp?.certificateExport?.data as { serialNumber: string })?.serialNumber || "";
|
||||
|
||||
// Reset form whenever the modal opens
|
||||
useEffect(() => {
|
||||
if (popUp?.certificateExport?.isOpen) {
|
||||
setSelectedFormat("pem");
|
||||
setPkcs12Options({
|
||||
password: "",
|
||||
alias: ""
|
||||
});
|
||||
}
|
||||
}, [popUp?.certificateExport?.isOpen]);
|
||||
|
||||
const isFormValid = () => {
|
||||
if (selectedFormat === "pkcs12") {
|
||||
return pkcs12Options.password.length >= 6 && pkcs12Options.alias.trim() !== "";
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
if (serialNumber && isFormValid()) {
|
||||
const options: ExportOptions = {};
|
||||
|
||||
if (selectedFormat === "pkcs12") {
|
||||
options.pkcs12 = pkcs12Options;
|
||||
}
|
||||
|
||||
onFormatSelected(selectedFormat, serialNumber, options);
|
||||
handlePopUpToggle("certificateExport", false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.certificateExport?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("certificateExport", isOpen);
|
||||
}}
|
||||
>
|
||||
<ModalContent title="Export Certificate">
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-gray-400">Choose the format for exporting your certificate</p>
|
||||
|
||||
<FormControl
|
||||
label="Export Format"
|
||||
helperText={
|
||||
selectedFormat === "pem"
|
||||
? "Privacy Enhanced Mail - Text-based certificate format"
|
||||
: "PKCS12 format - Binary keystore format compatible with Java applications"
|
||||
}
|
||||
>
|
||||
<Select
|
||||
className="w-full"
|
||||
value={selectedFormat}
|
||||
onValueChange={(value) => setSelectedFormat(value as CertificateExportFormat)}
|
||||
>
|
||||
<SelectItem value="pem">PEM Format</SelectItem>
|
||||
<SelectItem value="pkcs12">PKCS12 Format</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{selectedFormat === "pkcs12" && (
|
||||
<>
|
||||
<FormControl
|
||||
label="Keystore Password"
|
||||
helperText={
|
||||
pkcs12Options.password.length > 0 && pkcs12Options.password.length < 6
|
||||
? undefined
|
||||
: "Password to protect the PKCS12 keystore (minimum 6 characters)"
|
||||
}
|
||||
isError={pkcs12Options.password.length > 0 && pkcs12Options.password.length < 6}
|
||||
errorText="Password must be at least 6 characters long"
|
||||
>
|
||||
<Input
|
||||
placeholder="Enter keystore password"
|
||||
value={pkcs12Options.password}
|
||||
onChange={(e) =>
|
||||
setPkcs12Options((prev) => ({ ...prev, password: e.target.value }))
|
||||
}
|
||||
type="password"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
label="Certificate Alias"
|
||||
helperText="Friendly name for the certificate in the keystore"
|
||||
>
|
||||
<Input
|
||||
placeholder="Enter certificate alias"
|
||||
value={pkcs12Options.alias}
|
||||
onChange={(e) => setPkcs12Options((prev) => ({ ...prev, alias: e.target.value }))}
|
||||
/>
|
||||
</FormControl>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-4">
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
onClick={() => handlePopUpToggle("certificateExport", false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
leftIcon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={handleExport}
|
||||
disabled={!serialNumber || !isFormValid()}
|
||||
>
|
||||
Export {selectedFormat.toUpperCase()}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
+48
-1
@@ -9,10 +9,11 @@ import {
|
||||
ProjectPermissionSub,
|
||||
useProject
|
||||
} from "@app/context";
|
||||
import { useDeleteCert } from "@app/hooks/api";
|
||||
import { useDeleteCert, useDownloadCertPkcs12 } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { CertificateCertModal } from "./CertificateCertModal";
|
||||
import { CertificateExportModal, ExportOptions } from "./CertificateExportModal";
|
||||
import { CertificateImportModal } from "./CertificateImportModal";
|
||||
import { CertificateIssuanceModal } from "./CertificateIssuanceModal";
|
||||
import { CertificateManagePkiSyncsModal } from "./CertificateManagePkiSyncsModal";
|
||||
@@ -24,11 +25,13 @@ import { CertificatesTable } from "./CertificatesTable";
|
||||
export const CertificatesSection = () => {
|
||||
const { currentProject } = useProject();
|
||||
const { mutateAsync: deleteCert } = useDeleteCert();
|
||||
const { mutateAsync: downloadCertPkcs12 } = useDownloadCertPkcs12();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"issueCertificate",
|
||||
"certificateImport",
|
||||
"certificateCert",
|
||||
"certificateExport",
|
||||
"deleteCertificate",
|
||||
"revokeCertificate",
|
||||
"manageRenewal",
|
||||
@@ -49,6 +52,45 @@ export const CertificatesSection = () => {
|
||||
handlePopUpClose("deleteCertificate");
|
||||
};
|
||||
|
||||
const handleCertificateExport = async (
|
||||
format: "pem" | "pkcs12",
|
||||
serialNumber: string,
|
||||
options?: ExportOptions
|
||||
) => {
|
||||
if (format === "pem") {
|
||||
handlePopUpOpen("certificateCert", { serialNumber });
|
||||
} else if (format === "pkcs12") {
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
if (!options?.pkcs12?.password || !options?.pkcs12?.alias) {
|
||||
createNotification({
|
||||
text: "Password and alias are required for PKCS12 export",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await downloadCertPkcs12({
|
||||
serialNumber,
|
||||
projectSlug: currentProject.slug,
|
||||
password: options.pkcs12.password,
|
||||
alias: options.pkcs12.alias
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "PKCS12 certificate downloaded successfully",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error: any) {
|
||||
createNotification({
|
||||
text: error?.message || "Failed to download PKCS12 certificate",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-4 flex justify-between">
|
||||
@@ -84,6 +126,11 @@ export const CertificatesSection = () => {
|
||||
<CertificateIssuanceModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CertificateImportModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CertificateCertModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CertificateExportModal
|
||||
popUp={popUp}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
onFormatSelected={handleCertificateExport}
|
||||
/>
|
||||
<CertificateManageRenewalModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CertificateRenewalModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CertificateRevocationModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
|
||||
@@ -68,6 +68,7 @@ type Props = {
|
||||
"deleteCertificate",
|
||||
"revokeCertificate",
|
||||
"certificateCert",
|
||||
"certificateExport",
|
||||
"manageRenewal",
|
||||
"renewCertificate",
|
||||
"managePkiSyncs"
|
||||
@@ -275,7 +276,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () =>
|
||||
handlePopUpOpen("certificateCert", {
|
||||
handlePopUpOpen("certificateExport", {
|
||||
serialNumber: certificate.serialNumber
|
||||
})
|
||||
}
|
||||
|
||||
+15
-1
@@ -2,6 +2,7 @@ import { useState } from "react";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { useProjectPermission } from "@app/context";
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
TCertificateProfileWithDetails,
|
||||
useDeleteCertificateProfile
|
||||
@@ -29,6 +31,7 @@ export const CertificateProfilesTab = () => {
|
||||
const [selectedProfile, setSelectedProfile] = useState<TCertificateProfileWithDetails | null>(
|
||||
null
|
||||
);
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||
|
||||
const deleteProfile = useDeleteCertificateProfile();
|
||||
|
||||
@@ -99,7 +102,17 @@ export const CertificateProfilesTab = () => {
|
||||
onDeleteProfile={handleDeleteProfile}
|
||||
/>
|
||||
|
||||
<CreateProfileModal isOpen={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} />
|
||||
<CreateProfileModal
|
||||
isOpen={isCreateModalOpen}
|
||||
onClose={() => setIsCreateModalOpen(false)}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
|
||||
text="Your current plan does not include access to managing template enrollment options for ACME. To unlock this feature, please upgrade to Infisical Enterprise plan."
|
||||
/>
|
||||
|
||||
{selectedProfile && (
|
||||
<>
|
||||
@@ -109,6 +122,7 @@ export const CertificateProfilesTab = () => {
|
||||
setIsEditModalOpen(false);
|
||||
setSelectedProfile(null);
|
||||
}}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
profile={selectedProfile}
|
||||
mode="edit"
|
||||
/>
|
||||
|
||||
+26
-4
@@ -18,8 +18,7 @@ import {
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { envConfig } from "@app/config/env";
|
||||
import { useProject } from "@app/context";
|
||||
import { useProject, useSubscription } from "@app/context";
|
||||
import { useListCasByProjectId } from "@app/hooks/api/ca/queries";
|
||||
import {
|
||||
TCertificateProfileWithDetails,
|
||||
@@ -29,6 +28,7 @@ import {
|
||||
useUpdateCertificateProfile
|
||||
} from "@app/hooks/api/certificateProfiles";
|
||||
import { useListCertificateTemplatesV2 } from "@app/hooks/api/certificateTemplates/queries";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const createSchema = z
|
||||
.object({
|
||||
@@ -151,12 +151,25 @@ export type FormData = z.infer<typeof createSchema>;
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
handlePopUpOpen: (
|
||||
popUpName: keyof UsePopUpState<["upgradePlan"]>,
|
||||
data?: {
|
||||
isEnterpriseFeature?: boolean;
|
||||
}
|
||||
) => void;
|
||||
profile?: TCertificateProfileWithDetails;
|
||||
mode?: "create" | "edit";
|
||||
}
|
||||
|
||||
export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }: Props) => {
|
||||
export const CreateProfileModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
handlePopUpOpen,
|
||||
profile,
|
||||
mode = "create"
|
||||
}: Props) => {
|
||||
const { currentProject } = useProject();
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { data: caData } = useListCasByProjectId(currentProject?.id || "");
|
||||
const { data: templateData } = useListCertificateTemplatesV2({
|
||||
@@ -248,6 +261,15 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
}, [isEdit, profile, reset]);
|
||||
|
||||
const onFormSubmit = async (data: FormData) => {
|
||||
if (!isEdit && !subscription?.pkiAcme && data.enrollmentType === "acme") {
|
||||
reset();
|
||||
onClose();
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
isEnterpriseFeature: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentProject?.id && !isEdit) return;
|
||||
|
||||
if (isEdit) {
|
||||
@@ -467,7 +489,7 @@ export const CreateProfileModal = ({ isOpen, onClose, profile, mode = "create" }
|
||||
>
|
||||
<SelectItem value="api">API</SelectItem>
|
||||
<SelectItem value="est">EST</SelectItem>
|
||||
{envConfig.ACME_FEATURE_ENABLED && <SelectItem value="acme">ACME</SelectItem>}
|
||||
<SelectItem value="acme">ACME</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
+7
-2
@@ -10,7 +10,7 @@ import {
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { useDeleteCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/mutations";
|
||||
import { TCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/types";
|
||||
import { type TCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/types";
|
||||
|
||||
import { CreateTemplateModal } from "./CreateTemplateModal";
|
||||
import { TemplateList } from "./TemplateList";
|
||||
@@ -84,7 +84,12 @@ export const CertificateTemplatesV2Tab = () => {
|
||||
|
||||
<TemplateList onEditTemplate={handleEditTemplate} onDeleteTemplate={handleDeleteTemplate} />
|
||||
|
||||
<CreateTemplateModal isOpen={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} />
|
||||
<CreateTemplateModal
|
||||
isOpen={isCreateModalOpen}
|
||||
onClose={() => {
|
||||
setIsCreateModalOpen(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
{selectedTemplate && (
|
||||
<>
|
||||
|
||||
+157
-32
@@ -36,13 +36,18 @@ import {
|
||||
CertDurationUnit,
|
||||
CertExtendedKeyUsageType,
|
||||
CertKeyUsageType,
|
||||
CertSanInclude,
|
||||
CertSubjectAlternativeNameType,
|
||||
CertSubjectAttributeInclude,
|
||||
CertSubjectAttributeType,
|
||||
SAN_INCLUDE_OPTIONS,
|
||||
SAN_TYPE_OPTIONS,
|
||||
SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS,
|
||||
SUBJECT_ATTRIBUTE_TYPE_OPTIONS
|
||||
SUBJECT_ATTRIBUTE_TYPE_OPTIONS,
|
||||
TEMPLATE_PRESET_IDS,
|
||||
type TemplatePresetId
|
||||
} from "./shared/certificate-constants";
|
||||
import { CERTIFICATE_TEMPLATE_PRESETS } from "./shared/template-presets";
|
||||
import { KeyUsagesSection, TemplateFormData, templateSchema } from "./shared";
|
||||
|
||||
export type FormData = TemplateFormData;
|
||||
@@ -91,7 +96,7 @@ const SIGNATURE_ALGORITHMS = [
|
||||
"SHA256-ECDSA",
|
||||
"SHA384-ECDSA",
|
||||
"SHA512-ECDSA"
|
||||
];
|
||||
] as const;
|
||||
|
||||
const KEY_ALGORITHMS = [
|
||||
"RSA-2048",
|
||||
@@ -100,7 +105,7 @@ const KEY_ALGORITHMS = [
|
||||
"ECDSA-P256",
|
||||
"ECDSA-P384",
|
||||
"ECDSA-P521"
|
||||
];
|
||||
] as const;
|
||||
|
||||
export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create" }: Props) => {
|
||||
const { currentProject } = useProject();
|
||||
@@ -117,7 +122,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
subj.allowed.forEach((allowedValue) => {
|
||||
attributes.push({
|
||||
type: subj.type as CertSubjectAttributeType,
|
||||
include: "optional",
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: [allowedValue]
|
||||
});
|
||||
});
|
||||
@@ -126,7 +131,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
subj.denied.forEach((deniedValue) => {
|
||||
attributes.push({
|
||||
type: subj.type as CertSubjectAttributeType,
|
||||
include: "prohibit",
|
||||
include: CertSubjectAttributeInclude.PROHIBIT,
|
||||
value: [deniedValue]
|
||||
});
|
||||
});
|
||||
@@ -141,7 +146,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
san.required.forEach((requiredValue) => {
|
||||
subjectAlternativeNames.push({
|
||||
type: san.type as CertSubjectAlternativeNameType,
|
||||
include: "mandatory",
|
||||
include: CertSanInclude.MANDATORY,
|
||||
value: [requiredValue]
|
||||
});
|
||||
});
|
||||
@@ -150,7 +155,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
san.allowed.forEach((allowedValue) => {
|
||||
subjectAlternativeNames.push({
|
||||
type: san.type as CertSubjectAlternativeNameType,
|
||||
include: "optional",
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: [allowedValue]
|
||||
});
|
||||
});
|
||||
@@ -159,7 +164,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
san.denied.forEach((deniedValue) => {
|
||||
subjectAlternativeNames.push({
|
||||
type: san.type as CertSubjectAlternativeNameType,
|
||||
include: "prohibit",
|
||||
include: CertSanInclude.PROHIBIT,
|
||||
value: [deniedValue]
|
||||
});
|
||||
});
|
||||
@@ -219,6 +224,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
};
|
||||
|
||||
return {
|
||||
preset: TEMPLATE_PRESET_IDS.CUSTOM,
|
||||
name: templateData.name || "",
|
||||
description: templateData.description || "",
|
||||
attributes,
|
||||
@@ -231,25 +237,30 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
};
|
||||
};
|
||||
|
||||
const getDefaultValues = (): FormData => ({
|
||||
name: "",
|
||||
description: "",
|
||||
attributes: [],
|
||||
keyUsages: { requiredUsages: [], optionalUsages: [] },
|
||||
extendedKeyUsages: { requiredUsages: [], optionalUsages: [] },
|
||||
subjectAlternativeNames: [],
|
||||
validity: {
|
||||
maxDuration: { value: 365, unit: CertDurationUnit.DAYS }
|
||||
},
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: []
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: []
|
||||
}
|
||||
});
|
||||
const getDefaultValues = (): FormData & { preset: TemplatePresetId } => {
|
||||
return {
|
||||
preset: TEMPLATE_PRESET_IDS.CUSTOM,
|
||||
name: "",
|
||||
description: "",
|
||||
attributes: [],
|
||||
keyUsages: { requiredUsages: [], optionalUsages: [] },
|
||||
extendedKeyUsages: { requiredUsages: [], optionalUsages: [] },
|
||||
subjectAlternativeNames: [],
|
||||
validity: {
|
||||
maxDuration: { value: 365, unit: CertDurationUnit.DAYS }
|
||||
},
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: []
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const { control, handleSubmit, reset, watch, setValue, formState } = useForm<FormData>({
|
||||
const { control, handleSubmit, reset, watch, setValue, formState } = useForm<
|
||||
FormData & { preset: TemplatePresetId }
|
||||
>({
|
||||
resolver: zodResolver(templateSchema),
|
||||
defaultValues: getDefaultValues(),
|
||||
mode: "onChange",
|
||||
@@ -260,7 +271,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
useEffect(() => {
|
||||
if (isEdit && template) {
|
||||
const convertedData = convertApiToUiFormat(template);
|
||||
reset(convertedData);
|
||||
reset({ ...convertedData, preset: TEMPLATE_PRESET_IDS.CUSTOM });
|
||||
} else if (!isEdit) {
|
||||
reset(getDefaultValues());
|
||||
}
|
||||
@@ -273,6 +284,37 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
requiredUsages: [],
|
||||
optionalUsages: []
|
||||
};
|
||||
const watchedPreset = watch("preset") || TEMPLATE_PRESET_IDS.CUSTOM;
|
||||
|
||||
const handlePresetChange = (presetId: TemplatePresetId) => {
|
||||
setValue("preset", presetId);
|
||||
|
||||
if (presetId === TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPreset = CERTIFICATE_TEMPLATE_PRESETS.find((p) => p.id === presetId);
|
||||
if (selectedPreset) {
|
||||
if (selectedPreset.formData.keyUsages) {
|
||||
setValue("keyUsages", selectedPreset.formData.keyUsages);
|
||||
}
|
||||
if (selectedPreset.formData.extendedKeyUsages) {
|
||||
setValue("extendedKeyUsages", selectedPreset.formData.extendedKeyUsages);
|
||||
}
|
||||
if (selectedPreset.formData.attributes) {
|
||||
setValue("attributes", selectedPreset.formData.attributes);
|
||||
}
|
||||
if (selectedPreset.formData.subjectAlternativeNames) {
|
||||
setValue("subjectAlternativeNames", selectedPreset.formData.subjectAlternativeNames);
|
||||
}
|
||||
if (selectedPreset.formData.signatureAlgorithm) {
|
||||
setValue("signatureAlgorithm", selectedPreset.formData.signatureAlgorithm);
|
||||
}
|
||||
if (selectedPreset.formData.keyAlgorithm) {
|
||||
setValue("keyAlgorithm", selectedPreset.formData.keyAlgorithm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const consolidateByType = <
|
||||
T extends { type: string; allowed?: string[]; required?: string[]; denied?: string[] }
|
||||
@@ -309,9 +351,17 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
data.attributes?.map((attr) => {
|
||||
const result: AttributeTransform = { type: attr.type };
|
||||
|
||||
if (attr.include === "optional" && attr.value && attr.value.length > 0) {
|
||||
if (
|
||||
attr.include === CertSubjectAttributeInclude.OPTIONAL &&
|
||||
attr.value &&
|
||||
attr.value.length > 0
|
||||
) {
|
||||
result.allowed = attr.value;
|
||||
} else if (attr.include === "prohibit" && attr.value && attr.value.length > 0) {
|
||||
} else if (
|
||||
attr.include === CertSubjectAttributeInclude.PROHIBIT &&
|
||||
attr.value &&
|
||||
attr.value.length > 0
|
||||
) {
|
||||
result.denied = attr.value;
|
||||
}
|
||||
|
||||
@@ -322,11 +372,11 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
data.subjectAlternativeNames?.map((san) => {
|
||||
const result: SanTransform = { type: san.type };
|
||||
|
||||
if (san.include === "mandatory" && san.value && san.value.length > 0) {
|
||||
if (san.include === CertSanInclude.MANDATORY && san.value && san.value.length > 0) {
|
||||
result.required = san.value;
|
||||
} else if (san.include === "optional" && san.value && san.value.length > 0) {
|
||||
} else if (san.include === CertSanInclude.OPTIONAL && san.value && san.value.length > 0) {
|
||||
result.allowed = san.value;
|
||||
} else if (san.include === "prohibit" && san.value && san.value.length > 0) {
|
||||
} else if (san.include === CertSanInclude.PROHIBIT && san.value && san.value.length > 0) {
|
||||
result.denied = san.value;
|
||||
}
|
||||
|
||||
@@ -464,11 +514,19 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
value: ["*"]
|
||||
};
|
||||
setValue("attributes", [...watchedAttributes, newAttribute]);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAttribute = (index: number) => {
|
||||
const newAttributes = watchedAttributes.filter((_, i) => i !== index);
|
||||
setValue("attributes", newAttributes);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
const addSan = () => {
|
||||
@@ -478,11 +536,19 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
value: ["*"]
|
||||
};
|
||||
setValue("subjectAlternativeNames", [...watchedSans, newSan]);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
const removeSan = (index: number) => {
|
||||
const newSans = watchedSans.filter((_, i) => i !== index);
|
||||
setValue("subjectAlternativeNames", newSans);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyUsagesChange = (usages: {
|
||||
@@ -493,6 +559,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
requiredUsages: usages.requiredUsages,
|
||||
optionalUsages: usages.optionalUsages
|
||||
});
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
const handleExtendedKeyUsagesChange = (usages: {
|
||||
@@ -503,6 +573,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
requiredUsages: usages.requiredUsages,
|
||||
optionalUsages: usages.optionalUsages
|
||||
});
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -555,6 +629,33 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="preset"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Template Preset"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={handlePresetChange}
|
||||
className="w-full"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
<SelectItem value={TEMPLATE_PRESET_IDS.CUSTOM}>Custom</SelectItem>
|
||||
{CERTIFICATE_TEMPLATE_PRESETS.map((preset) => (
|
||||
<SelectItem key={preset.id} value={preset.id}>
|
||||
{preset.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<AccordionItem value="attributes">
|
||||
<AccordionTrigger>Subject Attributes</AccordionTrigger>
|
||||
@@ -595,6 +696,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
type: value as CertSubjectAttributeType
|
||||
};
|
||||
setValue("attributes", newAttributes);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className="w-48"
|
||||
>
|
||||
@@ -615,6 +720,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
value as (typeof SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS)[number]
|
||||
};
|
||||
setValue("attributes", newAttributes);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className="w-32"
|
||||
>
|
||||
@@ -635,6 +744,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
value: e.target.value.trim() ? [e.target.value.trim()] : []
|
||||
};
|
||||
setValue("attributes", newAttributes);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className={`flex-1 ${
|
||||
attr.value && attr.value.length > 0 && attr.value[0] === ""
|
||||
@@ -701,6 +814,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
type: value as CertSubjectAlternativeNameType
|
||||
};
|
||||
setValue("subjectAlternativeNames", newSans);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className="w-36"
|
||||
>
|
||||
@@ -720,6 +837,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
include: value as (typeof SAN_INCLUDE_OPTIONS)[number]
|
||||
};
|
||||
setValue("subjectAlternativeNames", newSans);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className="w-32"
|
||||
>
|
||||
@@ -740,6 +861,10 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
||||
value: e.target.value.trim() ? [e.target.value.trim()] : []
|
||||
};
|
||||
setValue("subjectAlternativeNames", newSans);
|
||||
|
||||
if (watchedPreset !== TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||
setValue("preset", TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
}
|
||||
}}
|
||||
className={`flex-1 ${
|
||||
san.value && san.value.length > 0 && san.value[0] === ""
|
||||
|
||||
+37
-2
@@ -150,8 +150,19 @@ export const SUBJECT_ATTRIBUTE_TYPE_OPTIONS = Object.values(CertSubjectAttribute
|
||||
export const ATTRIBUTE_RULE_OPTIONS = Object.values(CertAttributeRule);
|
||||
export const SAN_EFFECT_OPTIONS = Object.values(CertSanEffect);
|
||||
|
||||
export const SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS = ["optional", "prohibit"] as const;
|
||||
export const SAN_INCLUDE_OPTIONS = ["mandatory", "optional", "prohibit"] as const;
|
||||
export enum CertSubjectAttributeInclude {
|
||||
OPTIONAL = "optional",
|
||||
PROHIBIT = "prohibit"
|
||||
}
|
||||
|
||||
export enum CertSanInclude {
|
||||
MANDATORY = "mandatory",
|
||||
OPTIONAL = "optional",
|
||||
PROHIBIT = "prohibit"
|
||||
}
|
||||
|
||||
export const SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS = Object.values(CertSubjectAttributeInclude);
|
||||
export const SAN_INCLUDE_OPTIONS = Object.values(CertSanInclude);
|
||||
|
||||
export const USAGE_STATES = {
|
||||
REQUIRED: "required",
|
||||
@@ -239,3 +250,27 @@ export const mapTemplateKeyAlgorithmToApi = (templateFormat: string): string =>
|
||||
};
|
||||
return mapping[templateFormat] || templateFormat;
|
||||
};
|
||||
|
||||
export const TEMPLATE_PRESET_IDS = {
|
||||
CUSTOM: "custom",
|
||||
TLS_SERVER: "tls-server",
|
||||
TLS_CLIENT: "tls-client",
|
||||
CODE_SIGNING: "code-signing",
|
||||
DEVICE: "device",
|
||||
USER: "user",
|
||||
EMAIL_PROTECTION: "email-protection",
|
||||
DUAL_PURPOSE_SERVER: "dual-purpose-server"
|
||||
} as const;
|
||||
|
||||
export type TemplatePresetId = (typeof TEMPLATE_PRESET_IDS)[keyof typeof TEMPLATE_PRESET_IDS];
|
||||
|
||||
export const ALGORITHM_FAMILIES = {
|
||||
ECDSA: {
|
||||
signature: ["SHA256-ECDSA", "SHA384-ECDSA", "SHA512-ECDSA"] as const,
|
||||
key: ["ECDSA-P256", "ECDSA-P384", "ECDSA-P521"] as const
|
||||
},
|
||||
RSA: {
|
||||
signature: ["SHA256-RSA", "SHA384-RSA", "SHA512-RSA"] as const,
|
||||
key: ["RSA-2048", "RSA-3072", "RSA-4096"] as const
|
||||
}
|
||||
} as const;
|
||||
|
||||
+19
-4
@@ -4,21 +4,22 @@ import {
|
||||
CertDurationUnit,
|
||||
CertExtendedKeyUsageType,
|
||||
CertKeyUsageType,
|
||||
CertSanInclude,
|
||||
CertSubjectAlternativeNameType,
|
||||
CertSubjectAttributeInclude,
|
||||
CertSubjectAttributeType,
|
||||
SAN_INCLUDE_OPTIONS,
|
||||
SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS
|
||||
TEMPLATE_PRESET_IDS
|
||||
} from "./certificate-constants";
|
||||
|
||||
export const uiAttributeSchema = z.object({
|
||||
type: z.nativeEnum(CertSubjectAttributeType),
|
||||
include: z.enum(SUBJECT_ATTRIBUTE_INCLUDE_OPTIONS),
|
||||
include: z.nativeEnum(CertSubjectAttributeInclude),
|
||||
value: z.array(z.string().min(1, "Value cannot be empty"))
|
||||
});
|
||||
|
||||
export const uiSanSchema = z.object({
|
||||
type: z.nativeEnum(CertSubjectAlternativeNameType),
|
||||
include: z.enum(SAN_INCLUDE_OPTIONS),
|
||||
include: z.nativeEnum(CertSanInclude),
|
||||
value: z.array(z.string().min(1, "Value cannot be empty"))
|
||||
});
|
||||
|
||||
@@ -51,7 +52,21 @@ export const uiKeyAlgorithmSchema = z.object({
|
||||
.min(1, "At least one key type must be selected")
|
||||
});
|
||||
|
||||
export const uiPresetSchema = z
|
||||
.enum([
|
||||
TEMPLATE_PRESET_IDS.CUSTOM,
|
||||
TEMPLATE_PRESET_IDS.TLS_SERVER,
|
||||
TEMPLATE_PRESET_IDS.TLS_CLIENT,
|
||||
TEMPLATE_PRESET_IDS.CODE_SIGNING,
|
||||
TEMPLATE_PRESET_IDS.DEVICE,
|
||||
TEMPLATE_PRESET_IDS.USER,
|
||||
TEMPLATE_PRESET_IDS.EMAIL_PROTECTION,
|
||||
TEMPLATE_PRESET_IDS.DUAL_PURPOSE_SERVER
|
||||
])
|
||||
.default(TEMPLATE_PRESET_IDS.CUSTOM);
|
||||
|
||||
export const templateSchema = z.object({
|
||||
preset: uiPresetSchema,
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
import {
|
||||
ALGORITHM_FAMILIES,
|
||||
CertDurationUnit,
|
||||
CertExtendedKeyUsageType,
|
||||
CertKeyUsageType,
|
||||
CertSanInclude,
|
||||
CertSubjectAlternativeNameType,
|
||||
CertSubjectAttributeInclude,
|
||||
CertSubjectAttributeType,
|
||||
TEMPLATE_PRESET_IDS,
|
||||
type TemplatePresetId
|
||||
} from "./certificate-constants";
|
||||
import { TemplateFormData } from ".";
|
||||
|
||||
export interface CertificateTemplatePreset {
|
||||
readonly id: TemplatePresetId;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly useCase: string;
|
||||
readonly formData: Omit<TemplateFormData, "preset">;
|
||||
}
|
||||
|
||||
export const CERTIFICATE_TEMPLATE_PRESETS: CertificateTemplatePreset[] = [
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.TLS_SERVER,
|
||||
name: "TLS Server Certificate",
|
||||
description: "Standard TLS/SSL server certificate for HTTPS services and API endpoints.",
|
||||
useCase: "Web servers, API endpoints, HTTPS services",
|
||||
formData: {
|
||||
name: "TLS Server Certificate",
|
||||
description: "Standard TLS/SSL server certificate for HTTPS services and API endpoints.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.KEY_ENCIPHERMENT]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [CertExtendedKeyUsageType.SERVER_AUTH],
|
||||
optionalUsages: [CertExtendedKeyUsageType.SERVER_AUTH]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.DNS_NAME,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
},
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.IP_ADDRESS,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.ECDSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.ECDSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.TLS_CLIENT,
|
||||
name: "TLS Client Certificate",
|
||||
description: "Client certificate for mutual TLS authentication and API access.",
|
||||
useCase: "Client authentication, mTLS, API authentication",
|
||||
formData: {
|
||||
name: "TLS Client Certificate",
|
||||
description: "Client certificate for mutual TLS authentication and API access.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.KEY_AGREEMENT]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [CertExtendedKeyUsageType.CLIENT_AUTH],
|
||||
optionalUsages: [CertExtendedKeyUsageType.CLIENT_AUTH]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.EMAIL,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
},
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.DNS_NAME,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.ECDSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.ECDSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.CODE_SIGNING,
|
||||
name: "Code Signing Certificate",
|
||||
description:
|
||||
"Certificate for signing software, executables, and packages. Requires hardware security modules.",
|
||||
useCase: "Software signing, executable authentication, package validation",
|
||||
formData: {
|
||||
name: "Code Signing Certificate",
|
||||
description:
|
||||
"Certificate for signing software, executables, and packages. Requires hardware security modules.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.NON_REPUDIATION],
|
||||
optionalUsages: [CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.NON_REPUDIATION]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [CertExtendedKeyUsageType.CODE_SIGNING],
|
||||
optionalUsages: [
|
||||
CertExtendedKeyUsageType.CODE_SIGNING,
|
||||
CertExtendedKeyUsageType.TIME_STAMPING
|
||||
]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.EMAIL,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.RSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.RSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.DEVICE,
|
||||
name: "Device Certificate",
|
||||
description:
|
||||
"Certificate for IoT devices and embedded systems authentication. IEEE 802.1AR compliant.",
|
||||
useCase: "Device authentication, IoT security, embedded systems",
|
||||
formData: {
|
||||
name: "Device Certificate",
|
||||
description:
|
||||
"Certificate for IoT devices and embedded systems authentication. IEEE 802.1AR compliant.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [CertKeyUsageType.DIGITAL_SIGNATURE, CertKeyUsageType.KEY_AGREEMENT]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [CertExtendedKeyUsageType.CLIENT_AUTH],
|
||||
optionalUsages: [CertExtendedKeyUsageType.CLIENT_AUTH, CertExtendedKeyUsageType.SERVER_AUTH]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.DNS_NAME,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
},
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.IP_ADDRESS,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.ECDSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.ECDSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.USER,
|
||||
name: "User Certificate",
|
||||
description:
|
||||
"Personal certificate for user authentication and email signing. FIPS 201 PIV compliant.",
|
||||
useCase: "Personal authentication, smart cards, email protection",
|
||||
formData: {
|
||||
name: "User Certificate",
|
||||
description:
|
||||
"Personal certificate for user authentication and email signing. FIPS 201 PIV compliant.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [
|
||||
CertKeyUsageType.DIGITAL_SIGNATURE,
|
||||
CertKeyUsageType.KEY_ENCIPHERMENT,
|
||||
CertKeyUsageType.KEY_AGREEMENT
|
||||
]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [
|
||||
CertExtendedKeyUsageType.CLIENT_AUTH,
|
||||
CertExtendedKeyUsageType.EMAIL_PROTECTION
|
||||
],
|
||||
optionalUsages: [
|
||||
CertExtendedKeyUsageType.CLIENT_AUTH,
|
||||
CertExtendedKeyUsageType.EMAIL_PROTECTION
|
||||
]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.EMAIL,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.ECDSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.ECDSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.EMAIL_PROTECTION,
|
||||
name: "Email Protection Certificate",
|
||||
description: "S/MIME certificate for email encryption and digital signing. RFC 8550 compliant.",
|
||||
useCase: "Email encryption, digital signing, secure messaging",
|
||||
formData: {
|
||||
name: "Email Protection Certificate",
|
||||
description:
|
||||
"S/MIME certificate for email encryption and digital signing. RFC 8550 compliant.",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [
|
||||
CertKeyUsageType.DIGITAL_SIGNATURE,
|
||||
CertKeyUsageType.KEY_ENCIPHERMENT,
|
||||
CertKeyUsageType.KEY_AGREEMENT
|
||||
]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [CertExtendedKeyUsageType.EMAIL_PROTECTION],
|
||||
optionalUsages: [CertExtendedKeyUsageType.EMAIL_PROTECTION]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.EMAIL,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.RSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.RSA.key]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: TEMPLATE_PRESET_IDS.DUAL_PURPOSE_SERVER,
|
||||
name: "Dual-Purpose Server Certificate",
|
||||
description:
|
||||
"Certificate for services requiring both server and client authentication capabilities",
|
||||
useCase: "Microservices, service mesh, dual authentication",
|
||||
formData: {
|
||||
name: "Dual-Purpose Server Certificate",
|
||||
description:
|
||||
"Certificate for services requiring both server and client authentication capabilities",
|
||||
keyUsages: {
|
||||
requiredUsages: [CertKeyUsageType.DIGITAL_SIGNATURE],
|
||||
optionalUsages: [
|
||||
CertKeyUsageType.DIGITAL_SIGNATURE,
|
||||
CertKeyUsageType.KEY_ENCIPHERMENT,
|
||||
CertKeyUsageType.KEY_AGREEMENT
|
||||
]
|
||||
},
|
||||
extendedKeyUsages: {
|
||||
requiredUsages: [
|
||||
CertExtendedKeyUsageType.SERVER_AUTH,
|
||||
CertExtendedKeyUsageType.CLIENT_AUTH
|
||||
],
|
||||
optionalUsages: [CertExtendedKeyUsageType.SERVER_AUTH, CertExtendedKeyUsageType.CLIENT_AUTH]
|
||||
},
|
||||
validity: {
|
||||
maxDuration: {
|
||||
value: 365,
|
||||
unit: CertDurationUnit.DAYS
|
||||
}
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
type: CertSubjectAttributeType.COMMON_NAME,
|
||||
include: CertSubjectAttributeInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
subjectAlternativeNames: [
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.DNS_NAME,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
},
|
||||
{
|
||||
type: CertSubjectAlternativeNameType.IP_ADDRESS,
|
||||
include: CertSanInclude.OPTIONAL,
|
||||
value: ["*"]
|
||||
}
|
||||
],
|
||||
signatureAlgorithm: {
|
||||
allowedAlgorithms: [...ALGORITHM_FAMILIES.ECDSA.signature]
|
||||
},
|
||||
keyAlgorithm: {
|
||||
allowedKeyTypes: [...ALGORITHM_FAMILIES.ECDSA.key]
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
+6
-3
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -76,7 +77,9 @@ export const IdentityAliCloudAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityAliCloudAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAliCloudAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -144,7 +147,7 @@ export const IdentityAliCloudAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
allowedArns,
|
||||
identityId,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
@@ -154,7 +157,7 @@ export const IdentityAliCloudAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
allowedArns,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
|
||||
+7
-6
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -78,7 +79,9 @@ export const IdentityAwsAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -154,7 +157,7 @@ export const IdentityAwsAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
stsEndpoint,
|
||||
allowedPrincipalArns,
|
||||
allowedAccountIds,
|
||||
@@ -166,7 +169,7 @@ export const IdentityAwsAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
stsEndpoint: stsEndpoint || "",
|
||||
allowedPrincipalArns: allowedPrincipalArns || "",
|
||||
@@ -176,10 +179,8 @@ export const IdentityAwsAuthForm = ({
|
||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||
accessTokenTrustedIps
|
||||
});
|
||||
handlePopUpToggle("identityAuthMethod", false);
|
||||
}
|
||||
|
||||
handlePopUpToggle("identityAuthMethod", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
|
||||
+6
-3
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -73,7 +74,9 @@ export const IdentityAzureAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -150,7 +153,7 @@ export const IdentityAzureAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
tenantId,
|
||||
resource,
|
||||
@@ -162,7 +165,7 @@ export const IdentityAzureAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
tenantId: tenantId || "",
|
||||
resource: resource || "",
|
||||
|
||||
+6
-4
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -76,7 +77,9 @@ export const IdentityGcpAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -160,7 +163,7 @@ export const IdentityGcpAuthForm = ({
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
identityId,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
type,
|
||||
allowedServiceAccounts,
|
||||
allowedProjects,
|
||||
@@ -173,7 +176,7 @@ export const IdentityGcpAuthForm = ({
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
identityId,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
type,
|
||||
allowedServiceAccounts: allowedServiceAccounts || "",
|
||||
allowedProjects: allowedProjects || "",
|
||||
@@ -191,7 +194,6 @@ export const IdentityGcpAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+6
-4
@@ -4,6 +4,7 @@ import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -109,7 +110,9 @@ export const IdentityJwtAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityJwtAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityJwtAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -227,7 +230,7 @@ export const IdentityJwtAuthForm = ({
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
identityId,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
configurationType,
|
||||
jwksUrl,
|
||||
jwksCaCert,
|
||||
@@ -252,7 +255,7 @@ export const IdentityJwtAuthForm = ({
|
||||
boundAudiences,
|
||||
boundClaims: Object.fromEntries(boundClaims.map((entry) => [entry.key, entry.value])),
|
||||
boundSubject,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||
@@ -266,7 +269,6 @@ export const IdentityJwtAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+6
-3
@@ -4,6 +4,7 @@ import { faInfoCircle, faPlus, faXmark } from "@fortawesome/free-solid-svg-icons
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -117,7 +118,9 @@ export const IdentityKubernetesAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -318,7 +321,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api
|
||||
? {
|
||||
kubernetesHost: kubernetesHost || ""
|
||||
@@ -341,7 +344,7 @@ export const IdentityKubernetesAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
...(tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Api
|
||||
? {
|
||||
|
||||
+5
-2
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faQuestionCircle, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -168,7 +169,9 @@ export const IdentityLdapAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityLdapAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityLdapAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -345,7 +348,7 @@ export const IdentityLdapAuthForm = ({
|
||||
ms(`${lockoutCounterResetValue}${lockoutCounterResetUnit}`) / 1000;
|
||||
|
||||
const basePayload = {
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
searchFilter,
|
||||
ldapCaCertificate,
|
||||
|
||||
+6
-4
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -84,7 +85,9 @@ export const IdentityOciAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityOciAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOciAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -156,7 +159,7 @@ export const IdentityOciAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
tenancyOcid,
|
||||
allowedUsernames,
|
||||
identityId,
|
||||
@@ -167,7 +170,7 @@ export const IdentityOciAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
tenancyOcid,
|
||||
allowedUsernames: allowedUsernames || undefined,
|
||||
@@ -184,7 +187,6 @@ export const IdentityOciAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+6
-4
@@ -4,6 +4,7 @@ import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -96,7 +97,9 @@ export const IdentityOidcAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -211,7 +214,7 @@ export const IdentityOidcAuthForm = ({
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
identityId,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
oidcDiscoveryUrl,
|
||||
caCert,
|
||||
boundIssuer,
|
||||
@@ -238,7 +241,7 @@ export const IdentityOidcAuthForm = ({
|
||||
? Object.fromEntries(claimMetadataMapping.map((entry) => [entry.key, entry.value]))
|
||||
: undefined,
|
||||
boundSubject,
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||
@@ -252,7 +255,6 @@ export const IdentityOidcAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+123
-45
@@ -1,5 +1,8 @@
|
||||
import { faLink, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useState } from "react";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { LinkIcon, PlusIcon } from "lucide-react";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -14,17 +17,23 @@ import {
|
||||
} from "@app/context";
|
||||
import { OrgPermissionMachineIdentityAuthTemplateActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { useDeleteIdentity } from "@app/hooks/api";
|
||||
import { useDeleteOrgIdentity } from "@app/hooks/api";
|
||||
import { useDeleteIdentityAuthTemplate } from "@app/hooks/api/identityAuthTemplates";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { IdentityAuthTemplateModal } from "./IdentityAuthTemplateModal";
|
||||
import { IdentityAuthTemplatesTable } from "./IdentityAuthTemplatesTable";
|
||||
import { IdentityLinkForm } from "./IdentityLinkForm";
|
||||
import { IdentityModal } from "./IdentityModal";
|
||||
import { IdentityTable } from "./IdentityTable";
|
||||
import { IdentityTokenAuthTokenModal } from "./IdentityTokenAuthTokenModal";
|
||||
import { MachineAuthTemplateUsagesModal } from "./MachineAuthTemplateUsagesModal";
|
||||
import { OrgIdentityLinkForm } from "./OrgIdentityLinkForm";
|
||||
import { OrgIdentityModal } from "./OrgIdentityModal";
|
||||
|
||||
enum IdentityWizardSteps {
|
||||
SelectAction = "select-action",
|
||||
LinkIdentity = "link-identity",
|
||||
OrganizationIdentity = "project-identity"
|
||||
}
|
||||
|
||||
export const IdentitySection = withPermission(
|
||||
() => {
|
||||
@@ -32,7 +41,9 @@ export const IdentitySection = withPermission(
|
||||
const { currentOrg, isSubOrganization } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteIdentity();
|
||||
const [wizardStep, setWizardStep] = useState(IdentityWizardSteps.SelectAction);
|
||||
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteOrgIdentity();
|
||||
const { mutateAsync: deleteTemplateMutateAsync } = useDeleteIdentityAuthTemplate();
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"identity",
|
||||
@@ -46,7 +57,7 @@ export const IdentitySection = withPermission(
|
||||
"editTemplate",
|
||||
"deleteTemplate",
|
||||
"viewUsages",
|
||||
"linkIdentity"
|
||||
"addOptions"
|
||||
] as const);
|
||||
|
||||
const isMoreIdentitiesAllowed = subscription?.identityLimit
|
||||
@@ -58,7 +69,7 @@ export const IdentitySection = withPermission(
|
||||
const onDeleteIdentitySubmit = async (identityId: string) => {
|
||||
await deleteMutateAsync({
|
||||
identityId,
|
||||
organizationId: orgId
|
||||
orgId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
@@ -91,50 +102,38 @@ export const IdentitySection = withPermission(
|
||||
<p className="text-xl font-medium text-mineshaft-100">Identities</p>
|
||||
<DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/identities/machine-identities" />
|
||||
</div>
|
||||
{isSubOrganization && (
|
||||
<div className="flex items-center">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Create}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
variant="plain"
|
||||
colorSchema="secondary"
|
||||
leftIcon={<FontAwesomeIcon icon={faLink} />}
|
||||
variant="outline_bg"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
handlePopUpOpen("linkIdentity");
|
||||
if (!isMoreIdentitiesAllowed && !isEnterprise) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
description:
|
||||
"You can add more identities if you upgrade your Infisical Pro plan."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSubOrganization) {
|
||||
setWizardStep(IdentityWizardSteps.OrganizationIdentity);
|
||||
}
|
||||
|
||||
handlePopUpOpen("identity");
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Link Identity
|
||||
Create Identity
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Create}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
if (!isMoreIdentitiesAllowed && !isEnterprise) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
text: "You have reached the maximum number of identities allowed on your current plan. Upgrade to Infisical Pro plan to add more identities."
|
||||
});
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("identity");
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Create Identity
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
<IdentityTable handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
@@ -173,7 +172,6 @@ export const IdentitySection = withPermission(
|
||||
</div>
|
||||
<IdentityAuthTemplatesTable handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<IdentityAuthTemplateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<MachineAuthTemplateUsagesModal
|
||||
isOpen={popUp.viewUsages.isOpen}
|
||||
@@ -187,19 +185,99 @@ export const IdentitySection = withPermission(
|
||||
?.name || ""
|
||||
}
|
||||
/>
|
||||
<IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<Modal
|
||||
isOpen={popUp.linkIdentity.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("linkIdentity", isOpen)}
|
||||
isOpen={popUp.identity.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle("identity", open);
|
||||
if (!open) {
|
||||
setWizardStep(IdentityWizardSteps.SelectAction);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title="Assign Existing Identity"
|
||||
subTitle="Assign an existing identity from your root organization to the sub organization. The identity will continue to be managed at its original scope."
|
||||
bodyClassName="overflow-visible"
|
||||
title="Add Identity"
|
||||
subTitle={
|
||||
isSubOrganization ? "Create a new identity or assign an existing identity" : undefined
|
||||
}
|
||||
>
|
||||
<IdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} />
|
||||
<AnimatePresence mode="wait">
|
||||
{wizardStep === IdentityWizardSteps.SelectAction && (
|
||||
<motion.div
|
||||
key="select-type-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<div
|
||||
className="cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setWizardStep(IdentityWizardSteps.OrganizationIdentity)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setWizardStep(IdentityWizardSteps.OrganizationIdentity);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<PlusIcon size="1rem" />
|
||||
<div>Create New Identity</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-mineshaft-300">
|
||||
Create a new machine identity specifically for this sub-organization. This
|
||||
identity will be managed at the sub-organization level.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="mt-4 cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setWizardStep(IdentityWizardSteps.LinkIdentity)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setWizardStep(IdentityWizardSteps.LinkIdentity);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon size="1rem" />
|
||||
<div>Assign Existing Identity</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-mineshaft-300">
|
||||
Assign an existing identity from your parent organization. The identity will
|
||||
continue to be managed at its original scope.
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
{wizardStep === IdentityWizardSteps.OrganizationIdentity && (
|
||||
<motion.div
|
||||
key="identity-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
</motion.div>
|
||||
)}
|
||||
{wizardStep === IdentityWizardSteps.LinkIdentity && (
|
||||
<motion.div
|
||||
key="link-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<OrgIdentityLinkForm onClose={() => handlePopUpClose("identity")} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<IdentityTokenAuthTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteIdentity.isOpen}
|
||||
title={`Are you sure you want to delete ${
|
||||
|
||||
+24
-11
@@ -2,7 +2,6 @@ import { useCallback, useState } from "react";
|
||||
import {
|
||||
faArrowDown,
|
||||
faArrowUp,
|
||||
faBuilding,
|
||||
faCheckCircle,
|
||||
faChevronRight,
|
||||
faEdit,
|
||||
@@ -46,6 +45,7 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Badge, OrgIcon, SubOrgIcon } from "@app/components/v3";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import {
|
||||
getUserTablePreference,
|
||||
@@ -56,8 +56,8 @@ import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||
import {
|
||||
identityAuthToNameMap,
|
||||
useGetOrgRoles,
|
||||
useSearchIdentities,
|
||||
useUpdateIdentity
|
||||
useSearchOrgIdentityMemberships,
|
||||
useUpdateOrgIdentity
|
||||
} from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types";
|
||||
@@ -110,9 +110,9 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
|
||||
const organizationId = currentOrg?.id || "";
|
||||
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentity();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateOrgIdentity();
|
||||
|
||||
const { data, isPending, isFetching } = useSearchIdentities({
|
||||
const { data, isPending, isFetching } = useSearchOrgIdentityMemberships({
|
||||
offset,
|
||||
limit,
|
||||
orderDirection,
|
||||
@@ -293,6 +293,8 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
lastLoginAuthMethod,
|
||||
lastLoginTime
|
||||
}) => {
|
||||
const isSubOrgIdentity = currentOrg.id === orgId;
|
||||
|
||||
return (
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
@@ -357,10 +359,19 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
</Td>
|
||||
{isSubOrganization && (
|
||||
<Td>
|
||||
<p className="truncate">
|
||||
<FontAwesomeIcon size="sm" className="mr-1.5" icon={faBuilding} />
|
||||
{currentOrg.id === orgId ? "Sub Organization" : "Root Organization"}
|
||||
</p>
|
||||
<Badge variant={isSubOrgIdentity ? "sub-org" : "org"}>
|
||||
{isSubOrgIdentity ? (
|
||||
<>
|
||||
<SubOrgIcon />
|
||||
Sub-Organization
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<OrgIcon />
|
||||
Root Organization
|
||||
</>
|
||||
)}
|
||||
</Badge>
|
||||
</Td>
|
||||
)}
|
||||
<Td>
|
||||
@@ -394,7 +405,7 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Edit Identity
|
||||
Edit Identity {isSubOrgIdentity ? "" : "Membership"}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
@@ -414,7 +425,9 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
isDisabled={!isAllowed}
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
>
|
||||
Delete Identity
|
||||
{isSubOrgIdentity
|
||||
? "Delete Identity"
|
||||
: "Remove From Sub-Organization"}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
|
||||
+6
-4
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -71,7 +72,9 @@ export const IdentityTlsCertAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityTlsCertAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTlsCertAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -141,7 +144,7 @@ export const IdentityTlsCertAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
caCertificate,
|
||||
allowedCommonNames: allowedCommonNames || null,
|
||||
identityId,
|
||||
@@ -152,7 +155,7 @@ export const IdentityTlsCertAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
caCertificate,
|
||||
allowedCommonNames: allowedCommonNames || undefined,
|
||||
@@ -169,7 +172,6 @@ export const IdentityTlsCertAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "configured"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+6
-3
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -70,7 +71,9 @@ export const IdentityTokenAuthForm = ({
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
||||
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||
@@ -134,7 +137,7 @@ export const IdentityTokenAuthForm = ({
|
||||
|
||||
if (data) {
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
@@ -143,7 +146,7 @@ export const IdentityTokenAuthForm = ({
|
||||
});
|
||||
} else {
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||
|
||||
+6
-3
@@ -3,6 +3,7 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -105,6 +106,9 @@ export const IdentityUniversalAuthForm = ({
|
||||
identityId,
|
||||
isUpdate
|
||||
}: Props) => {
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { subscription } = useSubscription();
|
||||
@@ -232,7 +236,7 @@ export const IdentityUniversalAuthForm = ({
|
||||
if (data) {
|
||||
// update universal auth configuration
|
||||
await updateMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
@@ -249,7 +253,7 @@ export const IdentityUniversalAuthForm = ({
|
||||
// create new universal auth configuration
|
||||
|
||||
await addMutateAsync({
|
||||
organizationId: orgId,
|
||||
...(projectId ? { projectId } : { organizationId: orgId }),
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL: Number(accessTokenTTL),
|
||||
@@ -270,7 +274,6 @@ export const IdentityUniversalAuthForm = ({
|
||||
text: `Successfully ${isUpdate ? "updated" : "created"} auth method`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
|
||||
+17
-4
@@ -1,13 +1,15 @@
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { useGetAvailableOrgIdentities, useGetOrgRoles } from "@app/hooks/api";
|
||||
import { useGetOrgRoles } from "@app/hooks/api";
|
||||
import { useCreateOrgIdentityMembership } from "@app/hooks/api/orgIdentityMembership";
|
||||
import { orgIdentityMembershipQuery } from "@app/hooks/api/orgIdentityMembership/queries";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
@@ -22,15 +24,26 @@ type Props = {
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const IdentityLinkForm = ({ onClose }: Props) => {
|
||||
export const OrgIdentityLinkForm = ({ onClose }: Props) => {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
|
||||
// const [searchValue, setSearchValue] = useState("");
|
||||
//
|
||||
// const [debouncedSearchValue] = useDebounce(searchValue);
|
||||
|
||||
const { mutateAsync: createMutateAsync } = useCreateOrgIdentityMembership();
|
||||
const { data: rootOrgIdentities, isPending: isRootOrgLoading } = useGetAvailableOrgIdentities();
|
||||
|
||||
// TODO: name filter needs to be implemented on backend
|
||||
const { data: rootOrgIdentities, isPending: isRootOrgLoading } = useQuery({
|
||||
...orgIdentityMembershipQuery.listAvailable({
|
||||
// identityName: debouncedSearchValue
|
||||
}),
|
||||
placeholderData: (prev) => prev
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -69,6 +82,7 @@ export const IdentityLinkForm = ({ onClose }: Props) => {
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder="Select identity..."
|
||||
// onInputChange={setSearchValue}
|
||||
options={rootOrgIdentities}
|
||||
getOptionValue={(option) => option.id}
|
||||
getOptionLabel={(option) => option.name}
|
||||
@@ -94,7 +108,6 @@ export const IdentityLinkForm = ({ onClose }: Props) => {
|
||||
placeholder="Select role..."
|
||||
getOptionValue={(option) => option.slug}
|
||||
getOptionLabel={(option) => option.name}
|
||||
// menuPortalTarget={document.body}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
+82
-93
@@ -14,13 +14,11 @@ import {
|
||||
FormLabel,
|
||||
IconButton,
|
||||
Input,
|
||||
Modal,
|
||||
ModalContent,
|
||||
Switch
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { findOrgMembershipRole } from "@app/helpers/roles";
|
||||
import { useCreateIdentity, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api";
|
||||
import { useCreateOrgIdentity, useGetOrgRoles, useUpdateOrgIdentity } from "@app/hooks/api";
|
||||
import { useAddIdentityUniversalAuth } from "@app/hooks/api/identities";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -47,7 +45,7 @@ type Props = {
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["identity"]>, state?: boolean) => void;
|
||||
};
|
||||
|
||||
export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
export const OrgIdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
@@ -55,8 +53,8 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
const isOrgIdentity = popUp?.identity?.data ? orgId === popUp?.identity?.data?.orgId : true;
|
||||
|
||||
const { mutateAsync: createMutateAsync } = useCreateIdentity();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentity();
|
||||
const { mutateAsync: createMutateAsync } = useCreateOrgIdentity();
|
||||
const { mutateAsync: updateMutateAsync } = useUpdateOrgIdentity();
|
||||
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
||||
|
||||
const {
|
||||
@@ -173,75 +171,66 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.identity?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("identity", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
bodyClassName="overflow-visible"
|
||||
title={`${popUp?.identity?.data ? "Update" : "Create"} Identity`}
|
||||
>
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
{isOrgIdentity && (
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="name"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
className="mb-4"
|
||||
label="Name"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input {...field} placeholder="Machine 1" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
{isOrgIdentity && (
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="name"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
className="mb-4"
|
||||
label="Name"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input {...field} placeholder="Machine 1" />
|
||||
</FormControl>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="role"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label={`${popUp?.identity?.data ? "Update" : ""} Role`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
/>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="role"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label={`${popUp?.identity?.data ? "Update" : ""} Role`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<FilterableSelect
|
||||
placeholder="Select role..."
|
||||
options={roles}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
getOptionValue={(option) => option.slug}
|
||||
getOptionLabel={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{isOrgIdentity && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="hasDeleteProtection"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
||||
<Switch
|
||||
className="mr-2 ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||
containerClassName="flex-row-reverse w-fit"
|
||||
id="delete-protection-enabled"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
>
|
||||
<FilterableSelect
|
||||
placeholder="Select role..."
|
||||
options={roles}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
getOptionValue={(option) => option.slug}
|
||||
getOptionLabel={(option) => option.name}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{isOrgIdentity && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="hasDeleteProtection"
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
||||
<Switch
|
||||
className="mr-2 ml-0 bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||
containerClassName="flex-row-reverse w-fit"
|
||||
id="delete-protection-enabled"
|
||||
thumbClassName="bg-mineshaft-800"
|
||||
onCheckedChange={onChange}
|
||||
isChecked={value}
|
||||
>
|
||||
<p>Delete Protection {value ? "Enabled" : "Disabled"}</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<p>Delete Protection {value ? "Enabled" : "Disabled"}</p>
|
||||
</Switch>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{isOrgIdentity && (
|
||||
<>
|
||||
<div>
|
||||
<FormLabel label="Metadata" />
|
||||
</div>
|
||||
@@ -303,26 +292,26 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting}
|
||||
>
|
||||
{popUp?.identity?.data ? "Update" : "Create"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpToggle("identity", false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting}
|
||||
>
|
||||
{popUp?.identity?.data ? "Update" : "Create"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpToggle("identity", false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
+59
@@ -4,8 +4,10 @@ import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
@@ -18,8 +20,11 @@ import {
|
||||
TextArea,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionSubjects, useSubscription } from "@app/context";
|
||||
import { OrgGatewayPermissionActions } from "@app/context/OrgPermissionContext/types";
|
||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||
import { DistinguishedNameRegex, UserPrincipalNameRegex } from "@app/helpers/string";
|
||||
import { gatewaysQueryKeys } from "@app/hooks/api";
|
||||
import {
|
||||
LdapConnectionMethod,
|
||||
LdapConnectionProvider,
|
||||
@@ -84,6 +89,7 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.LDAP,
|
||||
method: LdapConnectionMethod.SimpleBind,
|
||||
gatewayId: null,
|
||||
credentials: {
|
||||
provider: LdapConnectionProvider.ActiveDirectory,
|
||||
url: "",
|
||||
@@ -104,6 +110,8 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
|
||||
const selectedProvider = watch("credentials.provider");
|
||||
const sslEnabled = watch("credentials.url")?.startsWith("ldaps://") ?? false;
|
||||
const { subscription } = useSubscription();
|
||||
const { data: gateways, isPending: isGatewaysLoading } = useQuery(gatewaysQueryKeys.list());
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
@@ -114,6 +122,57 @@ export const LdapConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
}}
|
||||
>
|
||||
{!isUpdate && <GenericAppConnectionsFields />}
|
||||
{subscription.gateway && (
|
||||
<OrgPermissionCan
|
||||
I={OrgGatewayPermissionActions.AttachGateways}
|
||||
a={OrgPermissionSubjects.Gateway}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="gatewayId"
|
||||
defaultValue=""
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error?.message)}
|
||||
errorText={error?.message}
|
||||
label="Gateway"
|
||||
>
|
||||
<Tooltip
|
||||
isDisabled={isAllowed}
|
||||
content="Restricted access. You don't have permission to attach gateways to resources."
|
||||
>
|
||||
<div>
|
||||
<Select
|
||||
isDisabled={!isAllowed}
|
||||
value={value as string}
|
||||
onValueChange={onChange}
|
||||
className="w-full border border-mineshaft-500"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
isLoading={isGatewaysLoading}
|
||||
placeholder="Default: Internet Gateway"
|
||||
position="popper"
|
||||
>
|
||||
<SelectItem
|
||||
value={null as unknown as string}
|
||||
onClick={() => onChange(undefined)}
|
||||
>
|
||||
Internet Gateway
|
||||
</SelectItem>
|
||||
{gateways?.map((el) => (
|
||||
<SelectItem value={el.id} key={el.id}>
|
||||
{el.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
<div className="grid grid-cols-2 items-center gap-2">
|
||||
<Controller
|
||||
name="method"
|
||||
|
||||
+53
-9
@@ -7,16 +7,21 @@ import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { DeleteActionModal, PageHeader } from "@app/components/v2";
|
||||
import { Button, DeleteActionModal, Modal, ModalContent, PageHeader } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { useDeleteIdentity, useGetIdentityById } from "@app/hooks/api";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionIdentityActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization
|
||||
} from "@app/context";
|
||||
import { useDeleteOrgIdentity, useGetOrgIdentityMembershipById } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
import { ViewIdentityAuthModal } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityAuthModal";
|
||||
import { OrgAccessControlTabSections } from "@app/types/org";
|
||||
|
||||
import { IdentityAuthMethodModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
|
||||
import { IdentityModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal";
|
||||
import { OrgIdentityModal } from "../AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/OrgIdentityModal";
|
||||
import {
|
||||
IdentityAuthenticationSection,
|
||||
IdentityDetailsSection,
|
||||
@@ -31,8 +36,8 @@ const Page = () => {
|
||||
const identityId = params.identityId as string;
|
||||
const { currentOrg, isSubOrganization } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { data } = useGetIdentityById(identityId);
|
||||
const { mutateAsync: deleteIdentity } = useDeleteIdentity();
|
||||
const { data } = useGetOrgIdentityMembershipById(identityId);
|
||||
const { mutateAsync: deleteIdentity, isPending: isDeletingIdentity } = useDeleteOrgIdentity();
|
||||
const isAuthHidden = orgId !== data?.identity?.orgId;
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
@@ -46,7 +51,7 @@ const Page = () => {
|
||||
const onDeleteIdentitySubmit = async (id: string) => {
|
||||
await deleteIdentity({
|
||||
identityId: id,
|
||||
organizationId: orgId
|
||||
orgId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
@@ -81,7 +86,36 @@ const Page = () => {
|
||||
scope={isSubOrganization ? "namespace" : "org"}
|
||||
description={`${isSubOrganization ? "Sub-" : ""}Organization Identity`}
|
||||
title={data.identity.name}
|
||||
/>
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{isSubOrganization && data.identity.orgId !== currentOrg.id && (
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Delete}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
renderTooltip
|
||||
allowedLabel="Remove from sub-organization"
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="danger"
|
||||
variant="outline_bg"
|
||||
size="xs"
|
||||
isDisabled={!isAllowed}
|
||||
isLoading={isDeletingIdentity}
|
||||
onClick={() =>
|
||||
handlePopUpOpen("deleteIdentity", {
|
||||
identityId: data.identity.id,
|
||||
name: data.identity.name
|
||||
})
|
||||
}
|
||||
>
|
||||
Unlink Identity
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
</PageHeader>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<IdentityDetailsSection
|
||||
@@ -100,7 +134,17 @@ const Page = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<Modal
|
||||
isOpen={popUp?.identity?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("identity", isOpen)}
|
||||
>
|
||||
<ModalContent
|
||||
bodyClassName="overflow-visible"
|
||||
title={`${popUp?.identity?.data ? "Update" : "Create"} Identity`}
|
||||
>
|
||||
<OrgIdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<IdentityAuthMethodModal
|
||||
popUp={popUp}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
|
||||
+6
-2
@@ -4,7 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, Tooltip } from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { IdentityAuthMethod, identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
|
||||
import {
|
||||
IdentityAuthMethod,
|
||||
identityAuthToNameMap,
|
||||
useGetOrgIdentityMembershipById
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@@ -16,7 +20,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: Props) => {
|
||||
const { data, refetch } = useGetIdentityById(identityId);
|
||||
const { data, refetch } = useGetOrgIdentityMembershipById(identityId);
|
||||
|
||||
return data ? (
|
||||
<div className="mt-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
|
||||
+3
-3
@@ -7,9 +7,9 @@ import { Button, IconButton, Tooltip } from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import {
|
||||
useGetIdentityById,
|
||||
useGetIdentityUniversalAuth,
|
||||
useGetIdentityUniversalAuthClientSecrets
|
||||
useGetIdentityUniversalAuthClientSecrets,
|
||||
useGetOrgIdentityMembershipById
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -30,7 +30,7 @@ export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) =>
|
||||
initialState: "Copy Client ID to clipboard"
|
||||
});
|
||||
|
||||
const { data } = useGetIdentityById(identityId);
|
||||
const { data } = useGetOrgIdentityMembershipById(identityId);
|
||||
const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(identityId);
|
||||
const { data: clientSecrets } = useGetIdentityUniversalAuthClientSecrets(identityId);
|
||||
return (
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import {
|
||||
IconButton,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { useGetIdentityById, useGetIdentityTokensTokenAuth } from "@app/hooks/api";
|
||||
import { useGetIdentityTokensTokenAuth, useGetOrgIdentityMembershipById } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@@ -23,7 +23,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const IdentityTokens = ({ identityId, handlePopUpOpen }: Props) => {
|
||||
const { data } = useGetIdentityById(identityId);
|
||||
const { data } = useGetOrgIdentityMembershipById(identityId);
|
||||
const { data: tokens } = useGetIdentityTokensTokenAuth(identityId);
|
||||
return (
|
||||
<div>
|
||||
|
||||
+4
-4
@@ -23,7 +23,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
|
||||
import { identityAuthToNameMap, useGetOrgIdentityMembershipById } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@@ -41,7 +41,7 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen, isOrgIdent
|
||||
});
|
||||
const { isSubOrganization } = useOrganization();
|
||||
|
||||
const { data } = useGetIdentityById(identityId);
|
||||
const { data } = useGetOrgIdentityMembershipById(identityId);
|
||||
return data ? (
|
||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
@@ -86,7 +86,7 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen, isOrgIdent
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
Edit Identity
|
||||
{isOrgIdentity ? "Edit Identity" : "Edit Identity Role"}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
@@ -110,7 +110,7 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen, isOrgIdent
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
Delete Identity
|
||||
{!isOrgIdentity ? "Remove From Sub-Organization" : "Delete Identity"}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import {
|
||||
useAddIdentityToWorkspace,
|
||||
useCreateProjectIdentityMembership,
|
||||
useGetIdentityProjectMemberships,
|
||||
useGetProjectRoles,
|
||||
useGetUserProjects,
|
||||
@@ -45,7 +45,7 @@ type Props = {
|
||||
const Content = ({ identityId, handlePopUpToggle }: Omit<Props, "popUp">) => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { data: workspaces = [] } = useGetUserProjects();
|
||||
const { mutateAsync: addIdentityToWorkspace } = useAddIdentityToWorkspace();
|
||||
const { mutateAsync: addIdentityToWorkspace } = useCreateProjectIdentityMembership();
|
||||
|
||||
const {
|
||||
control,
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
|
||||
import { getProjectBaseURL } from "@app/helpers/project";
|
||||
import { formatProjectRoleName } from "@app/helpers/roles";
|
||||
import { useGetUserProjects } from "@app/hooks/api";
|
||||
import { IdentityMembership } from "@app/hooks/api/identities/types";
|
||||
import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
export enum TabSections {
|
||||
@@ -20,7 +20,7 @@ export enum TabSections {
|
||||
}
|
||||
|
||||
type Props = {
|
||||
membership: IdentityMembership;
|
||||
membership: IdentityProjectMembershipV1;
|
||||
handlePopUpOpen: (
|
||||
popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>,
|
||||
data?: object
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { DeleteActionModal, IconButton } from "@app/components/v2";
|
||||
import { useDeleteIdentityFromWorkspace } from "@app/hooks/api";
|
||||
import { useDeleteProjectIdentityMembership } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { IdentityAddToProjectModal } from "./IdentityAddToProjectModal";
|
||||
@@ -14,7 +14,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const IdentityProjectsSection = ({ identityId }: Props) => {
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteProjectIdentityMembership();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"addIdentityToProject",
|
||||
|
||||
+27
-6
@@ -1,11 +1,18 @@
|
||||
import { useState } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import { UseMutationResult } from "@tanstack/react-query";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import ms from "ms";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { VariablePermissionCan } from "@app/components/permissions";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import {
|
||||
OrgPermissionIdentityActions,
|
||||
OrgPermissionSubjects,
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context";
|
||||
|
||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||
|
||||
@@ -31,7 +38,11 @@ export const LockoutFields = ({
|
||||
|
||||
const [lockedOutState, setLockedOutState] = useState(lockedOut);
|
||||
|
||||
const clearLockouts = async () => {
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
|
||||
async function clearLockouts() {
|
||||
const deleted = await mutateAsync({ identityId });
|
||||
createNotification({
|
||||
text: `Successfully cleared ${deleted} lockout${deleted === 1 ? "" : "s"}`,
|
||||
@@ -39,13 +50,23 @@ export const LockoutFields = ({
|
||||
});
|
||||
setLockedOutState(false);
|
||||
onResetAllLockouts();
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="col-span-2 mt-3 flex justify-between border-b border-mineshaft-500 pb-2">
|
||||
<span className="text-bunker-300">Lockout Options</span>
|
||||
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}>
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isDisabled={!isAllowed || !lockedOutState || isPending}
|
||||
@@ -57,7 +78,7 @@ export const LockoutFields = ({
|
||||
Reset All Lockouts
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
</div>
|
||||
<IdentityAuthFieldDisplay label="Lockout Threshold">
|
||||
{data.lockoutThreshold}
|
||||
|
||||
+55
-12
@@ -1,10 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faBan, faEdit, faKey, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { VariablePermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -20,7 +22,12 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import {
|
||||
OrgPermissionIdentityActions,
|
||||
OrgPermissionSubjects,
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useRevokeIdentityTokenAuthToken } from "@app/hooks/api";
|
||||
import { IdentityAccessToken } from "@app/hooks/api/identities/types";
|
||||
@@ -37,6 +44,10 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
"revokeToken"
|
||||
] as const);
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(5);
|
||||
|
||||
@@ -68,7 +79,17 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
<div className="col-span-2 mt-3">
|
||||
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
|
||||
<span className="text-bunker-300">Access Tokens</span>
|
||||
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}>
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
size="xs"
|
||||
@@ -84,7 +105,7 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
Add Token
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
</div>
|
||||
<TableContainer className="mt-4 rounded-none border-none">
|
||||
<Table>
|
||||
@@ -132,9 +153,20 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center gap-2">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Edit}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={
|
||||
projectId
|
||||
? ProjectPermissionIdentityActions.Edit
|
||||
: OrgPermissionIdentityActions.Edit
|
||||
}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Tooltip content={isAllowed ? "Edit Token" : "Access Restricted"}>
|
||||
@@ -155,11 +187,22 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
{!isAccessTokenRevoked && (
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Edit}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={
|
||||
projectId
|
||||
? ProjectPermissionIdentityActions.Edit
|
||||
: OrgPermissionIdentityActions.Edit
|
||||
}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Tooltip content={isAllowed ? "Revoke Token" : "Access Restricted"}>
|
||||
@@ -181,7 +224,7 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
|
||||
+40
-8
@@ -1,10 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faKey, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { VariablePermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -20,7 +22,12 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import {
|
||||
OrgPermissionIdentityActions,
|
||||
OrgPermissionSubjects,
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useRevokeIdentityUniversalAuthClientSecret } from "@app/hooks/api";
|
||||
import { ClientSecretData } from "@app/hooks/api/identities/types";
|
||||
@@ -37,6 +44,10 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
||||
"clientSecret"
|
||||
] as const);
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(5);
|
||||
|
||||
@@ -60,7 +71,17 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
||||
<div className="col-span-2">
|
||||
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
|
||||
<span className="text-bunker-300">Client Secrets</span>
|
||||
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}>
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={projectId ? ProjectPermissionIdentityActions.Edit : OrgPermissionIdentityActions.Edit}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isDisabled={!isAllowed}
|
||||
@@ -76,7 +97,7 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
||||
Add Client Secret
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
</div>
|
||||
<TableContainer className="mt-4 rounded-none border-none">
|
||||
<Table>
|
||||
@@ -120,9 +141,20 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
||||
{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}
|
||||
</Td>
|
||||
<Td>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Edit}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={
|
||||
projectId
|
||||
? ProjectPermissionIdentityActions.Edit
|
||||
: OrgPermissionIdentityActions.Edit
|
||||
}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Tooltip content={isAllowed ? "Delete Secret" : "Access Restricted"}>
|
||||
@@ -143,7 +175,7 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
|
||||
+1
@@ -49,6 +49,7 @@ export const ViewIdentityAliCloudAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+11
-4
@@ -1,3 +1,5 @@
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { DeleteActionModal, Modal, ModalContent } from "@app/components/v2";
|
||||
@@ -46,8 +48,7 @@ type Props = {
|
||||
|
||||
type TRevokeOptions = {
|
||||
identityId: string;
|
||||
organizationId: string;
|
||||
};
|
||||
} & ({ projectId: string } | { organizationId: string });
|
||||
|
||||
export const Content = ({
|
||||
identityId,
|
||||
@@ -61,7 +62,9 @@ export const Content = ({
|
||||
>) => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||
"revokeAuthMethod",
|
||||
"upgradePlan",
|
||||
@@ -142,7 +145,11 @@ export const Content = ({
|
||||
const handleDeleteAuthMethod = async () => {
|
||||
await revokeMethod({
|
||||
identityId,
|
||||
organizationId: orgId
|
||||
...(projectId
|
||||
? { projectId }
|
||||
: {
|
||||
organizationId: orgId
|
||||
})
|
||||
});
|
||||
|
||||
createNotification({
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ export const ViewIdentityAwsAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ export const ViewIdentityAzureAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+45
-11
@@ -1,8 +1,10 @@
|
||||
import { ReactNode } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { VariablePermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
@@ -10,15 +12,25 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
|
||||
import {
|
||||
OrgPermissionIdentityActions,
|
||||
OrgPermissionSubjects,
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
onEdit: VoidFunction;
|
||||
onDelete: VoidFunction;
|
||||
identityId: string;
|
||||
};
|
||||
|
||||
export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props) => {
|
||||
export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit, identityId }: Props) => {
|
||||
const { projectId } = useParams({
|
||||
strict: false
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
@@ -36,9 +48,20 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="mt-3 min-w-[120px]" align="end">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Edit}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={
|
||||
projectId
|
||||
? ProjectPermissionIdentityActions.Edit
|
||||
: OrgPermissionIdentityActions.Edit
|
||||
}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -49,10 +72,21 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionIdentityActions.Delete}
|
||||
a={OrgPermissionSubjects.Identity}
|
||||
</VariablePermissionCan>
|
||||
<VariablePermissionCan
|
||||
type={projectId ? "project" : "org"}
|
||||
I={
|
||||
projectId
|
||||
? ProjectPermissionIdentityActions.Delete
|
||||
: OrgPermissionIdentityActions.Delete
|
||||
}
|
||||
a={
|
||||
projectId
|
||||
? subject(ProjectPermissionSub.Identity, {
|
||||
identityId
|
||||
})
|
||||
: OrgPermissionSubjects.Identity
|
||||
}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -63,7 +97,7 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</VariablePermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ export const ViewIdentityGcpAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -49,6 +49,7 @@ export const ViewIdentityJwtAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -59,6 +59,7 @@ export const ViewIdentityKubernetesAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -52,6 +52,7 @@ export const ViewIdentityLdapAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ export const ViewIdentityOciAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ export const ViewIdentityOidcAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -51,6 +51,7 @@ export const ViewIdentityTlsCertAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -49,6 +49,7 @@ export const ViewIdentityTokenAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
|
||||
{data.accessTokenTTL}
|
||||
|
||||
+1
@@ -66,6 +66,7 @@ export const ViewIdentityUniversalAuthContent = ({
|
||||
<ViewIdentityContentWrapper
|
||||
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||
onDelete={onDelete}
|
||||
identityId={identityId}
|
||||
>
|
||||
{Number(data.accessTokenPeriod) > 0 ? (
|
||||
<IdentityAuthFieldDisplay label="Access Token Period (seconds)">
|
||||
|
||||
@@ -26,9 +26,21 @@ export const ResourceTypeSelect = ({ onSelect }: Props) => {
|
||||
return [
|
||||
...resourceOptions,
|
||||
// We are temporarily showing these resources so that we can gauge interest before committing
|
||||
{ name: "OracleDB", resource: PamResourceType.OracleDB },
|
||||
{ name: "SQLite", resource: PamResourceType.SQLite },
|
||||
{ name: "Microsoft SQL Server", resource: PamResourceType.MsSQL },
|
||||
{ name: "MongoDB", resource: PamResourceType.MongoDB },
|
||||
{ name: "Cassandra", resource: PamResourceType.Cassandra },
|
||||
{ name: "CockroachDB", resource: PamResourceType.CockroachDB },
|
||||
{ name: "DynamoDB", resource: PamResourceType.DynamoDB },
|
||||
{ name: "Snowflake", resource: PamResourceType.Snowflake },
|
||||
{ name: "Elasticsearch", resource: PamResourceType.Elasticsearch },
|
||||
{ name: "Redis", resource: PamResourceType.Redis },
|
||||
{ name: "RDP", resource: PamResourceType.RDP },
|
||||
{ name: "SSH", resource: PamResourceType.SSH },
|
||||
{ name: "Kubernetes", resource: PamResourceType.Kubernetes }
|
||||
{ name: "Kubernetes", resource: PamResourceType.Kubernetes },
|
||||
{ name: "MCP", resource: PamResourceType.MCP },
|
||||
{ name: "Web Application", resource: PamResourceType.WebApp }
|
||||
];
|
||||
}, [resourceOptions]);
|
||||
|
||||
@@ -38,11 +50,13 @@ export const ResourceTypeSelect = ({ onSelect }: Props) => {
|
||||
|
||||
const filteredOptions = useMemo(
|
||||
() =>
|
||||
appendedResourceOptions?.filter(
|
||||
({ name, resource }) =>
|
||||
name.toLowerCase().includes(search.trim().toLowerCase()) ||
|
||||
resource.toLowerCase().includes(search.trim().toLowerCase())
|
||||
) ?? [],
|
||||
appendedResourceOptions
|
||||
?.filter(
|
||||
({ name, resource }) =>
|
||||
name.toLowerCase().includes(search.trim().toLowerCase()) ||
|
||||
resource.toLowerCase().includes(search.trim().toLowerCase())
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)) ?? [],
|
||||
[appendedResourceOptions, search]
|
||||
);
|
||||
|
||||
@@ -65,7 +79,16 @@ export const ResourceTypeSelect = ({ onSelect }: Props) => {
|
||||
if (
|
||||
resource === PamResourceType.RDP ||
|
||||
resource === PamResourceType.SSH ||
|
||||
resource === PamResourceType.Kubernetes
|
||||
resource === PamResourceType.Kubernetes ||
|
||||
resource === PamResourceType.MCP ||
|
||||
resource === PamResourceType.Redis ||
|
||||
resource === PamResourceType.MongoDB ||
|
||||
resource === PamResourceType.WebApp ||
|
||||
resource === PamResourceType.Cassandra ||
|
||||
resource === PamResourceType.CockroachDB ||
|
||||
resource === PamResourceType.Elasticsearch ||
|
||||
resource === PamResourceType.Snowflake ||
|
||||
resource === PamResourceType.DynamoDB
|
||||
) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
text: "Your current plan does not include access to this resource type. To unlock this feature, please upgrade to Infisical Enterprise plan.",
|
||||
@@ -105,17 +128,16 @@ export const ResourceTypeSelect = ({ onSelect }: Props) => {
|
||||
onClick={() => handleResourceSelect(option.resource)}
|
||||
className="group relative flex h-28 cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-700 p-4 duration-200 hover:bg-mineshaft-600"
|
||||
>
|
||||
<div className="relative">
|
||||
<div className="relative my-auto">
|
||||
<img
|
||||
src={`/images/integrations/${image}`}
|
||||
style={{
|
||||
width: `${size}px`
|
||||
}}
|
||||
className="mt-auto"
|
||||
alt={`${name} logo`}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-auto max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
||||
<div className="max-w-xs text-center text-xs font-medium text-gray-300 duration-200 group-hover:text-gray-200">
|
||||
{name}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
+243
-54
@@ -1,3 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faArrowDown,
|
||||
@@ -11,7 +12,8 @@ import {
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { format } from "date-fns";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { LinkIcon, PlusIcon } from "lucide-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -29,6 +31,8 @@ import {
|
||||
HoverCardTrigger,
|
||||
IconButton,
|
||||
Input,
|
||||
Modal,
|
||||
ModalContent,
|
||||
Pagination,
|
||||
Spinner,
|
||||
Table,
|
||||
@@ -42,8 +46,20 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
|
||||
import { Blur } from "@app/components/v2/Blur";
|
||||
import {
|
||||
Badge,
|
||||
DocumentationLinkBadge,
|
||||
OrgIcon,
|
||||
ProjectIcon,
|
||||
SubOrgIcon
|
||||
} from "@app/components/v3";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub,
|
||||
useOrganization,
|
||||
useProject
|
||||
} from "@app/context";
|
||||
import { getProjectBaseURL } from "@app/helpers/project";
|
||||
import { formatProjectRoleName } from "@app/helpers/roles";
|
||||
import {
|
||||
@@ -53,19 +69,33 @@ import {
|
||||
} from "@app/helpers/userTablePreferences";
|
||||
import { withProjectPermission } from "@app/hoc";
|
||||
import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||
import { useDeleteIdentityFromWorkspace, useGetWorkspaceIdentityMemberships } from "@app/hooks/api";
|
||||
import {
|
||||
useDeleteProjectIdentity,
|
||||
useDeleteProjectIdentityMembership,
|
||||
useListProjectIdentityMemberships
|
||||
} from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { ProjectIdentityOrderBy } from "@app/hooks/api/projects/types";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
import { ProjectIdentityModal } from "@app/pages/project/AccessControlPage/components/IdentityTab/components/ProjectIdentityModal";
|
||||
|
||||
import { IdentityModal } from "./components/IdentityModal";
|
||||
import { ProjectLinkIdentityModal } from "./components/ProjectLinkIdentityModal";
|
||||
|
||||
const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
|
||||
|
||||
enum WizardSteps {
|
||||
SelectAction = "select-action",
|
||||
LinkIdentity = "link-identity",
|
||||
ProjectIdentity = "project-identity"
|
||||
}
|
||||
|
||||
export const IdentityTab = withProjectPermission(
|
||||
() => {
|
||||
const { currentProject, projectId } = useProject();
|
||||
const navigate = useNavigate();
|
||||
const { isSubOrganization, currentOrg } = useOrganization();
|
||||
|
||||
const [wizardStep, setWizardStep] = useState(WizardSteps.SelectAction);
|
||||
|
||||
const {
|
||||
offset,
|
||||
@@ -90,7 +120,7 @@ export const IdentityTab = withProjectPermission(
|
||||
setUserTablePreference("projectIdentityTable", PreferenceKey.PerPage, newPerPage);
|
||||
};
|
||||
|
||||
const { data, isPending, isFetching } = useGetWorkspaceIdentityMemberships(
|
||||
const { data, isPending, isFetching } = useListProjectIdentityMemberships(
|
||||
{
|
||||
projectId,
|
||||
offset,
|
||||
@@ -110,28 +140,41 @@ export const IdentityTab = withProjectPermission(
|
||||
setPage
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
|
||||
const { mutateAsync: deleteMembershipMutateAsync } = useDeleteProjectIdentityMembership();
|
||||
const { mutateAsync: deleteProjectIdentity } = useDeleteProjectIdentity();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"identity",
|
||||
"createIdentity",
|
||||
"deleteIdentity",
|
||||
"upgradePlan"
|
||||
"upgradePlan",
|
||||
"addOptions"
|
||||
] as const);
|
||||
|
||||
const onRemoveIdentitySubmit = async (identityId: string) => {
|
||||
await deleteMutateAsync({
|
||||
identityId,
|
||||
projectId
|
||||
});
|
||||
const onRemoveIdentitySubmit = async (identityId: string, isProjectIdentity: boolean) => {
|
||||
if (isProjectIdentity) {
|
||||
await deleteProjectIdentity({
|
||||
identityId,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully removed identity from project",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted project identity",
|
||||
type: "success"
|
||||
});
|
||||
} else {
|
||||
await deleteMembershipMutateAsync({
|
||||
identityId,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully removed identity from project",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose("deleteIdentity");
|
||||
};
|
||||
|
||||
const handleSort = (column: ProjectIdentityOrderBy) => {
|
||||
if (column === orderBy) {
|
||||
setOrderDirection((prev) =>
|
||||
@@ -144,6 +187,12 @@ export const IdentityTab = withProjectPermission(
|
||||
setOrderDirection(OrderByDirection.ASC);
|
||||
};
|
||||
|
||||
const noAccessIdentityCount = Math.max(
|
||||
(page * perPage > totalCount ? totalCount % perPage : perPage) -
|
||||
(data?.identityMemberships?.length || 0),
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
@@ -151,22 +200,23 @@ export const IdentityTab = withProjectPermission(
|
||||
<p className="text-xl font-medium text-mineshaft-100">Identities</p>
|
||||
<DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/identities/machine-identities" />
|
||||
</div>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={ProjectPermissionSub.Identity}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("identity")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Add Identity
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<div className="flex items-center">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={ProjectPermissionSub.Identity}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("createIdentity")}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Create Identity
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
containerClassName="mb-4"
|
||||
@@ -202,7 +252,7 @@ export const IdentityTab = withProjectPermission(
|
||||
</div>
|
||||
</Th>
|
||||
<Th className="w-1/3">Role</Th>
|
||||
<Th>Added on</Th>
|
||||
<Th>Managed by</Th>
|
||||
<Th className="w-5">{isFetching ? <Spinner size="xs" /> : null}</Th>
|
||||
</Tr>
|
||||
</THead>
|
||||
@@ -213,9 +263,8 @@ export const IdentityTab = withProjectPermission(
|
||||
data.identityMemberships.length > 0 &&
|
||||
data.identityMemberships.map((identityMember) => {
|
||||
const {
|
||||
identity: { id, name },
|
||||
roles,
|
||||
createdAt
|
||||
identity: { id, name, projectId: identityProjectId, orgId: identityOrgId },
|
||||
roles
|
||||
} = identityMember;
|
||||
return (
|
||||
<Tr
|
||||
@@ -339,7 +388,36 @@ export const IdentityTab = withProjectPermission(
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
||||
<Td>
|
||||
<Badge
|
||||
variant={
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
identityProjectId
|
||||
? "project"
|
||||
: isSubOrganization && currentOrg.id === identityOrgId
|
||||
? "sub-org"
|
||||
: "org"
|
||||
}
|
||||
>
|
||||
{/* eslint-disable-next-line no-nested-ternary */}
|
||||
{identityProjectId ? (
|
||||
<>
|
||||
<ProjectIcon />
|
||||
Project
|
||||
</>
|
||||
) : isSubOrganization && currentOrg.id === identityOrgId ? (
|
||||
<>
|
||||
<SubOrgIcon />
|
||||
Sub-Organization
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<OrgIcon />
|
||||
Organization
|
||||
</>
|
||||
)}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td className="flex justify-end space-x-2">
|
||||
<Tooltip className="max-w-sm text-center" content="Options">
|
||||
<DropdownMenu>
|
||||
@@ -369,11 +447,12 @@ export const IdentityTab = withProjectPermission(
|
||||
evt.preventDefault();
|
||||
handlePopUpOpen("deleteIdentity", {
|
||||
identityId: id,
|
||||
name
|
||||
name,
|
||||
isProjectIdentity: Boolean(identityProjectId)
|
||||
});
|
||||
}}
|
||||
>
|
||||
Remove Identity From Project
|
||||
{identityProjectId ? "Delete Identity" : "Remove From Project"}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
@@ -384,6 +463,20 @@ export const IdentityTab = withProjectPermission(
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
{!isPending &&
|
||||
data &&
|
||||
data?.totalCount !== 0 &&
|
||||
Array.from(Array(noAccessIdentityCount)).map((_e, i) => (
|
||||
<Tr key={`hid-identity-${i + 1}`}>
|
||||
<Td>No Access</Td>
|
||||
<Td colSpan={3}>
|
||||
<Blur
|
||||
className="w-min"
|
||||
tooltipText="You do not have permission to read this identity."
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</TBody>
|
||||
</Table>
|
||||
{!isPending && data && totalCount > 0 && (
|
||||
@@ -395,18 +488,113 @@ export const IdentityTab = withProjectPermission(
|
||||
onChangePerPage={handlePerPageChange}
|
||||
/>
|
||||
)}
|
||||
{!isPending && data && data?.identityMemberships.length === 0 && (
|
||||
<EmptyState
|
||||
title={
|
||||
debouncedSearch.trim().length > 0
|
||||
? "No identities match search filter"
|
||||
: "No identities have been added to this project"
|
||||
}
|
||||
icon={faServer}
|
||||
/>
|
||||
)}
|
||||
{!isPending &&
|
||||
data &&
|
||||
data?.identityMemberships.length === 0 &&
|
||||
data?.totalCount === 0 && (
|
||||
<EmptyState
|
||||
title={
|
||||
debouncedSearch.trim().length > 0
|
||||
? "No identities match search filter"
|
||||
: "No identities have been added to this project"
|
||||
}
|
||||
icon={faServer}
|
||||
/>
|
||||
)}
|
||||
</TableContainer>
|
||||
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<Modal
|
||||
isOpen={popUp.createIdentity.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle("createIdentity", open);
|
||||
if (!open) setWizardStep(WizardSteps.SelectAction);
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
bodyClassName="overflow-visible"
|
||||
title="Add Project Identity"
|
||||
subTitle="Create a new identity or assign an existing identity"
|
||||
>
|
||||
<AnimatePresence mode="wait">
|
||||
{wizardStep === WizardSteps.SelectAction && (
|
||||
<motion.div
|
||||
key="select-type-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<div
|
||||
className="cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setWizardStep(WizardSteps.ProjectIdentity)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setWizardStep(WizardSteps.ProjectIdentity);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<PlusIcon size="1rem" />
|
||||
<div>Create New Identity</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-mineshaft-300">
|
||||
Create a new machine identity specifically for this project. This identity
|
||||
will be managed at the project-level.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="mt-4 cursor-pointer rounded-md border border-mineshaft-600 p-4 transition-all hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setWizardStep(WizardSteps.LinkIdentity)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setWizardStep(WizardSteps.LinkIdentity);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon size="1rem" />
|
||||
<div>Assign Existing Identity</div>
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-mineshaft-300">
|
||||
Assign an existing identity from your organization. The identity will continue
|
||||
to be managed at its original scope.
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
{wizardStep === WizardSteps.ProjectIdentity && (
|
||||
<motion.div
|
||||
key="identity-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<ProjectIdentityModal
|
||||
onClose={() => {
|
||||
handlePopUpClose("createIdentity");
|
||||
setWizardStep(WizardSteps.SelectAction);
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
{wizardStep === WizardSteps.LinkIdentity && (
|
||||
<motion.div
|
||||
key="link-step"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<ProjectLinkIdentityModal handlePopUpToggle={handlePopUpToggle} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteIdentity.isOpen}
|
||||
title={`Are you sure you want to remove ${
|
||||
@@ -416,7 +604,8 @@ export const IdentityTab = withProjectPermission(
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() =>
|
||||
onRemoveIdentitySubmit(
|
||||
(popUp?.deleteIdentity?.data as { identityId: string })?.identityId
|
||||
popUp?.deleteIdentity?.data?.identityId,
|
||||
popUp?.deleteIdentity?.data?.isProjectIdentity
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user