Merge pull request #2096 from Infisical/identity-project-provisioning

Identities Page - Project Provisioning / De-provisioning
This commit is contained in:
BlackMagiq
2024-07-10 23:08:21 +07:00
committed by GitHub
18 changed files with 467 additions and 113 deletions

View File

@@ -5,6 +5,7 @@ import {
IdentitiesSchema,
IdentityProjectMembershipsSchema,
ProjectMembershipRole,
ProjectsSchema,
ProjectUserMembershipRolesSchema
} from "@app/db/schemas";
import { PROJECT_IDENTITIES } from "@app/lib/api-docs";
@@ -234,7 +235,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }),
project: ProjectsSchema.pick({ name: true, id: true })
})
.array()
})
@@ -291,7 +293,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true })
identity: IdentitiesSchema.pick({ name: true, id: true, authMethod: true }),
project: ProjectsSchema.pick({ name: true, id: true })
})
})
}

View File

@@ -111,6 +111,7 @@ export const identityProjectDALFactory = (db: TDbClient) => {
try {
const docs = await (tx || db.replicaNode())(TableName.IdentityProjectMembership)
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
.where((qb) => {
if (filter.identityId) {
@@ -149,12 +150,13 @@ export const identityProjectDALFactory = (db: TDbClient) => {
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole)
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project)
);
const members = sqlNestRelationships({
data: docs,
parentMapper: ({ identityId, identityName, identityAuthMethod, id, createdAt, updatedAt }) => ({
parentMapper: ({ identityId, identityName, identityAuthMethod, id, createdAt, updatedAt, projectName }) => ({
id,
identityId,
createdAt,
@@ -163,6 +165,10 @@ export const identityProjectDALFactory = (db: TDbClient) => {
id: identityId,
name: identityName,
authMethod: identityAuthMethod
},
project: {
id: projectId,
name: projectName
}
}),
key: "id",

View File

@@ -16,9 +16,9 @@ export {
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityOidcAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth,
useDeleteIdentityOidcAuth,
useRevokeIdentityTokenAuthToken,
useRevokeIdentityUniversalAuthClientSecret,
useUpdateIdentity,
@@ -26,21 +26,19 @@ export {
useUpdateIdentityAzureAuth,
useUpdateIdentityGcpAuth,
useUpdateIdentityKubernetesAuth,
useUpdateIdentityOidcAuth,
useUpdateIdentityTokenAuth,
useUpdateIdentityTokenAuthToken,
useUpdateIdentityUniversalAuth,
useUpdateIdentityOidcAuth
} from "./mutations";
useUpdateIdentityUniversalAuth} from "./mutations";
export {
useGetIdentityAwsAuth,
useGetIdentityAzureAuth,
useGetIdentityById,
useGetIdentityGcpAuth,
useGetIdentityKubernetesAuth,
useGetIdentityOidcAuth,
useGetIdentityProjectMemberships,
useGetIdentityTokenAuth,
useGetIdentityTokensTokenAuth,
useGetIdentityUniversalAuth,
useGetIdentityUniversalAuthClientSecrets,
useGetIdentityOidcAuth
} from "./queries";
useGetIdentityUniversalAuthClientSecrets} from "./queries";

View File

@@ -23,10 +23,10 @@ import {
DeleteIdentityDTO,
DeleteIdentityGcpAuthDTO,
DeleteIdentityKubernetesAuthDTO,
DeleteIdentityOidcAuthDTO,
DeleteIdentityTokenAuthDTO,
DeleteIdentityUniversalAuthClientSecretDTO,
DeleteIdentityUniversalAuthDTO,
DeleteIdentityOidcAuthDTO,
Identity,
IdentityAccessToken,
IdentityAwsAuth,
@@ -44,8 +44,8 @@ import {
UpdateIdentityGcpAuthDTO,
UpdateIdentityKubernetesAuthDTO,
UpdateIdentityOidcAuthDTO,
UpdateIdentityUniversalAuthDTO,
UpdateIdentityTokenAuthDTO,
UpdateIdentityUniversalAuthDTO,
UpdateTokenIdentityTokenAuthDTO
} from "./types";

View File

@@ -9,11 +9,11 @@ import {
IdentityAzureAuth,
IdentityGcpAuth,
IdentityKubernetesAuth,
IdentityMembership,
IdentityMembershipOrg,
IdentityOidcAuth,
IdentityTokenAuth,
IdentityUniversalAuth,
IdentityOidcAuth
} from "./types";
IdentityUniversalAuth} from "./types";
export const identitiesKeys = {
getIdentityById: (identityId: string) => [{ identityId }, "identity"] as const,
@@ -56,7 +56,9 @@ export const useGetIdentityProjectMemberships = (identityId: string) => {
queryFn: async () => {
const {
data: { identityMemberships }
} = await apiRequest.get(`/api/v1/identities/${identityId}/identity-memberships`);
} = await apiRequest.get<{ identityMemberships: IdentityMembership[] }>(
`/api/v1/identities/${identityId}/identity-memberships`
);
return identityMemberships;
}
});

View File

@@ -1,4 +1,5 @@
import { TOrgRole } from "../roles/types";
import { Workspace } from "../workspace/types";
import { IdentityAuthMethod } from "./enums";
export type IdentityTrustedIp = {
@@ -45,6 +46,7 @@ export type IdentityMembershipOrg = {
export type IdentityMembership = {
id: string;
identity: Identity;
project: Pick<Workspace, "id" | "name">;
roles: Array<
{
id: string;

View File

@@ -6,6 +6,7 @@ import { CaStatus } from "../ca/enums";
import { TCertificateAuthority } from "../ca/types";
import { TCertificate } from "../certificates/types";
import { TGroupMembership } from "../groups/types";
import { identitiesKeys } from "../identities/queries";
import { IdentityMembership } from "../identities/types";
import { IntegrationAuth } from "../integrationAuth/types";
import { TIntegration } from "../integrations/types";
@@ -152,7 +153,7 @@ export const useGetWorkspaceById = (workspaceId: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceById(workspaceId),
queryFn: () => fetchWorkspaceById(workspaceId),
enabled: true
enabled: Boolean(workspaceId)
});
};
@@ -441,8 +442,9 @@ export const useAddIdentityToWorkspace = () => {
return identityMembership;
},
onSuccess: (_, { workspaceId }) => {
onSuccess: (_, { identityId, workspaceId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(workspaceId));
queryClient.invalidateQueries(identitiesKeys.getIdentityProjectMemberships(identityId));
}
});
};
@@ -462,8 +464,9 @@ export const useUpdateIdentityWorkspaceRole = () => {
return identityMembership;
},
onSuccess: (_, { workspaceId }) => {
onSuccess: (_, { identityId, workspaceId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(workspaceId));
queryClient.invalidateQueries(identitiesKeys.getIdentityProjectMemberships(identityId));
}
});
};
@@ -485,8 +488,9 @@ export const useDeleteIdentityFromWorkspace = () => {
);
return identityMembership;
},
onSuccess: (_, { workspaceId }) => {
onSuccess: (_, { identityId, workspaceId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceIdentityMemberships(workspaceId));
queryClient.invalidateQueries(identitiesKeys.getIdentityProjectMemberships(identityId));
}
});
};

View File

@@ -1,4 +1,4 @@
import { faCheck, faCopy,faKey, faTrash } from "@fortawesome/free-solid-svg-icons";
import { faCheck, faCopy, faKey, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
@@ -37,21 +37,23 @@ export const IdentityClientSecrets = ({ identityId, handlePopUpOpen }: Props) =>
<div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Client ID</p>
<div className="flex align-top">
<div className="group flex align-top">
<p className="text-sm text-mineshaft-300">{identityUniversalAuth?.clientId ?? ""}</p>
<Tooltip content={copyTextClientId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(identityUniversalAuth?.clientId ?? "");
setCopyTextClientId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingClientId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<Tooltip content={copyTextClientId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(identityUniversalAuth?.clientId ?? "");
setCopyTextClientId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingClientId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
</div>
</div>
</div>
{clientSecrets?.length ? (

View File

@@ -1,4 +1,4 @@
import { faCheck,faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
import { faCheck, faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
@@ -54,21 +54,23 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen }: Props) =
<div className="pt-4">
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Identity ID</p>
<div className="flex align-top">
<div className="group flex align-top">
<p className="text-sm text-mineshaft-300">{data.identity.id}</p>
<Tooltip content={copyTextId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(data.identity.id);
setCopyTextId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<Tooltip content={copyTextId}>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative ml-2"
onClick={() => {
navigator.clipboard.writeText(data.identity.id);
setCopyTextId("Copied");
}}
>
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
</IconButton>
</Tooltip>
</div>
</div>
</div>
<div className="mb-4">

View File

@@ -1,57 +0,0 @@
import { faKey } from "@fortawesome/free-solid-svg-icons";
import {
EmptyState,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr
} from "@app/components/v2";
import { useGetIdentityProjectMemberships } from "@app/hooks/api";
type Props = {
identityId: string;
};
export const IdentityProjectsSection = ({ identityId }: Props) => {
const { data: projectMemberships, isLoading } = useGetIdentityProjectMemberships(identityId);
return (
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Projects</h3>
</div>
<div className="py-4">
<TableContainer>
<Table>
<THead>
<Tr>
<Th>Name</Th>
<Th>Role</Th>
</Tr>
</THead>
<TBody>
{isLoading && <TableSkeleton columns={2} innerKey="identity-project-memberships" />}
{!isLoading &&
projectMemberships?.map((membership: any) => {
// TODO: fix any
return (
<Tr className="h-10" key={`identity-project-membership-${membership.id}`}>
<Td>{membership.project.name}</Td>
<Td>{membership.roles[0].role}</Td>
</Tr>
);
})}
</TBody>
</Table>
{!isLoading && !projectMemberships?.length && (
<EmptyState title="This identity has not been assigned to any projects" icon={faKey} />
)}
</TableContainer>
</div>
</div>
);
};

View File

@@ -0,0 +1,175 @@
import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Modal, ModalContent,Select, SelectItem } from "@app/components/v2";
import { useWorkspace } from "@app/context";
import {
useAddIdentityToWorkspace,
useGetIdentityProjectMemberships,
useGetProjectRoles,
useGetWorkspaceById
} from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = z
.object({
projectId: z.string(),
role: z.string()
})
.required();
type FormData = z.infer<typeof schema>;
type Props = {
identityId: string;
popUp: UsePopUpState<["addIdentityToProject"]>;
handlePopUpToggle: (
popUpName: keyof UsePopUpState<["addIdentityToProject"]>,
state?: boolean
) => void;
};
export const IdentityAddToProjectModal = ({ identityId, popUp, handlePopUpToggle }: Props) => {
const { workspaces } = useWorkspace();
const { mutateAsync: addIdentityToWorkspace } = useAddIdentityToWorkspace();
const {
control,
handleSubmit,
reset,
formState: { isSubmitting },
watch
} = useForm<FormData>({
resolver: zodResolver(schema)
});
const projectId = watch("projectId");
const { data: projectMemberships } = useGetIdentityProjectMemberships(identityId);
const { data: project } = useGetWorkspaceById(projectId);
const { data: roles } = useGetProjectRoles(project?.slug ?? "");
const filteredWorkspaces = useMemo(() => {
const wsWorkspaceIds = new Map();
projectMemberships?.forEach((projectMembership: any) => {
wsWorkspaceIds.set(projectMembership.project.id, true);
});
return (workspaces || []).filter(({ id }) => !wsWorkspaceIds.has(id));
}, [workspaces, projectMemberships]);
const onFormSubmit = async ({ projectId: workspaceId, role }: FormData) => {
try {
await addIdentityToWorkspace({
workspaceId,
identityId,
role: role || undefined
});
createNotification({
text: "Successfully added identity to project",
type: "success"
});
reset();
handlePopUpToggle("addIdentityToProject", false);
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to add identity to project";
createNotification({
text,
type: "error"
});
}
};
return (
<Modal
isOpen={popUp?.addIdentityToProject?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("addIdentityToProject", isOpen);
reset();
}}
>
<ModalContent title="Add Identity to Project">
<form onSubmit={handleSubmit(onFormSubmit)}>
<Controller
control={control}
name="projectId"
defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Project"
errorText={error?.message}
isError={Boolean(error)}
className="mt-4"
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{(filteredWorkspaces || []).map(({ id, name }) => (
<SelectItem value={id} key={`project-${id}`}>
{name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
name="role"
defaultValue=""
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Role"
errorText={error?.message}
isError={Boolean(error)}
className="mt-4"
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
>
{(roles || []).map(({ name, slug }) => (
<SelectItem value={slug} key={`project-role-${slug}`}>
{name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<div className="flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
isLoading={isSubmitting}
isDisabled={isSubmitting}
>
Add
</Button>
<Button
colorSchema="secondary"
variant="plain"
onClick={() => handlePopUpToggle("addIdentityToProject", false)}
>
Cancel
</Button>
</div>
</form>
</ModalContent>
</Modal>
);
};

View File

@@ -0,0 +1,68 @@
import { useRouter } from "next/router";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
import { IdentityMembership } from "@app/hooks/api/identities/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
membership: IdentityMembership;
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>,
data?: {}
) => void;
};
const formatRoleName = (role: string, customRoleName?: string) => {
if (role === ProjectMembershipRole.Custom) return customRoleName;
if (role === ProjectMembershipRole.Admin) return "Admin";
if (role === ProjectMembershipRole.Member) return "Developer";
if (role === ProjectMembershipRole.Viewer) return "Viewer";
if (role === ProjectMembershipRole.NoAccess) return "No access";
return role;
};
export const IdentityProjectRow = ({
membership: { id, createdAt, identity, project, roles },
handlePopUpOpen
}: Props) => {
const router = useRouter();
return (
<Tr
className="group h-10 cursor-pointer transition-colors duration-300 hover:bg-mineshaft-700"
key={`identity-project-membership-${id}`}
onClick={() => router.push(`/project/${project.id}/members`)}
>
<Td>{project.name}</Td>
<Td>{`${formatRoleName(roles[0].role, roles[0].customRoleName)}${
roles.length > 1 ? ` (+${roles.length - 1})` : ""
}`}</Td>
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
<Td>
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<Tooltip content="Remove">
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative"
onClick={(e) => {
e.stopPropagation();
handlePopUpOpen("removeIdentityFromProject", {
identityId: identity.id,
identityName: identity.name,
projectId: project.id,
projectName: project.name
});
}}
>
<FontAwesomeIcon icon={faTrash} />
</IconButton>
</Tooltip>
</div>
</Td>
</Tr>
);
};

View File

@@ -0,0 +1,92 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
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 { usePopUp } from "@app/hooks/usePopUp";
import { IdentityAddToProjectModal } from "./IdentityAddToProjectModal";
import { IdentityProjectsTable } from "./IdentityProjectsTable";
type Props = {
identityId: string;
};
export const IdentityProjectsSection = ({ identityId }: Props) => {
const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"addIdentityToProject",
"removeIdentityFromProject"
] as const);
const onRemoveIdentitySubmit = async (id: string, projectId: string) => {
try {
await deleteMutateAsync({
identityId: id,
workspaceId: projectId
});
createNotification({
text: "Successfully removed identity from project",
type: "success"
});
handlePopUpClose("removeIdentityFromProject");
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to remove identity from project";
createNotification({
text,
type: "error"
});
}
};
return (
<div className="w-full 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">
<h3 className="text-lg font-semibold text-mineshaft-100">Projects</h3>
<IconButton
ariaLabel="copy icon"
variant="plain"
className="group relative"
onClick={() => {
handlePopUpOpen("addIdentityToProject");
}}
>
<FontAwesomeIcon icon={faPlus} />
</IconButton>
</div>
<div className="py-4">
<IdentityProjectsTable identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
</div>
<DeleteActionModal
isOpen={popUp.removeIdentityFromProject.isOpen}
title={`Are you sure want to remove ${
(popUp?.removeIdentityFromProject?.data as { identityName: string })?.identityName || ""
} from ${
(popUp?.removeIdentityFromProject?.data as { projectName: string })?.projectName || ""
}?`}
onChange={(isOpen) => handlePopUpToggle("removeIdentityFromProject", isOpen)}
deleteKey="confirm"
onDeleteApproved={() => {
const popupData = popUp?.removeIdentityFromProject?.data as {
identityId: string;
projectId: string;
};
return onRemoveIdentitySubmit(popupData.identityId, popupData.projectId);
}}
/>
<IdentityAddToProjectModal
identityId={identityId}
popUp={popUp}
handlePopUpToggle={handlePopUpToggle}
/>
</div>
);
};

View File

@@ -0,0 +1,58 @@
import { faKey } from "@fortawesome/free-solid-svg-icons";
import {
EmptyState,
Table,
TableContainer,
TableSkeleton,
TBody,
Th,
THead,
Tr
} from "@app/components/v2";
import { useGetIdentityProjectMemberships } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { IdentityProjectRow } from "./IdentityProjectRow";
type Props = {
identityId: string;
handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>,
data?: {}
) => void;
};
export const IdentityProjectsTable = ({ identityId, handlePopUpOpen }: Props) => {
const { data: projectMemberships, isLoading } = useGetIdentityProjectMemberships(identityId);
return (
<TableContainer>
<Table>
<THead>
<Tr>
<Th>Name</Th>
<Th>Role</Th>
<Th>Added On</Th>
<Th className="w-5" />
</Tr>
</THead>
<TBody>
{isLoading && <TableSkeleton columns={2} innerKey="identity-project-memberships" />}
{!isLoading &&
projectMemberships?.map((membership) => {
return (
<IdentityProjectRow
key={`identity-project-membership-${membership.id}`}
membership={membership}
handlePopUpOpen={handlePopUpOpen}
/>
);
})}
</TBody>
</Table>
{!isLoading && !projectMemberships?.length && (
<EmptyState title="This identity has not been assigned to any projects" icon={faKey} />
)}
</TableContainer>
);
};

View File

@@ -0,0 +1 @@
export { IdentityProjectsSection } from "./IdentityProjectsSection";

View File

@@ -92,7 +92,6 @@ export const IdentityTokenModal = ({ popUp, handlePopUpToggle }: Props) => {
});
setToken(newTokenData.accessToken);
// note: may be helpful to tell user ttl etc.
}
createNotification({

View File

@@ -1,6 +1,6 @@
export { IdentityAuthenticationSection } from "./IdentityAuthenticationSection/IdentityAuthenticationSection";
export { IdentityClientSecretModal } from "./IdentityClientSecretModal";
export { IdentityDetailsSection } from "./IdentityDetailsSection";
export { IdentityProjectsSection } from "./IdentityProjectsSection";
export { IdentityProjectsSection } from "./IdentityProjectsSection/IdentityProjectsSection";
export { IdentityTokenListModal } from "./IdentityTokenListModal";
export { IdentityTokenModal } from "./IdentityTokenModal";

View File

@@ -19,10 +19,9 @@ import {
useDeleteIdentityAzureAuth,
useDeleteIdentityGcpAuth,
useDeleteIdentityKubernetesAuth,
useDeleteIdentityOidcAuth,
useDeleteIdentityTokenAuth,
useDeleteIdentityUniversalAuth,
useDeleteIdentityOidcAuth
} from "@app/hooks/api";
useDeleteIdentityUniversalAuth} from "@app/hooks/api";
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
import { UsePopUpState } from "@app/hooks/usePopUp";