Merge pull request #3389 from Infisical/daniel/get-project-identity-membership-by-id

feat(project-identity): get project identity by membership ID
This commit is contained in:
Daniel Hougaard
2025-04-11 00:55:03 +04:00
committed by GitHub
4 changed files with 109 additions and 2 deletions

View File

@@ -351,4 +351,56 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
return { identityMembership }; return { identityMembership };
} }
}); });
server.route({
method: "GET",
url: "/identity-memberships/:identityMembershipId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
params: z.object({
identityMembershipId: z.string().trim()
}),
response: {
200: z.object({
identityMembership: z.object({
id: z.string(),
identityId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.string())
}),
project: SanitizedProjectSchema.pick({ name: true, id: true })
})
})
}
},
handler: async (req) => {
const identityMembership = await server.services.identityProject.getProjectIdentityByMembershipId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityMembershipId: req.params.identityMembershipId
});
return { identityMembership };
}
});
}; };

View File

@@ -21,6 +21,7 @@ import {
TCreateProjectIdentityDTO, TCreateProjectIdentityDTO,
TDeleteProjectIdentityDTO, TDeleteProjectIdentityDTO,
TGetProjectIdentityByIdentityIdDTO, TGetProjectIdentityByIdentityIdDTO,
TGetProjectIdentityByMembershipIdDTO,
TListProjectIdentityDTO, TListProjectIdentityDTO,
TUpdateProjectIdentityDTO TUpdateProjectIdentityDTO
} from "./identity-project-types"; } from "./identity-project-types";
@@ -370,11 +371,48 @@ export const identityProjectServiceFactory = ({
return identityMembership; return identityMembership;
}; };
const getProjectIdentityByMembershipId = async ({
identityMembershipId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: TGetProjectIdentityByMembershipIdDTO) => {
const membership = await identityProjectDAL.findOne({ id: identityMembershipId });
if (!membership) {
throw new NotFoundError({
message: `Project membership with ID '${identityMembershipId}' not found`
});
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId: membership.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Read,
subject(ProjectPermissionSub.Identity, { identityId: membership.identityId })
);
const [identityMembership] = await identityProjectDAL.findByProjectId(membership.projectId, {
identityId: membership.identityId
});
return identityMembership;
};
return { return {
createProjectIdentity, createProjectIdentity,
updateProjectIdentity, updateProjectIdentity,
deleteProjectIdentity, deleteProjectIdentity,
listProjectIdentities, listProjectIdentities,
getProjectIdentityByIdentityId getProjectIdentityByIdentityId,
getProjectIdentityByMembershipId
}; };
}; };

View File

@@ -52,6 +52,10 @@ export type TGetProjectIdentityByIdentityIdDTO = {
identityId: string; identityId: string;
} & TProjectPermission; } & TProjectPermission;
export type TGetProjectIdentityByMembershipIdDTO = {
identityMembershipId: string;
} & Omit<TProjectPermission, "projectId">;
export enum ProjectIdentityOrderBy { export enum ProjectIdentityOrderBy {
Name = "name" Name = "name"
} }

View File

@@ -86,7 +86,20 @@ const Page = () => {
title={identityMembershipDetails?.identity?.name} title={identityMembershipDetails?.identity?.name}
description={`Identity joined on ${identityMembershipDetails?.createdAt && formatRelative(new Date(identityMembershipDetails?.createdAt || ""), new Date())}`} description={`Identity joined on ${identityMembershipDetails?.createdAt && formatRelative(new Date(identityMembershipDetails?.createdAt || ""), new Date())}`}
> >
<div> <div className="flex items-center gap-2">
<Button
variant="outline_bg"
size="xs"
onClick={() => {
navigator.clipboard.writeText(identityMembershipDetails.id);
createNotification({
text: "Membership ID copied to clipboard",
type: "success"
});
}}
>
Copy Membership ID
</Button>
<ProjectPermissionCan <ProjectPermissionCan
I={ProjectPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={subject(ProjectPermissionSub.Identity, { a={subject(ProjectPermissionSub.Identity, {