improvements: address feedback

This commit is contained in:
Scott Wilson
2025-12-08 15:36:56 -08:00
parent d5948b3246
commit 77ec24c323
42 changed files with 599 additions and 682 deletions

View File

@@ -0,0 +1,79 @@
/* eslint-disable react/prop-types */
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "../../utils";
function UnstableAccordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return (
<AccordionPrimitive.Root
data-slot="accordion"
className="border border-border bg-container"
{...props}
/>
);
}
function UnstableAccordionItem({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
<AccordionPrimitive.Item
data-slot="accordion-item"
className={cn("border-b border-border last:border-b-0", className)}
{...props}
/>
);
}
function UnstableAccordionTrigger({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"flex min-h-12 flex-1 items-center gap-4 border-border bg-container px-4 text-left text-sm font-medium",
"transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
"disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
"cursor-pointer hover:bg-foreground/5",
"data-[state=open]:bg-foreground/5",
className
)}
{...props}
>
<ChevronDownIcon className="pointer-events-none size-4 shrink-0 translate-y-0 text-label transition-transform duration-200" />
{children}
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
);
}
function UnstableAccordionContent({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
return (
<AccordionPrimitive.Content
data-slot="accordion-content"
className="overflow-hidden text-sm transition data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("p-6", className)}>{children}</div>
</AccordionPrimitive.Content>
);
}
export {
UnstableAccordion,
UnstableAccordionContent,
UnstableAccordionItem,
UnstableAccordionTrigger
};

View File

@@ -0,0 +1 @@
export * from "./Accordion";

View File

@@ -25,8 +25,8 @@ const badgeVariants = cva(
},
variant: {
ghost: "text-foreground border-none",
default: "bg-foreground text-background [a&,button&]:hover:bg-primary/35",
outline: "text-foreground border-foreground border",
default: "bg-label text-background border-label [a&,button&]:hover:bg-primary/35",
outline: "text-label border-label border",
neutral: "bg-neutral/15 border-neutral/10 text-neutral [a&,button&]:hover:bg-neutral/35",
success: "bg-success/15 border-success/10 text-success [a&,button&]:hover:bg-success/35",
info: "bg-info/15 border-info/10 border text-info [a&,button&]:hover:bg-info/35",

View File

@@ -8,7 +8,10 @@ function UnstableCard({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn("flex h-fit flex-col gap-6 rounded-[6px] text-foreground shadow-sm", className)}
className={cn(
"flex h-fit flex-col gap-6 rounded-md border border-border bg-card p-5 text-foreground shadow-sm",
className
)}
{...props}
/>
);
@@ -19,7 +22,8 @@ function UnstableCardHeader({ className, ...props }: React.ComponentProps<"div">
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-center gap-1 border-mineshaft-400/60 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-center gap-1",
"border-border has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...props}
@@ -31,10 +35,7 @@ function UnstableCardTitle({ className, ...props }: React.ComponentProps<"div">)
return (
<div
data-slot="card-title"
className={cn(
"flex items-center gap-1.5 leading-none font-semibold [&>svg]:inline-block [&>svg]:size-[18px]",
className
)}
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
);
@@ -42,14 +43,7 @@ function UnstableCardTitle({ className, ...props }: React.ComponentProps<"div">)
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn(
"flex items-center gap-1 text-sm text-accent [&>svg]:inline-block [&>svg]:size-[12px]",
className
)}
{...props}
/>
<div data-slot="card-description" className={cn("text-sm text-accent", className)} {...props} />
);
}

View File

@@ -36,7 +36,8 @@ function UnstableDropdownMenuContent({
sideOffset={sideOffset}
className={cn(
"max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin)",
"z-50 overflow-x-hidden overflow-y-auto rounded-[6px] border border-border/50 bg-popover p-1 text-xs text-foreground shadow-md",
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
"z-50 overflow-x-hidden overflow-y-auto rounded-[6px] border border-border bg-popover p-1 text-xs text-foreground shadow-md",
className
)}
{...props}

View File

@@ -70,7 +70,7 @@ function UnstableEmptyDescription({ className, ...props }: React.ComponentProps<
<div
data-slot="empty-description"
className={cn(
"text-xs/relaxed text-muted [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
"text-xs/relaxed text-muted [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-project",
className
)}
{...props}

View File

@@ -35,8 +35,8 @@ const iconButtonVariants = cva(
"border-danger/75 bg-danger/40 text-foreground hover:bg-danger/50 hover:border-danger"
},
size: {
xs: "h-6 w-6 [&>svg]:size-4 rounded-[5px] [&>svg]:stroke-[1.75]",
sm: "h-8 w-8 [&>svg]:size-5 [&>svg]:stroke-[1.5]",
xs: "h-7 w-7 [&>svg]:size-3.5 [&>svg]:stroke-[1.75]",
sm: "h-8 w-8 [&>svg]:size-4 [&>svg]:stroke-[1.5]",
md: "h-9 w-9 [&>svg]:size-6 [&>svg]:stroke-[1.5]",
lg: "h-10 w-10 [&>svg]:size-7 [&>svg]:stroke-[1.5]"
},

View File

@@ -8,7 +8,7 @@ function UnstableTable({ className, ...props }: React.ComponentProps<"table">) {
return (
<div
data-slot="table-container"
className="relative w-full overflow-x-auto border border-border/50 bg-mineshaft-800/50"
className="relative w-full overflow-x-auto border border-border bg-container"
>
<table
data-slot="table"
@@ -39,10 +39,7 @@ function UnstableTableFooter({ className, ...props }: React.ComponentProps<"tfoo
return (
<tfoot
data-slot="table-footer"
className={cn(
"border-t border-border/50 bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
className={cn("border-t border-border font-medium [&>tr]:last:border-b-0", className)}
{...props}
/>
);
@@ -53,7 +50,7 @@ function UnstableTableRow({ className, ...props }: React.ComponentProps<"tr">) {
<tr
data-slot="table-row"
className={cn(
"border-b border-border/50 transition-colors hover:bg-foreground/5 data-[state=selected]:bg-foreground/5",
"border-b border-border transition-colors hover:bg-foreground/5 data-[state=selected]:bg-foreground/5",
className
)}
{...props}
@@ -66,7 +63,7 @@ function UnstableTableHead({ className, ...props }: React.ComponentProps<"th">)
<th
data-slot="table-head"
className={cn(
"h-[30px] border-x-0 border-t-0 border-b border-border/50 px-3 text-left align-middle text-xs whitespace-nowrap text-accent text-mineshaft-400 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
"h-[30px] border-x-0 border-t-0 border-b border-border px-3 text-left align-middle text-xs whitespace-nowrap text-accent [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className
)}
{...props}
@@ -91,7 +88,7 @@ function UnstableTableCaption({ className, ...props }: React.ComponentProps<"cap
return (
<caption
data-slot="table-caption"
className={cn("mt-4 text-sm text-muted-foreground", className)}
className={cn("text-muted-foreground mt-4 text-sm", className)}
{...props}
/>
);

View File

@@ -1,3 +1,4 @@
export * from "./Accordion";
export * from "./Alert";
export * from "./Badge";
export * from "./Button";

View File

@@ -18,7 +18,7 @@ export type TIdentity = {
updatedAt: string;
hasDeleteProtection: boolean;
authMethods: IdentityAuthMethod[];
activeLockoutAuthMethods: string[];
activeLockoutAuthMethods: IdentityAuthMethod[];
metadata?: Array<TMetadata & { id: string }>;
};

View File

@@ -49,14 +49,14 @@
--color-sub-org: #96ff59;
--color-project: #e0ed34;
--color-neutral: #adaeb0;
--color-border: #323439;
--color-border: #2b2c30;
--color-label: #adaeb0;
--color-muted: #707174;
--color-popover: #111419;
--color-popover: #141617;
--color-ring: #2d2f33;
--color-container: #16181a;
--color-card: #16181a;
--color-accent: #7d7f80;
--color-muted-foreground: ;
--color-container: #1a1c1e;
/*legacy color schema */
--color-org-v1: #30b3ff;

View File

@@ -17,7 +17,6 @@ import {
} 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";
@@ -125,14 +124,16 @@ const Page = () => {
identityId={identityId}
handlePopUpOpen={handlePopUpOpen}
/>
</div>
<div className="flex flex-1 flex-col gap-y-4">
{!isAuthHidden && (
<IdentityAuthenticationSection
identityId={identityId}
handlePopUpOpen={handlePopUpOpen}
/>
)}
<IdentityProjectsSection identityId={identityId} />
</div>
<IdentityProjectsSection identityId={identityId} />
</div>
</div>
)}
@@ -171,14 +172,6 @@ const Page = () => {
)
}
/>
<ViewIdentityAuthModal
isOpen={popUp.viewAuthMethod.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("viewAuthMethod", isOpen)}
authMethod={popUp.viewAuthMethod.data?.authMethod}
lockedOut={popUp.viewAuthMethod.data?.lockedOut || false}
identityId={identityId}
onResetAllLockouts={popUp.viewAuthMethod.data?.refetchIdentity}
/>
</div>
);
};

View File

@@ -1,16 +1,14 @@
import { faCog, faLock, faPlus } from "@fortawesome/free-solid-svg-icons";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button, Tooltip } from "@app/components/v2";
import { Button } from "@app/components/v2";
import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/context";
import {
IdentityAuthMethod,
identityAuthToNameMap,
useGetOrgIdentityMembershipById
} from "@app/hooks/api";
import { IdentityAuthMethod, useGetOrgIdentityMembershipById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { ViewIdentityAuth } from "../ViewIdentityAuth";
type Props = {
identityId: string;
handlePopUpOpen: (
@@ -23,37 +21,42 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
const { data, refetch } = useGetOrgIdentityMembershipById(identityId);
return data ? (
<div className="mt-4 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">
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-medium text-mineshaft-100">Authentication</h3>
{!Object.values(IdentityAuthMethod).every((method) =>
data.identity.authMethods.includes(method)
) && (
<OrgPermissionCan
I={OrgPermissionIdentityActions.Edit}
a={OrgPermissionSubjects.Identity}
>
{(isAllowed) => (
<Button
isDisabled={!isAllowed}
onClick={() => {
handlePopUpOpen("identityAuthMethod", {
identityId,
name: data.identity.name,
allAuthMethods: data.identity.authMethods
});
}}
variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
{data.identity.authMethods.length ? "Add" : "Create"} Auth Method
</Button>
)}
</OrgPermissionCan>
)}
</div>
{data.identity.authMethods.length > 0 ? (
<div className="flex flex-col divide-y divide-mineshaft-400/50">
{data.identity.authMethods.map((authMethod) => (
<button
key={authMethod}
onClick={() =>
handlePopUpOpen("viewAuthMethod", {
authMethod,
lockedOut: data.identity.activeLockoutAuthMethods.includes(authMethod),
refetchIdentity: refetch
})
}
type="button"
className="flex w-full items-center justify-between bg-mineshaft-900 px-4 py-2 text-sm hover:bg-mineshaft-700 data-[state=open]:bg-mineshaft-600"
>
<span>{identityAuthToNameMap[authMethod]}</span>
<div className="flex gap-2">
{data.identity.activeLockoutAuthMethods.includes(authMethod) && (
<Tooltip content="Auth method has active lockouts">
<FontAwesomeIcon icon={faLock} size="xs" className="text-red-400/50" />
</Tooltip>
)}
<FontAwesomeIcon icon={faCog} size="xs" className="text-mineshaft-400" />
</div>
</button>
))}
</div>
<ViewIdentityAuth
activeLockoutAuthMethods={data.identity.activeLockoutAuthMethods}
identityId={identityId}
authMethods={data.identity.authMethods}
onResetAllLockouts={refetch}
/>
) : (
<div className="w-full space-y-2 pt-2">
<p className="text-sm text-mineshaft-300">
@@ -61,30 +64,6 @@ export const IdentityAuthenticationSection = ({ identityId, handlePopUpOpen }: P
</p>
</div>
)}
{!Object.values(IdentityAuthMethod).every((method) =>
data.identity.authMethods.includes(method)
) && (
<OrgPermissionCan I={OrgPermissionIdentityActions.Edit} a={OrgPermissionSubjects.Identity}>
{(isAllowed) => (
<Button
isDisabled={!isAllowed}
onClick={() => {
handlePopUpOpen("identityAuthMethod", {
identityId,
name: data.identity.name,
allAuthMethods: data.identity.authMethods
});
}}
variant="outline_bg"
className="mt-3 w-full"
size="xs"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
{data.identity.authMethods.length ? "Add" : "Create"} Auth Method
</Button>
)}
</OrgPermissionCan>
)}
</div>
) : (
<div />

View File

@@ -0,0 +1,24 @@
import { ReactNode } from "react";
import { Detail, DetailLabel, DetailValue } from "@app/components/v3";
type Props = {
label: string;
children: ReactNode;
className?: string;
};
export const IdentityAuthFieldDisplay = ({ label, children, className }: Props) => {
return (
<Detail className={className}>
<DetailLabel>{label}</DetailLabel>
<DetailValue>
{children ? (
<p className="break-words">{children}</p>
) : (
<p className="text-muted italic">Not set</p>
)}
</DetailValue>
</Detail>
);
};

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityAliCloudAuth } from "@app/hooks/api";
import { IdentityAliCloudAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAliCloudAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -10,10 +9,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityAliCloudAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityAliCloudAuth(identityId);
@@ -34,23 +31,8 @@ export const ViewIdentityAliCloudAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityAliCloudAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -0,0 +1,276 @@
import { useParams } from "@tanstack/react-router";
import { EllipsisIcon, LockIcon } from "lucide-react";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { DeleteActionModal, Modal, ModalContent, Tooltip } from "@app/components/v2";
import {
Badge,
UnstableAccordion,
UnstableAccordionContent,
UnstableAccordionItem,
UnstableAccordionTrigger,
UnstableDropdownMenu,
UnstableDropdownMenuContent,
UnstableDropdownMenuItem,
UnstableDropdownMenuTrigger,
UnstableIconButton
} from "@app/components/v3";
import { useOrganization } from "@app/context";
import { usePopUp } from "@app/hooks";
import {
IdentityAuthMethod,
identityAuthToNameMap,
useDeleteIdentityAliCloudAuth,
useDeleteIdentityAwsAuth,
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityJwtAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityLdapAuth,
useDeleteIdentityOciAuth,
useDeleteIdentityOidcAuth,
useDeleteIdentityTlsCertAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth
} from "@app/hooks/api";
import { IdentityAliCloudAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAliCloudAuthForm";
import { IdentityAwsAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAwsAuthForm";
import { IdentityAzureAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAzureAuthForm";
import { IdentityGcpAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityGcpAuthForm";
import { IdentityJwtAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityJwtAuthForm";
import { IdentityKubernetesAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityKubernetesAuthForm";
import { IdentityLdapAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityLdapAuthForm";
import { IdentityOciAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm";
import { IdentityOidcAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOidcAuthForm";
import { IdentityTlsCertAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityTlsCertAuthForm";
import { IdentityTokenAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityTokenAuthForm";
import { IdentityUniversalAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm";
import { ViewIdentityAliCloudAuthContent } from "./ViewIdentityAliCloudAuthContent";
import { ViewIdentityAwsAuthContent } from "./ViewIdentityAwsAuthContent";
import { ViewIdentityAzureAuthContent } from "./ViewIdentityAzureAuthContent";
import { ViewIdentityGcpAuthContent } from "./ViewIdentityGcpAuthContent";
import { ViewIdentityJwtAuthContent } from "./ViewIdentityJwtAuthContent";
import { ViewIdentityKubernetesAuthContent } from "./ViewIdentityKubernetesAuthContent";
import { ViewIdentityLdapAuthContent } from "./ViewIdentityLdapAuthContent";
import { ViewIdentityOciAuthContent } from "./ViewIdentityOciAuthContent";
import { ViewIdentityOidcAuthContent } from "./ViewIdentityOidcAuthContent";
import { ViewIdentityTlsCertAuthContent } from "./ViewIdentityTlsCertAuthContent";
import { ViewIdentityTokenAuthContent } from "./ViewIdentityTokenAuthContent";
import { ViewIdentityUniversalAuthContent } from "./ViewIdentityUniversalAuthContent";
type Props = {
identityId: string;
authMethods: IdentityAuthMethod[];
onResetAllLockouts: () => void;
activeLockoutAuthMethods: IdentityAuthMethod[];
};
const AuthMethodComponentMap = {
[IdentityAuthMethod.UNIVERSAL_AUTH]: ViewIdentityUniversalAuthContent,
[IdentityAuthMethod.TOKEN_AUTH]: ViewIdentityTokenAuthContent,
[IdentityAuthMethod.TLS_CERT_AUTH]: ViewIdentityTlsCertAuthContent,
[IdentityAuthMethod.KUBERNETES_AUTH]: ViewIdentityKubernetesAuthContent,
[IdentityAuthMethod.LDAP_AUTH]: ViewIdentityLdapAuthContent,
[IdentityAuthMethod.OCI_AUTH]: ViewIdentityOciAuthContent,
[IdentityAuthMethod.OIDC_AUTH]: ViewIdentityOidcAuthContent,
[IdentityAuthMethod.GCP_AUTH]: ViewIdentityGcpAuthContent,
[IdentityAuthMethod.AWS_AUTH]: ViewIdentityAwsAuthContent,
[IdentityAuthMethod.ALICLOUD_AUTH]: ViewIdentityAliCloudAuthContent,
[IdentityAuthMethod.AZURE_AUTH]: ViewIdentityAzureAuthContent,
[IdentityAuthMethod.JWT_AUTH]: ViewIdentityJwtAuthContent
};
const EditAuthMethodMap = {
[IdentityAuthMethod.KUBERNETES_AUTH]: IdentityKubernetesAuthForm,
[IdentityAuthMethod.GCP_AUTH]: IdentityGcpAuthForm,
[IdentityAuthMethod.TLS_CERT_AUTH]: IdentityTlsCertAuthForm,
[IdentityAuthMethod.AWS_AUTH]: IdentityAwsAuthForm,
[IdentityAuthMethod.AZURE_AUTH]: IdentityAzureAuthForm,
[IdentityAuthMethod.ALICLOUD_AUTH]: IdentityAliCloudAuthForm,
[IdentityAuthMethod.UNIVERSAL_AUTH]: IdentityUniversalAuthForm,
[IdentityAuthMethod.TOKEN_AUTH]: IdentityTokenAuthForm,
[IdentityAuthMethod.OCI_AUTH]: IdentityOciAuthForm,
[IdentityAuthMethod.OIDC_AUTH]: IdentityOidcAuthForm,
[IdentityAuthMethod.JWT_AUTH]: IdentityJwtAuthForm,
[IdentityAuthMethod.LDAP_AUTH]: IdentityLdapAuthForm
};
export const Content = ({
identityId,
authMethods,
onResetAllLockouts,
activeLockoutAuthMethods
}: Pick<
Props,
"authMethods" | "identityId" | "onResetAllLockouts" | "activeLockoutAuthMethods"
>) => {
const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || "";
const { projectId } = useParams({
strict: false
});
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"revokeAuthMethod",
"identityAuthMethod",
"upgradePlan"
] as const);
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
const { mutateAsync: revokeTlsCertAuth } = useDeleteIdentityTlsCertAuth();
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
const { mutateAsync: revokeAliCloudAuth } = useDeleteIdentityAliCloudAuth();
const { mutateAsync: revokeOciAuth } = useDeleteIdentityOciAuth();
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
const { mutateAsync: revokeJwtAuth } = useDeleteIdentityJwtAuth();
const { mutateAsync: revokeLdapAuth } = useDeleteIdentityLdapAuth();
const RemoveAuthMethodMap = {
[IdentityAuthMethod.KUBERNETES_AUTH]: revokeKubernetesAuth,
[IdentityAuthMethod.GCP_AUTH]: revokeGcpAuth,
[IdentityAuthMethod.TLS_CERT_AUTH]: revokeTlsCertAuth,
[IdentityAuthMethod.AWS_AUTH]: revokeAwsAuth,
[IdentityAuthMethod.AZURE_AUTH]: revokeAzureAuth,
[IdentityAuthMethod.ALICLOUD_AUTH]: revokeAliCloudAuth,
[IdentityAuthMethod.UNIVERSAL_AUTH]: revokeUniversalAuth,
[IdentityAuthMethod.TOKEN_AUTH]: revokeTokenAuth,
[IdentityAuthMethod.OCI_AUTH]: revokeOciAuth,
[IdentityAuthMethod.OIDC_AUTH]: revokeOidcAuth,
[IdentityAuthMethod.JWT_AUTH]: revokeJwtAuth,
[IdentityAuthMethod.LDAP_AUTH]: revokeLdapAuth
};
const handleDeleteAuthMethod = async (authMethod: IdentityAuthMethod) => {
await RemoveAuthMethodMap[authMethod]({
identityId,
...(projectId
? { projectId }
: {
organizationId: orgId
})
});
createNotification({
text: "Successfully removed auth method",
type: "success"
});
handlePopUpToggle("revokeAuthMethod", false);
};
const EditForm = popUp.identityAuthMethod?.data
? EditAuthMethodMap[popUp.identityAuthMethod.data as IdentityAuthMethod]
: null;
return (
<>
<UnstableAccordion type={authMethods.length === 1 ? "single" : "multiple"} collapsible>
{authMethods.map((authMethod) => {
const Component = AuthMethodComponentMap[authMethod];
return (
<UnstableAccordionItem key={authMethod} value={authMethod}>
<UnstableAccordionTrigger>
<span className="mr-auto">{identityAuthToNameMap[authMethod]}</span>
{activeLockoutAuthMethods?.includes(authMethod) && (
<Tooltip content="Auth method has active lockouts">
<Badge isSquare variant="danger">
<LockIcon />
</Badge>
</Tooltip>
)}
<UnstableDropdownMenu>
<UnstableDropdownMenuTrigger asChild>
<UnstableIconButton variant="ghost" size="xs">
<EllipsisIcon />
</UnstableIconButton>
</UnstableDropdownMenuTrigger>
<UnstableDropdownMenuContent align="end">
<UnstableDropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("identityAuthMethod", authMethod);
}}
>
Edit Auth Method
</UnstableDropdownMenuItem>
<UnstableDropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("revokeAuthMethod", authMethod);
}}
variant="danger"
>
Remove Auth Method
</UnstableDropdownMenuItem>
</UnstableDropdownMenuContent>
</UnstableDropdownMenu>
</UnstableAccordionTrigger>
<UnstableAccordionContent>
<Component
identityId={identityId}
onEdit={() => handlePopUpOpen("identityAuthMethod", authMethod)}
onDelete={() => handlePopUpOpen("revokeAuthMethod", authMethod)}
onResetAllLockouts={onResetAllLockouts}
lockedOut={activeLockoutAuthMethods?.includes(authMethod)}
/>
</UnstableAccordionContent>
</UnstableAccordionItem>
);
})}
</UnstableAccordion>
<DeleteActionModal
isOpen={popUp?.revokeAuthMethod?.isOpen}
title={`Are you sure you want to remove ${popUp?.revokeAuthMethod?.data ? identityAuthToNameMap[popUp?.revokeAuthMethod?.data as IdentityAuthMethod] : "this auth method"} on this identity?`}
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
deleteKey="confirm"
buttonText="Remove"
onDeleteApproved={() =>
handleDeleteAuthMethod(popUp?.revokeAuthMethod?.data as IdentityAuthMethod)
}
/>
<Modal
isOpen={popUp?.identityAuthMethod?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("identityAuthMethod", isOpen)}
>
<ModalContent
title={`Edit ${popUp?.identityAuthMethod?.data ? identityAuthToNameMap[popUp?.identityAuthMethod?.data as IdentityAuthMethod] : "this auth method"}`}
>
{EditForm && (
<EditForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
)}
</ModalContent>
</Modal>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={`Your current plan does not include access to ${popUp.upgradePlan.data?.featureName}. To unlock this feature, please upgrade to Infisical ${popUp.upgradePlan.data?.isEnterpriseFeature ? "Enterprise" : "Pro"} plan.`}
/>
</>
);
};
export const ViewIdentityAuth = ({
authMethods,
identityId,
onResetAllLockouts,
activeLockoutAuthMethods
}: Props) => {
return (
<Content
identityId={identityId}
authMethods={authMethods}
activeLockoutAuthMethods={activeLockoutAuthMethods}
onResetAllLockouts={() => onResetAllLockouts()}
/>
);
};

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityAwsAuth } from "@app/hooks/api";
import { IdentityAwsAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAwsAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -10,10 +9,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityAwsAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityAwsAuth(identityId);
@@ -31,23 +28,8 @@ export const ViewIdentityAwsAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityAwsAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityAzureAuth } from "@app/hooks/api";
import { IdentityAzureAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAzureAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -10,10 +9,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityAzureAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityAzureAuth(identityId);
@@ -31,23 +28,8 @@ export const ViewIdentityAzureAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityAzureAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityGcpAuth } from "@app/hooks/api";
import { IdentityGcpAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityGcpAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -10,10 +9,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityGcpAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityGcpAuth(identityId);
@@ -31,23 +28,8 @@ export const ViewIdentityGcpAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityGcpAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -5,18 +5,15 @@ import { EmptyState, Spinner, Tooltip } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { useGetIdentityJwtAuth } from "@app/hooks/api";
import { IdentityJwtConfigurationType } from "@app/hooks/api/identities/enums";
import { IdentityJwtAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityJwtAuthForm";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuth/ViewIdentityContentWrapper";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
export const ViewIdentityJwtAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityJwtAuth(identityId);
@@ -34,23 +31,8 @@ export const ViewIdentityJwtAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityJwtAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -6,7 +6,6 @@ import { EyeIcon } from "lucide-react";
import { EmptyState, Spinner, Tooltip } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { gatewaysQueryKeys, useGetIdentityKubernetesAuth } from "@app/hooks/api";
import { IdentityKubernetesAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityKubernetesAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -14,10 +13,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityKubernetesAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data: gateways } = useQuery(gatewaysQueryKeys.list());
@@ -44,23 +41,8 @@ export const ViewIdentityKubernetesAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityKubernetesAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -4,8 +4,7 @@ import { EyeIcon } from "lucide-react";
import { EmptyState, Spinner, Tooltip } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { useClearIdentityLdapAuthLockouts, useGetIdentityLdapAuth } from "@app/hooks/api";
import { IdentityLdapAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityLdapAuthForm";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuth/ViewIdentityContentWrapper";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { LockoutFields } from "./IdentityAuthLockoutFields";
@@ -13,10 +12,8 @@ import { ViewAuthMethodProps } from "./types";
export const ViewIdentityLdapAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp,
onEdit,
lockedOut,
onResetAllLockouts
}: ViewAuthMethodProps) => {
@@ -37,23 +34,8 @@ export const ViewIdentityLdapAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityLdapAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityOciAuth } from "@app/hooks/api";
import { IdentityOciAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOciAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -10,10 +9,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityOciAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityOciAuth(identityId);
@@ -31,23 +28,8 @@ export const ViewIdentityOciAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityOciAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -4,18 +4,15 @@ import { EyeIcon } from "lucide-react";
import { EmptyState, Spinner, Tooltip } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { useGetIdentityOidcAuth } from "@app/hooks/api";
import { IdentityOidcAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOidcAuthForm";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuth/ViewIdentityContentWrapper";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
export const ViewIdentityOidcAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityOidcAuth(identityId);
@@ -33,23 +30,8 @@ export const ViewIdentityOidcAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityOidcAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -4,7 +4,6 @@ import { EyeIcon } from "lucide-react";
import { EmptyState, Spinner, Tooltip } from "@app/components/v2";
import { Badge } from "@app/components/v3";
import { useGetIdentityTlsCertAuth } from "@app/hooks/api";
import { IdentityTlsCertAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityTlsCertAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { ViewAuthMethodProps } from "./types";
@@ -12,10 +11,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityTlsCertAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit,
onDelete
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityTlsCertAuth(identityId);
@@ -36,23 +33,8 @@ export const ViewIdentityTlsCertAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityTlsCertAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -2,7 +2,6 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
import { EmptyState, Spinner } from "@app/components/v2";
import { useGetIdentityTokenAuth, useGetIdentityTokensTokenAuth } from "@app/hooks/api";
import { IdentityTokenAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityTokenAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { IdentityTokenAuthTokensTable } from "./IdentityTokenAuthTokensTable";
@@ -11,10 +10,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityTokenAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp
onEdit
}: ViewAuthMethodProps) => {
const { data, isPending } = useGetIdentityTokenAuth(identityId);
const { data: tokens = [], isPending: clientSecretsPending } =
@@ -34,23 +31,8 @@ export const ViewIdentityTokenAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityTokenAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
<IdentityAuthFieldDisplay label="Access Token TTL (seconds)">
{data.accessTokenTTL}
</IdentityAuthFieldDisplay>

View File

@@ -8,7 +8,6 @@ import {
useGetIdentityUniversalAuth,
useGetIdentityUniversalAuthClientSecrets
} from "@app/hooks/api";
import { IdentityUniversalAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm";
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
import { LockoutFields } from "./IdentityAuthLockoutFields";
@@ -18,10 +17,8 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
export const ViewIdentityUniversalAuthContent = ({
identityId,
handlePopUpToggle,
handlePopUpOpen,
onDelete,
popUp,
onEdit,
lockedOut,
onResetAllLockouts
}: ViewAuthMethodProps) => {
@@ -51,23 +48,8 @@ export const ViewIdentityUniversalAuthContent = ({
);
}
if (popUp.identityAuthMethod.isOpen) {
return (
<IdentityUniversalAuthForm
identityId={identityId}
isUpdate
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
/>
);
}
return (
<ViewIdentityContentWrapper
onEdit={() => handlePopUpOpen("identityAuthMethod")}
onDelete={onDelete}
identityId={identityId}
>
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete} identityId={identityId}>
{Number(data.accessTokenPeriod) > 0 ? (
<IdentityAuthFieldDisplay label="Access Token Period (seconds)">
{data.accessTokenPeriod}

View File

@@ -0,0 +1 @@
export * from "./ViewIdentityAuth";

View File

@@ -0,0 +1,7 @@
export type ViewAuthMethodProps = {
identityId: string;
onDelete: () => void;
onEdit: () => void;
lockedOut: boolean;
onResetAllLockouts: () => void;
};

View File

@@ -1,20 +0,0 @@
import { ReactNode } from "react";
type Props = {
label: string;
children: ReactNode;
className?: string;
};
export const IdentityAuthFieldDisplay = ({ label, children, className }: Props) => {
return (
<div className={className}>
<span className="text-sm text-mineshaft-400">{label}</span>
{children ? (
<p className="text-base leading-4 break-words">{children}</p>
) : (
<p className="text-base leading-4 text-bunker-400 italic">Not set</p>
)}
</div>
);
};

View File

@@ -1,214 +0,0 @@
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";
import { useOrganization } from "@app/context";
import { usePopUp } from "@app/hooks";
import {
IdentityAuthMethod,
identityAuthToNameMap,
useDeleteIdentityAliCloudAuth,
useDeleteIdentityAwsAuth,
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityJwtAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityLdapAuth,
useDeleteIdentityOciAuth,
useDeleteIdentityOidcAuth,
useDeleteIdentityTlsCertAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth
} from "@app/hooks/api";
import { ViewAuthMethodProps } from "./types";
import { ViewIdentityAliCloudAuthContent } from "./ViewIdentityAliCloudAuthContent";
import { ViewIdentityAwsAuthContent } from "./ViewIdentityAwsAuthContent";
import { ViewIdentityAzureAuthContent } from "./ViewIdentityAzureAuthContent";
import { ViewIdentityGcpAuthContent } from "./ViewIdentityGcpAuthContent";
import { ViewIdentityJwtAuthContent } from "./ViewIdentityJwtAuthContent";
import { ViewIdentityKubernetesAuthContent } from "./ViewIdentityKubernetesAuthContent";
import { ViewIdentityLdapAuthContent } from "./ViewIdentityLdapAuthContent";
import { ViewIdentityOciAuthContent } from "./ViewIdentityOciAuthContent";
import { ViewIdentityOidcAuthContent } from "./ViewIdentityOidcAuthContent";
import { ViewIdentityTlsCertAuthContent } from "./ViewIdentityTlsCertAuthContent";
import { ViewIdentityTokenAuthContent } from "./ViewIdentityTokenAuthContent";
import { ViewIdentityUniversalAuthContent } from "./ViewIdentityUniversalAuthContent";
type Props = {
identityId: string;
authMethod?: IdentityAuthMethod;
lockedOut: boolean;
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
onDeleteAuthMethod: () => void;
onResetAllLockouts: () => void;
};
type TRevokeOptions = {
identityId: string;
} & ({ projectId: string } | { organizationId: string });
export const Content = ({
identityId,
authMethod,
lockedOut,
onDeleteAuthMethod,
onResetAllLockouts
}: Pick<
Props,
"authMethod" | "lockedOut" | "identityId" | "onDeleteAuthMethod" | "onResetAllLockouts"
>) => {
const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || "";
const { projectId } = useParams({
strict: false
});
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
"revokeAuthMethod",
"upgradePlan",
"identityAuthMethod"
] as const);
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
const { mutateAsync: revokeTlsCertAuth } = useDeleteIdentityTlsCertAuth();
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
const { mutateAsync: revokeAliCloudAuth } = useDeleteIdentityAliCloudAuth();
const { mutateAsync: revokeOciAuth } = useDeleteIdentityOciAuth();
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
const { mutateAsync: revokeJwtAuth } = useDeleteIdentityJwtAuth();
const { mutateAsync: revokeLdapAuth } = useDeleteIdentityLdapAuth();
let Component: (props: ViewAuthMethodProps) => JSX.Element;
let revokeMethod: (revokeOptions: TRevokeOptions) => Promise<any>;
const handleDelete = () => handlePopUpOpen("revokeAuthMethod");
switch (authMethod) {
case IdentityAuthMethod.UNIVERSAL_AUTH:
revokeMethod = revokeUniversalAuth;
Component = ViewIdentityUniversalAuthContent;
break;
case IdentityAuthMethod.TOKEN_AUTH:
revokeMethod = revokeTokenAuth;
Component = ViewIdentityTokenAuthContent;
break;
case IdentityAuthMethod.KUBERNETES_AUTH:
revokeMethod = revokeKubernetesAuth;
Component = ViewIdentityKubernetesAuthContent;
break;
case IdentityAuthMethod.GCP_AUTH:
revokeMethod = revokeGcpAuth;
Component = ViewIdentityGcpAuthContent;
break;
case IdentityAuthMethod.TLS_CERT_AUTH:
revokeMethod = revokeTlsCertAuth;
Component = ViewIdentityTlsCertAuthContent;
break;
case IdentityAuthMethod.AWS_AUTH:
revokeMethod = revokeAwsAuth;
Component = ViewIdentityAwsAuthContent;
break;
case IdentityAuthMethod.AZURE_AUTH:
revokeMethod = revokeAzureAuth;
Component = ViewIdentityAzureAuthContent;
break;
case IdentityAuthMethod.OCI_AUTH:
revokeMethod = revokeOciAuth;
Component = ViewIdentityOciAuthContent;
break;
case IdentityAuthMethod.ALICLOUD_AUTH:
revokeMethod = revokeAliCloudAuth;
Component = ViewIdentityAliCloudAuthContent;
break;
case IdentityAuthMethod.OIDC_AUTH:
revokeMethod = revokeOidcAuth;
Component = ViewIdentityOidcAuthContent;
break;
case IdentityAuthMethod.JWT_AUTH:
revokeMethod = revokeJwtAuth;
Component = ViewIdentityJwtAuthContent;
break;
case IdentityAuthMethod.LDAP_AUTH:
revokeMethod = revokeLdapAuth;
Component = ViewIdentityLdapAuthContent;
break;
default:
throw new Error(`Unhandled Auth Method: ${authMethod}`);
}
const handleDeleteAuthMethod = async () => {
await revokeMethod({
identityId,
...(projectId
? { projectId }
: {
organizationId: orgId
})
});
createNotification({
text: "Successfully removed auth method",
type: "success"
});
handlePopUpToggle("revokeAuthMethod", false);
onDeleteAuthMethod();
};
return (
<>
<Component
identityId={identityId}
onDelete={handleDelete}
onResetAllLockouts={onResetAllLockouts}
popUp={popUp}
handlePopUpOpen={handlePopUpOpen}
handlePopUpToggle={handlePopUpToggle}
lockedOut={lockedOut}
/>
<DeleteActionModal
isOpen={popUp?.revokeAuthMethod?.isOpen}
title={`Are you sure you want to remove ${identityAuthToNameMap[authMethod]} on this identity?`}
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
deleteKey="confirm"
buttonText="Remove"
onDeleteApproved={handleDeleteAuthMethod}
/>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={`Your current plan does not include access to ${popUp.upgradePlan.data?.featureName}. To unlock this feature, please upgrade to Infisical ${popUp.upgradePlan.data?.isEnterpriseFeature ? "Enterprise" : "Pro"} plan.`}
/>
</>
);
};
export const ViewIdentityAuthModal = ({
isOpen,
onOpenChange,
authMethod,
identityId,
lockedOut,
onResetAllLockouts
}: Omit<Props, "onDeleteAuthMethod">) => {
if (!identityId || !authMethod) return null;
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent className="max-w-2xl" title={identityAuthToNameMap[authMethod]}>
<Content
identityId={identityId}
authMethod={authMethod}
lockedOut={lockedOut}
onDeleteAuthMethod={() => onOpenChange(false)}
onResetAllLockouts={() => onResetAllLockouts()}
/>
</ModalContent>
</Modal>
);
};

View File

@@ -1 +0,0 @@
export * from "./ViewIdentityAuthModal";

View File

@@ -1,14 +0,0 @@
import { UsePopUpState } from "@app/hooks/usePopUp";
export type ViewAuthMethodProps = {
identityId: string;
onDelete: () => void;
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan", "identityAuthMethod"]>) => void;
handlePopUpToggle: (
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
state?: boolean
) => void;
popUp: UsePopUpState<["revokeAuthMethod", "upgradePlan", "identityAuthMethod"]>;
lockedOut: boolean;
onResetAllLockouts: () => void;
};

View File

@@ -179,14 +179,13 @@ const Page = () => {
search={{
selectedTab: ProjectAccessControlTabs.Identities
}}
className="mb-3 flex w-fit items-center gap-x-1 text-sm text-mineshaft-400 transition duration-100 hover:text-mineshaft-400/80"
className="mb-4 flex w-fit items-center gap-x-1 text-sm text-mineshaft-400 transition duration-100 hover:text-mineshaft-400/80"
>
<ChevronLeftIcon size={16} />
Project Machine Identities
</Link>
<PageHeader
scope={currentProject.type}
className="mb-20"
description={`Configure and manage${isProjectIdentity ? " machine identity and " : " "}project access control`}
title={identityMembershipDetails.identity.name}
>
@@ -256,14 +255,14 @@ const Page = () => {
</UnstableDropdownMenuContent>
</DropdownMenu>
</PageHeader>
<div className="flex flex-col gap-20 lg:flex-row">
<div className="flex flex-col gap-5 lg:flex-row">
<ProjectIdentityDetailsSection
identity={identity || { ...identityMembershipDetails?.identity, projectId: "" }}
isOrgIdentity={!isProjectIdentity}
membership={identityMembershipDetails!}
/>
<div className="flex flex-1 flex-col gap-y-20">
<div className="flex flex-1 flex-col gap-y-5">
{identity ? (
<ProjectIdentityAuthenticationSection
identity={identity}

View File

@@ -81,7 +81,9 @@ export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDe
return (
<>
<UnstableCard>
<UnstableCardHeader className="border-b">
<UnstableCardHeader
// className="border-b"
>
<UnstableCardTitle>Project Additional Privileges</UnstableCardTitle>
<UnstableCardDescription>
Assign one-off policies to this machine identity
@@ -96,7 +98,7 @@ export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDe
>
{(isAllowed) => (
<UnstableButton
variant="project"
variant="outline"
size="xs"
onClick={() => {
handlePopUpOpen("modifyPrivilege");

View File

@@ -96,7 +96,9 @@ export const IdentityRoleDetailsSection = ({
return (
<>
<UnstableCard>
<UnstableCardHeader className="border-b">
<UnstableCardHeader
// className="border-b"
>
<UnstableCardTitle>Project Roles</UnstableCardTitle>
<UnstableCardDescription>
Manage roles assigned to this machine identity
@@ -112,7 +114,7 @@ export const IdentityRoleDetailsSection = ({
{(isAllowed) => (
<UnstableButton
size="xs"
variant="project"
variant="outline"
onClick={() => {
handlePopUpOpen("modifyRole");
}}

View File

@@ -1,11 +1,11 @@
import { subject } from "@casl/ability";
import { EllipsisIcon, LockIcon, PlusIcon } from "lucide-react";
import { PlusIcon } from "lucide-react";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Tooltip } from "@app/components/v2";
import { DeleteActionModal } from "@app/components/v2";
import {
Badge,
UnstableButton,
UnstableCard,
UnstableCardAction,
@@ -17,20 +17,29 @@ import {
UnstableEmptyContent,
UnstableEmptyDescription,
UnstableEmptyHeader,
UnstableEmptyTitle,
UnstableIconButton,
UnstableTable,
UnstableTableBody,
UnstableTableCell,
UnstableTableHead,
UnstableTableHeader,
UnstableTableRow
UnstableEmptyTitle
} from "@app/components/v3";
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "@app/context";
import { IdentityAuthMethod, identityAuthToNameMap, TProjectIdentity } from "@app/hooks/api";
import {
IdentityAuthMethod,
identityAuthToNameMap,
TProjectIdentity,
useDeleteIdentityAliCloudAuth,
useDeleteIdentityAwsAuth,
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityJwtAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityLdapAuth,
useDeleteIdentityOciAuth,
useDeleteIdentityOidcAuth,
useDeleteIdentityTlsCertAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth
} from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAuthMethodModal } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
import { ViewIdentityAuthModal } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal";
import { ViewIdentityAuth } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuth";
type Props = {
identity: TProjectIdentity;
@@ -39,17 +48,58 @@ type Props = {
export const ProjectIdentityAuthenticationSection = ({ identity, refetchIdentity }: Props) => {
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([
"viewAuthMethod",
"identityAuthMethod",
"upgradePlan"
"upgradePlan",
"revokeAuthMethod"
]);
const hasAuthMethods = Boolean(identity.authMethods.length);
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
const { mutateAsync: revokeTlsCertAuth } = useDeleteIdentityTlsCertAuth();
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
const { mutateAsync: revokeAliCloudAuth } = useDeleteIdentityAliCloudAuth();
const { mutateAsync: revokeOciAuth } = useDeleteIdentityOciAuth();
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
const { mutateAsync: revokeJwtAuth } = useDeleteIdentityJwtAuth();
const { mutateAsync: revokeLdapAuth } = useDeleteIdentityLdapAuth();
const RemoveAuthMap = {
[IdentityAuthMethod.KUBERNETES_AUTH]: revokeKubernetesAuth,
[IdentityAuthMethod.GCP_AUTH]: revokeGcpAuth,
[IdentityAuthMethod.TLS_CERT_AUTH]: revokeTlsCertAuth,
[IdentityAuthMethod.AWS_AUTH]: revokeAwsAuth,
[IdentityAuthMethod.AZURE_AUTH]: revokeAzureAuth,
[IdentityAuthMethod.ALICLOUD_AUTH]: revokeAliCloudAuth,
[IdentityAuthMethod.UNIVERSAL_AUTH]: revokeUniversalAuth,
[IdentityAuthMethod.TOKEN_AUTH]: revokeTokenAuth,
[IdentityAuthMethod.OCI_AUTH]: revokeOciAuth,
[IdentityAuthMethod.OIDC_AUTH]: revokeOidcAuth,
[IdentityAuthMethod.JWT_AUTH]: revokeJwtAuth,
[IdentityAuthMethod.LDAP_AUTH]: revokeLdapAuth
};
const handleDeleteAuthMethod = async (authMethod: IdentityAuthMethod) => {
await RemoveAuthMap[authMethod]({
identityId: identity.id,
projectId: identity.projectId!
});
createNotification({
text: "Successfully removed auth method",
type: "success"
});
handlePopUpToggle("revokeAuthMethod", false);
};
return (
<>
<UnstableCard>
<UnstableCardHeader className="border-b">
<UnstableCardHeader>
<UnstableCardTitle>Authentication</UnstableCardTitle>
<UnstableCardDescription>Configure authentication methods</UnstableCardDescription>
{hasAuthMethods &&
@@ -65,7 +115,7 @@ export const ProjectIdentityAuthenticationSection = ({ identity, refetchIdentity
>
{(isAllowed) => (
<UnstableButton
variant="project"
variant="outline"
isFullWidth
size="xs"
isDisabled={!isAllowed}
@@ -87,43 +137,12 @@ export const ProjectIdentityAuthenticationSection = ({ identity, refetchIdentity
</UnstableCardHeader>
<UnstableCardContent>
{identity.authMethods.length > 0 ? (
<UnstableTable>
<UnstableTableHeader>
<UnstableTableHead className="w-full">Method</UnstableTableHead>
<UnstableTableHead className="w-5" />
</UnstableTableHeader>
<UnstableTableBody>
{identity.authMethods.map((authMethod) => (
<UnstableTableRow
key={authMethod}
className="cursor-pointer"
onClick={() =>
handlePopUpOpen("viewAuthMethod", {
authMethod,
lockedOut: identity.activeLockoutAuthMethods?.includes(authMethod) ?? false,
refetchIdentity
})
}
>
<UnstableTableCell>{identityAuthToNameMap[authMethod]}</UnstableTableCell>
<UnstableTableCell>
<div className="flex items-center gap-2">
{identity.activeLockoutAuthMethods?.includes(authMethod) && (
<Tooltip content="Auth method has active lockouts">
<Badge isSquare variant="danger">
<LockIcon />
</Badge>
</Tooltip>
)}
<UnstableIconButton variant="ghost" size="xs">
<EllipsisIcon />
</UnstableIconButton>
</div>
</UnstableTableCell>
</UnstableTableRow>
))}
</UnstableTableBody>
</UnstableTable>
<ViewIdentityAuth
authMethods={identity.authMethods}
identityId={identity.id}
onResetAllLockouts={refetchIdentity}
activeLockoutAuthMethods={identity.activeLockoutAuthMethods}
/>
) : (
<UnstableEmpty className="rounded-sm border bg-mineshaft-800/50">
<UnstableEmptyHeader>
@@ -175,13 +194,15 @@ export const ProjectIdentityAuthenticationSection = ({ identity, refetchIdentity
text={(popUp.upgradePlan?.data as { description: string })?.description}
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
/>
<ViewIdentityAuthModal
isOpen={popUp.viewAuthMethod.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("viewAuthMethod", isOpen)}
authMethod={popUp.viewAuthMethod.data?.authMethod}
lockedOut={popUp.viewAuthMethod.data?.lockedOut || false}
identityId={identity.id}
onResetAllLockouts={popUp.viewAuthMethod.data?.refetchIdentity}
<DeleteActionModal
isOpen={popUp?.revokeAuthMethod?.isOpen}
title={`Are you sure you want to remove ${popUp?.revokeAuthMethod?.data ? identityAuthToNameMap[popUp.revokeAuthMethod.data as IdentityAuthMethod] : "this auth method"} on this identity?`}
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
deleteKey="confirm"
buttonText="Remove"
onDeleteApproved={() =>
handleDeleteAuthMethod(popUp.revokeAuthMethod.data as IdentityAuthMethod)
}
/>
</>
);

View File

@@ -12,7 +12,6 @@ import {
DetailValue,
OrgIcon,
ProjectIcon,
UnstableButton,
UnstableButtonGroup,
UnstableCard,
UnstableCardAction,
@@ -44,8 +43,10 @@ export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, members
return (
<>
<UnstableCard className="w-full max-w-84">
<UnstableCardHeader className="border-b">
<UnstableCard className="w-full max-w-[22rem]">
<UnstableCardHeader
// className="border-b"
>
<UnstableCardTitle>Details</UnstableCardTitle>
<UnstableCardDescription>Machine identity details</UnstableCardDescription>
{!isOrgIdentity && (
@@ -57,17 +58,16 @@ export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, members
})}
>
{(isAllowed) => (
<UnstableButton
<UnstableIconButton
isDisabled={!isAllowed}
onClick={() => {
handlePopUpOpen("editIdentity");
}}
size="xs"
variant="project"
variant="outline"
>
<PencilIcon />
Edit Details
</UnstableButton>
</UnstableIconButton>
)}
</ProjectPermissionCan>
</UnstableCardAction>
@@ -92,6 +92,7 @@ export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, members
variant="ghost"
size="xs"
>
{/* TODO(scott): color this should be a button variant */}
{isCopyingId ? <CheckIcon /> : <ClipboardListIcon className="text-label" />}
</UnstableIconButton>
</Tooltip>
@@ -118,9 +119,13 @@ export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, members
<DetailValue className="flex flex-wrap gap-2">
{identity?.metadata?.length ? (
identity.metadata?.map((el) => (
<UnstableButtonGroup key={el.id}>
<Badge>{el.key}</Badge>
<Badge variant="outline">{el.value}</Badge>
<UnstableButtonGroup className="min-w-0" key={el.id}>
<Badge isTruncatable>
<span>{el.key}</span>
</Badge>
<Badge variant="outline" isTruncatable>
<span>{el.value}</span>
</Badge>
</UnstableButtonGroup>
))
) : (