feat: updated frontend with the new endpoints for project identity

memberships
This commit is contained in:
=
2025-11-13 12:52:41 +05:30
parent 3d69d48835
commit 5eb67aefae
13 changed files with 91 additions and 33 deletions
@@ -7,13 +7,13 @@ import {
ProjectMembershipRole, ProjectMembershipRole,
TemporaryPermissionMode TemporaryPermissionMode
} from "@app/db/schemas"; } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, PROJECT_IDENTITIES, PROJECT_IDENTITY_MEMBERSHIP } from "@app/lib/api-docs"; import { ApiDocsTags, PROJECT_IDENTITIES, PROJECT_IDENTITY_MEMBERSHIP } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { ms } from "@app/lib/ms"; import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
export const registerIdentityProjectMembershipRouter = async (server: FastifyZodProvider) => { export const registerIdentityProjectMembershipRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
@@ -289,7 +289,7 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
limit: z.coerce limit: z.coerce
.number() .number()
.min(1) .min(1)
.max(100) .max(1000)
.default(20) .default(20)
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_IDENTITY_MEMBERSHIPS.limit) .describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_IDENTITY_MEMBERSHIPS.limit)
.optional(), .optional(),
@@ -456,7 +456,7 @@ export const registerIdentityProjectMembershipRouter = async (server: FastifyZod
limit: z.coerce limit: z.coerce
.number() .number()
.min(1) .min(1)
.max(100) .max(1000)
.default(20) .default(20)
.describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit) .describe(PROJECT_IDENTITY_MEMBERSHIP.LIST_AVAILABLE_IDENTITIES.limit)
.optional(), .optional(),
+2 -2
View File
@@ -14,6 +14,7 @@ import { CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP } from "./certificate-authori
import { registerCertificateProfilesRouter } from "./certificate-profiles-router"; import { registerCertificateProfilesRouter } from "./certificate-profiles-router";
import { registerCertRouter } from "./certificate-router"; import { registerCertRouter } from "./certificate-router";
import { registerCertificateTemplateRouter } from "./certificate-template-router"; import { registerCertificateTemplateRouter } from "./certificate-template-router";
import { registerDeprecatedIdentityProjectMembershipRouter } from "./deprecated-identity-project-membership-router";
import { registerDeprecatedProjectEnvRouter } from "./deprecated-project-env-router"; import { registerDeprecatedProjectEnvRouter } from "./deprecated-project-env-router";
import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router"; import { registerDeprecatedProjectMembershipRouter } from "./deprecated-project-membership-router";
import { registerDeprecatedProjectRouter } from "./deprecated-project-router"; import { registerDeprecatedProjectRouter } from "./deprecated-project-router";
@@ -34,7 +35,7 @@ import { registerIdentityLdapAuthRouter } from "./identity-ldap-auth-router";
import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router"; import { registerIdentityOciAuthRouter } from "./identity-oci-auth-router";
import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router"; import { registerIdentityOidcAuthRouter } from "./identity-oidc-auth-router";
import { registerIdentityOrgMembershipRouter } from "./identity-org-membership-router"; import { registerIdentityOrgMembershipRouter } from "./identity-org-membership-router";
import { registerDeprecatedIdentityProjectMembershipRouter } from "./deprecated-identity-project-membership-router"; import { registerIdentityProjectMembershipRouter } from "./identity-project-membership-router";
import { registerIdentityRouter } from "./identity-router"; import { registerIdentityRouter } from "./identity-router";
import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router"; import { registerIdentityTlsCertAuthRouter } from "./identity-tls-cert-auth-router";
import { registerIdentityTokenAuthRouter } from "./identity-token-auth-router"; import { registerIdentityTokenAuthRouter } from "./identity-token-auth-router";
@@ -69,7 +70,6 @@ import { registerUserEngagementRouter } from "./user-engagement-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
import { registerWebhookRouter } from "./webhook-router"; import { registerWebhookRouter } from "./webhook-router";
import { registerWorkflowIntegrationRouter } from "./workflow-integration-router"; import { registerWorkflowIntegrationRouter } from "./workflow-integration-router";
import { registerIdentityProjectMembershipRouter } from "./identity-project-membership-router";
export const registerV1Routes = async (server: FastifyZodProvider) => { export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerSsoRouter, { prefix: "/sso" }); await server.register(registerSsoRouter, { prefix: "/sso" });
@@ -16,7 +16,7 @@ import {
IdentityMembershipOrg, IdentityMembershipOrg,
IdentityOciAuth, IdentityOciAuth,
IdentityOidcAuth, IdentityOidcAuth,
IdentityProjectMembership, IdentityProjectMembershipV1,
IdentityTlsCertAuth, IdentityTlsCertAuth,
IdentityTokenAuth, IdentityTokenAuth,
IdentityUniversalAuth, IdentityUniversalAuth,
@@ -95,7 +95,7 @@ export const useGetIdentityProjectMemberships = (identityId: string) => {
queryFn: async () => { queryFn: async () => {
const { const {
data: { identityMemberships } data: { identityMemberships }
} = await apiRequest.get<{ identityMemberships: IdentityProjectMembership[] }>( } = await apiRequest.get<{ identityMemberships: IdentityProjectMembershipV1[] }>(
`/api/v1/identities/${identityId}/identity-memberships` `/api/v1/identities/${identityId}/identity-memberships`
); );
return identityMemberships; return identityMemberships;
+40 -2
View File
@@ -56,7 +56,7 @@ export type IdentityMembershipOrg = {
updatedAt: string; updatedAt: string;
}; };
export type IdentityProjectMembership = { export type IdentityProjectMembershipV1 = {
id: string; id: string;
identity: Identity; identity: Identity;
project: Pick<Project, "id" | "name" | "type">; project: Pick<Project, "id" | "name" | "type">;
@@ -90,6 +90,39 @@ export type IdentityProjectMembership = {
lastLoginAuthMethod?: IdentityAuthMethod; 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 = { export type CreateIdentityDTO = {
name: string; name: string;
organizationId: string; organizationId: string;
@@ -828,7 +861,12 @@ export type RevokeTokenRes = {
}; };
export type TProjectIdentityMembershipsList = { export type TProjectIdentityMembershipsList = {
identityMemberships: IdentityProjectMembership[]; identityMemberships: IdentityProjectMembershipV1[];
totalCount: number;
};
export type TProjectIdentityMembershipsListV2 = {
identityMemberships: IdentityProjectMembershipV2[];
totalCount: number; totalCount: number;
}; };
@@ -18,7 +18,7 @@ export const useCreateProjectIdentityMembership = () => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.post<{ identityMembership: TProjectIdentityMembership }>( } = await apiRequest.post<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`, `/api/v1/projects/${projectId}/memberships/identities/${identityId}`,
{ {
role role
} }
@@ -49,7 +49,7 @@ export const useUpdateProjectIdentityMembership = () => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.patch<{ identityMembership: TProjectIdentityMembership }>( } = await apiRequest.patch<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}`, `/api/v1/projects/${projectId}/memberships/identities/${identityId}`,
updates updates
); );
return identityMembership; return identityMembership;
@@ -76,7 +76,7 @@ export const useDeleteProjectIdentityMembership = () => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.delete<{ identityMembership: TProjectIdentityMembership }>( } = await apiRequest.delete<{ identityMembership: TProjectIdentityMembership }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}` `/api/v1/projects/${projectId}/memberships/identities/${identityId}`
); );
return identityMembership; return identityMembership;
}, },
@@ -8,8 +8,8 @@ import {
} from "@app/hooks/api"; } from "@app/hooks/api";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
import { import {
IdentityProjectMembership, IdentityProjectMembershipV1,
TProjectIdentityMembershipsList TProjectIdentityMembershipsListV2
} from "@app/hooks/api/identities/types"; } from "@app/hooks/api/identities/types";
import { ProjectIdentityOrderBy, TListProjectIdentitiesDTO } from "@app/hooks/api/projects/types"; import { ProjectIdentityOrderBy, TListProjectIdentitiesDTO } from "@app/hooks/api/projects/types";
@@ -23,7 +23,7 @@ export const projectIdentityMembershipQuery = {
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<{
identities: TAvailableProjectIdentities; identities: TAvailableProjectIdentities;
}>(`/api/v1/projects/${params.projectId}/available-identities`, { }>(`/api/v1/projects/${params.projectId}/memberships/available-identities`, {
params: { params: {
offset: params.offset, offset: params.offset,
limit: params.limit, limit: params.limit,
@@ -48,9 +48,9 @@ export const useListProjectIdentityMemberships = (
}: TListProjectIdentitiesDTO, }: TListProjectIdentitiesDTO,
options?: Omit< options?: Omit<
UseQueryOptions< UseQueryOptions<
TProjectIdentityMembershipsList, TProjectIdentityMembershipsListV2,
unknown, unknown,
TProjectIdentityMembershipsList, TProjectIdentityMembershipsListV2,
ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams> ReturnType<typeof projectKeys.getProjectIdentityMembershipsWithParams>
>, >,
"queryKey" | "queryFn" "queryKey" | "queryFn"
@@ -74,8 +74,8 @@ export const useListProjectIdentityMemberships = (
search: String(search) search: String(search)
}); });
const { data } = await apiRequest.get<TProjectIdentityMembershipsList>( const { data } = await apiRequest.get<TProjectIdentityMembershipsListV2>(
`/api/v1/projects/${projectId}/identity-memberships`, `/api/v1/projects/${projectId}/memberships/identities`,
{ params } { params }
); );
return data; return data;
@@ -92,7 +92,7 @@ export const useGetProjectIdentityMembership = (projectId: string, identityId: s
queryFn: async () => { queryFn: async () => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.get<{ identityMembership: IdentityProjectMembership }>( } = await apiRequest.get<{ identityMembership: IdentityProjectMembershipV1 }>(
`/api/v1/projects/${projectId}/identity-memberships/${identityId}` `/api/v1/projects/${projectId}/identity-memberships/${identityId}`
); );
return identityMembership; return identityMembership;
@@ -107,8 +107,8 @@ export const useGetProjectIdentityMembershipV2 = (projectId: string, identityId:
queryFn: async () => { queryFn: async () => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.get<{ identityMembership: IdentityProjectMembership }>( } = await apiRequest.get<{ identityMembership: IdentityProjectMembershipV1 }>(
`/api/v2/projects/${projectId}/identity-memberships/${identityId}` `/api/v1/projects/${projectId}/memberships/identities/${identityId}`
); );
return identityMembership; return identityMembership;
} }
@@ -9,7 +9,7 @@ import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
import { getProjectBaseURL } from "@app/helpers/project"; import { getProjectBaseURL } from "@app/helpers/project";
import { formatProjectRoleName } from "@app/helpers/roles"; import { formatProjectRoleName } from "@app/helpers/roles";
import { useGetUserProjects } from "@app/hooks/api"; import { useGetUserProjects } from "@app/hooks/api";
import { IdentityProjectMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
export enum TabSections { export enum TabSections {
@@ -20,7 +20,7 @@ export enum TabSections {
} }
type Props = { type Props = {
membership: IdentityProjectMembership; membership: IdentityProjectMembershipV1;
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>, popUpName: keyof UsePopUpState<["removeIdentityFromProject"]>,
data?: object data?: object
@@ -47,7 +47,7 @@ export const ProjectLinkIdentityModal = ({ handlePopUpToggle }: Props) => {
const { data: identityMembershipsData } = useListProjectIdentityMemberships({ const { data: identityMembershipsData } = useListProjectIdentityMemberships({
projectId, projectId,
limit: 20000 // TODO: this is temp to preserve functionality for larger projects, will optimize in PR referenced above limit: 1000 // TODO: this is temp to preserve functionality for larger projects, will optimize in PR referenced above
}); });
const identityMemberships = identityMembershipsData?.identityMemberships; const identityMemberships = identityMembershipsData?.identityMemberships;
@@ -10,6 +10,8 @@ import { formatRelative } from "date-fns";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Alert,
AlertDescription,
Button, Button,
ConfirmActionModal, ConfirmActionModal,
DeleteActionModal, DeleteActionModal,
@@ -147,6 +149,7 @@ const Page = () => {
scope={currentProject.type} scope={currentProject.type}
title={identityMembershipDetails?.identity?.name} title={identityMembershipDetails?.identity?.name}
description={`Identity ${isProjectIdentity ? "created" : "added"} on ${identityMembershipDetails?.createdAt && formatRelative(new Date(identityMembershipDetails?.createdAt || ""), new Date())}`} description={`Identity ${isProjectIdentity ? "created" : "added"} on ${identityMembershipDetails?.createdAt && formatRelative(new Date(identityMembershipDetails?.createdAt || ""), new Date())}`}
className={!isProjectIdentity ? "mb-4" : undefined}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button <Button
@@ -207,6 +210,23 @@ const Page = () => {
)} )}
</div> </div>
</PageHeader> </PageHeader>
{!isProjectIdentity && (
<Alert hideTitle className="mb-4 border-primary/50 bg-primary/10">
<AlertDescription>
This identity is managed by your organization.{" "}
<Link
to="/organization/identities/$identityId"
params={{
identityId
}}
>
<span className="cursor-pointer text-primary underline underline-offset-2">
Click here to retrieve credentials.
</span>
</Link>
</AlertDescription>
</Alert>
)}
<div className="flex gap-x-4"> <div className="flex gap-x-4">
{identity ? ( {identity ? (
<div className="flex w-72 flex-col gap-y-4"> <div className="flex w-72 flex-col gap-y-4">
@@ -31,13 +31,13 @@ import {
} from "@app/context"; } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useDeleteIdentityProjectAdditionalPrivilege } from "@app/hooks/api"; import { useDeleteIdentityProjectAdditionalPrivilege } from "@app/hooks/api";
import { IdentityProjectMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
import { useListIdentityProjectPrivileges } from "@app/hooks/api/identityProjectAdditionalPrivilege/queries"; import { useListIdentityProjectPrivileges } from "@app/hooks/api/identityProjectAdditionalPrivilege/queries";
import { IdentityProjectAdditionalPrivilegeModifySection } from "./IdentityProjectAdditionalPrivilegeModifySection"; import { IdentityProjectAdditionalPrivilegeModifySection } from "./IdentityProjectAdditionalPrivilegeModifySection";
type Props = { type Props = {
identityMembershipDetails: IdentityProjectMembership; identityMembershipDetails: IdentityProjectMembershipV1;
}; };
export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDetails }: Props) => { export const IdentityProjectAdditionalPrivilegeSection = ({ identityMembershipDetails }: Props) => {
@@ -27,13 +27,13 @@ import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app
import { formatProjectRoleName } from "@app/helpers/roles"; import { formatProjectRoleName } from "@app/helpers/roles";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { useUpdateProjectIdentityMembership } from "@app/hooks/api"; import { useUpdateProjectIdentityMembership } from "@app/hooks/api";
import { IdentityProjectMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
import { TProjectRole } from "@app/hooks/api/roles/types"; import { TProjectRole } from "@app/hooks/api/roles/types";
import { IdentityRoleModify } from "./IdentityRoleModify"; import { IdentityRoleModify } from "./IdentityRoleModify";
type Props = { type Props = {
identityMembershipDetails: IdentityProjectMembership; identityMembershipDetails: IdentityProjectMembershipV1;
isMembershipDetailsLoading?: boolean; isMembershipDetailsLoading?: boolean;
}; };
@@ -33,7 +33,7 @@ import {
useProjectPermission useProjectPermission
} from "@app/context"; } from "@app/context";
import { useGetProjectRoles, useUpdateProjectIdentityMembership } from "@app/hooks/api"; import { useGetProjectRoles, useUpdateProjectIdentityMembership } from "@app/hooks/api";
import { IdentityProjectMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
import { TemporaryPermissionMode } from "@app/hooks/api/shared"; import { TemporaryPermissionMode } from "@app/hooks/api/shared";
@@ -58,7 +58,7 @@ const roleFormSchema = z.object({
type TRoleForm = z.infer<typeof roleFormSchema>; type TRoleForm = z.infer<typeof roleFormSchema>;
type Props = { type Props = {
identityProjectMembership: IdentityProjectMembership; identityProjectMembership: IdentityProjectMembershipV1;
}; };
export const IdentityRoleModify = ({ identityProjectMembership }: Props) => { export const IdentityRoleModify = ({ identityProjectMembership }: Props) => {
@@ -31,13 +31,13 @@ import { ProjectPermissionIdentityActions, ProjectPermissionSub, useProject } fr
import { getProjectBaseURL } from "@app/helpers/project"; import { getProjectBaseURL } from "@app/helpers/project";
import { usePopUp, useTimedReset } from "@app/hooks"; import { usePopUp, useTimedReset } from "@app/hooks";
import { identityAuthToNameMap, TProjectIdentity, useDeleteProjectIdentity } from "@app/hooks/api"; import { identityAuthToNameMap, TProjectIdentity, useDeleteProjectIdentity } from "@app/hooks/api";
import { IdentityProjectMembership } from "@app/hooks/api/identities/types"; import { IdentityProjectMembershipV1 } from "@app/hooks/api/identities/types";
import { ProjectIdentityModal } from "@app/pages/project/AccessControlPage/components/IdentityTab/components/ProjectIdentityModal"; import { ProjectIdentityModal } from "@app/pages/project/AccessControlPage/components/IdentityTab/components/ProjectIdentityModal";
type Props = { type Props = {
identity: TProjectIdentity; identity: TProjectIdentity;
isOrgIdentity?: boolean; isOrgIdentity?: boolean;
membership: IdentityProjectMembership; membership: IdentityProjectMembershipV1;
}; };
export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, membership }: Props) => { export const ProjectIdentityDetailsSection = ({ identity, isOrgIdentity, membership }: Props) => {