mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: added endpoints to fetch a particule project user membership and identity
This commit is contained in:
@@ -197,6 +197,10 @@ export const PROJECT_USERS = {
|
||||
GET_USER_MEMBERSHIPS: {
|
||||
workspaceId: "The ID of the project to get memberships from."
|
||||
},
|
||||
GET_USER_MEMBERSHIP: {
|
||||
workspaceId: "The ID of the project to get memberships from.",
|
||||
username: "The username to get project membership of. Email is the default username."
|
||||
},
|
||||
UPDATE_USER_MEMBERSHIP: {
|
||||
workspaceId: "The ID of the project to update the membership for.",
|
||||
membershipId: "The ID of the membership to update.",
|
||||
@@ -208,6 +212,10 @@ export const PROJECT_IDENTITIES = {
|
||||
LIST_IDENTITY_MEMBERSHIPS: {
|
||||
projectId: "The ID of the project to get identity memberships from."
|
||||
},
|
||||
GET_IDENTITY_MEMBERSHIP_BY_ID: {
|
||||
identityId: "The ID of the identity to get the membership for.",
|
||||
projectId: "The ID of the project to get the identity membership for."
|
||||
},
|
||||
UPDATE_IDENTITY_MEMBERSHIP: {
|
||||
projectId: "The ID of the project to update the identity membership for.",
|
||||
identityId: "The ID of the identity to update the membership for.",
|
||||
|
||||
@@ -74,6 +74,66 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:workspaceId/memberships/details",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Return project user memberships",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
workspaceId: z.string().min(1).trim().describe(PROJECT_USERS.GET_USER_MEMBERSHIP.workspaceId)
|
||||
}),
|
||||
body: z.object({
|
||||
username: z.string().min(1).trim().describe(PROJECT_USERS.GET_USER_MEMBERSHIP.username)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
membership: ProjectMembershipsSchema.extend({
|
||||
user: UsersSchema.pick({
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
id: true
|
||||
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
|
||||
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()
|
||||
})
|
||||
)
|
||||
}).omit({ createdAt: true, updatedAt: true })
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const membership = await server.services.projectMembership.getProjectMembershipByUsername({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId,
|
||||
username: req.body.username
|
||||
});
|
||||
return { membership };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:workspaceId/memberships",
|
||||
|
||||
@@ -251,4 +251,61 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
||||
return { identityMemberships };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:projectId/identity-memberships/:identityId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
description: "Return project identity membership",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectId: z.string().trim().describe(PROJECT_IDENTITIES.GET_IDENTITY_MEMBERSHIP_BY_ID.projectId),
|
||||
identityId: z.string().trim().describe(PROJECT_IDENTITIES.GET_IDENTITY_MEMBERSHIP_BY_ID.identityId)
|
||||
}),
|
||||
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, authMethod: true })
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const identityMembership = await server.services.identityProject.getProjectIdentityByIdentityId({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.projectId,
|
||||
identityId: req.params.identityId
|
||||
});
|
||||
return { identityMembership };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -10,11 +10,16 @@ export type TIdentityProjectDALFactory = ReturnType<typeof identityProjectDALFac
|
||||
export const identityProjectDALFactory = (db: TDbClient) => {
|
||||
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
||||
|
||||
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
||||
const findByProjectId = async (projectId: string, filter: { identityId?: string } = {}, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||
.where((qb) => {
|
||||
if (filter.identityId) {
|
||||
void qb.where("identityId", filter.identityId);
|
||||
}
|
||||
})
|
||||
.join(
|
||||
TableName.IdentityProjectMembershipRole,
|
||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||
|
||||
@@ -18,6 +18,7 @@ import { TIdentityProjectMembershipRoleDALFactory } from "./identity-project-mem
|
||||
import {
|
||||
TCreateProjectIdentityDTO,
|
||||
TDeleteProjectIdentityDTO,
|
||||
TGetProjectIdentityByIdentityIdDTO,
|
||||
TListProjectIdentityDTO,
|
||||
TUpdateProjectIdentityDTO
|
||||
} from "./identity-project-types";
|
||||
@@ -282,10 +283,33 @@ export const identityProjectServiceFactory = ({
|
||||
return identityMemberships;
|
||||
};
|
||||
|
||||
const getProjectIdentityByIdentityId = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
identityId
|
||||
}: TGetProjectIdentityByIdentityIdDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||
|
||||
const [identityMembership] = await identityProjectDAL.findByProjectId(projectId, { identityId });
|
||||
if (!identityMembership) throw new BadRequestError({ message: `Membership not found for identity ${identityId}` });
|
||||
return identityMembership;
|
||||
};
|
||||
|
||||
return {
|
||||
createProjectIdentity,
|
||||
updateProjectIdentity,
|
||||
deleteProjectIdentity,
|
||||
listProjectIdentities
|
||||
listProjectIdentities,
|
||||
getProjectIdentityByIdentityId
|
||||
};
|
||||
};
|
||||
|
||||
@@ -41,3 +41,7 @@ export type TDeleteProjectIdentityDTO = {
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TListProjectIdentityDTO = TProjectPermission;
|
||||
|
||||
export type TGetProjectIdentityByIdentityIdDTO = {
|
||||
identityId: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
@@ -9,11 +9,19 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
|
||||
const projectMemberOrm = ormify(db, TableName.ProjectMembership);
|
||||
|
||||
// special query
|
||||
const findAllProjectMembers = async (projectId: string) => {
|
||||
const findAllProjectMembers = async (projectId: string, filter: { usernames?: string[]; username?: string } = {}) => {
|
||||
try {
|
||||
const docs = await db(TableName.ProjectMembership)
|
||||
.where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId })
|
||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
||||
.where((qb) => {
|
||||
if (filter.usernames) {
|
||||
void qb.whereIn("username", filter.usernames);
|
||||
}
|
||||
if (filter.username) {
|
||||
void qb.where("username", filter.username);
|
||||
}
|
||||
})
|
||||
.join<TUserEncryptionKeys>(
|
||||
TableName.UserEncryptionKey,
|
||||
`${TableName.UserEncryptionKey}.userId`,
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
TAddUsersToWorkspaceNonE2EEDTO,
|
||||
TDeleteProjectMembershipOldDTO,
|
||||
TDeleteProjectMembershipsDTO,
|
||||
TGetProjectMembershipByUsernameDTO,
|
||||
TGetProjectMembershipDTO,
|
||||
TUpdateProjectMembershipDTO
|
||||
} from "./project-membership-types";
|
||||
@@ -89,6 +90,28 @@ export const projectMembershipServiceFactory = ({
|
||||
return projectMembershipDAL.findAllProjectMembers(projectId);
|
||||
};
|
||||
|
||||
const getProjectMembershipByUsername = async ({
|
||||
actorId,
|
||||
actor,
|
||||
actorOrgId,
|
||||
actorAuthMethod,
|
||||
projectId,
|
||||
username
|
||||
}: TGetProjectMembershipByUsernameDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||
|
||||
const [membership] = await projectMembershipDAL.findAllProjectMembers(projectId, { username });
|
||||
if (!membership) throw new BadRequestError({ message: `Project membership not found for user ${username}` });
|
||||
return membership;
|
||||
};
|
||||
|
||||
const addUsersToProject = async ({
|
||||
projectId,
|
||||
actorId,
|
||||
@@ -510,6 +533,7 @@ export const projectMembershipServiceFactory = ({
|
||||
|
||||
return {
|
||||
getProjectMemberships,
|
||||
getProjectMembershipByUsername,
|
||||
updateProjectMembership,
|
||||
addUsersToProjectNonE2EE,
|
||||
deleteProjectMemberships,
|
||||
|
||||
@@ -9,6 +9,10 @@ export type TInviteUserToProjectDTO = {
|
||||
emails: string[];
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TGetProjectMembershipByUsernameDTO = {
|
||||
username: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TUpdateProjectMembershipDTO = {
|
||||
membershipId: string;
|
||||
roles: (
|
||||
|
||||
Reference in New Issue
Block a user