mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 20:27:12 +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: {
|
GET_USER_MEMBERSHIPS: {
|
||||||
workspaceId: "The ID of the project to get memberships from."
|
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: {
|
UPDATE_USER_MEMBERSHIP: {
|
||||||
workspaceId: "The ID of the project to update the membership for.",
|
workspaceId: "The ID of the project to update the membership for.",
|
||||||
membershipId: "The ID of the membership to update.",
|
membershipId: "The ID of the membership to update.",
|
||||||
@@ -208,6 +212,10 @@ export const PROJECT_IDENTITIES = {
|
|||||||
LIST_IDENTITY_MEMBERSHIPS: {
|
LIST_IDENTITY_MEMBERSHIPS: {
|
||||||
projectId: "The ID of the project to get identity memberships from."
|
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: {
|
UPDATE_IDENTITY_MEMBERSHIP: {
|
||||||
projectId: "The ID of the project to update the identity membership for.",
|
projectId: "The ID of the project to update the identity membership for.",
|
||||||
identityId: "The ID of the identity to update the 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({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:workspaceId/memberships",
|
url: "/:workspaceId/memberships",
|
||||||
|
|||||||
@@ -251,4 +251,61 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
return { identityMemberships };
|
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) => {
|
export const identityProjectDALFactory = (db: TDbClient) => {
|
||||||
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
const identityProjectOrm = ormify(db, TableName.IdentityProjectMembership);
|
||||||
|
|
||||||
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
const findByProjectId = async (projectId: string, filter: { identityId?: string } = {}, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
const docs = await (tx || db)(TableName.IdentityProjectMembership)
|
||||||
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
.where(`${TableName.IdentityProjectMembership}.projectId`, projectId)
|
||||||
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
.join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`)
|
||||||
|
.where((qb) => {
|
||||||
|
if (filter.identityId) {
|
||||||
|
void qb.where("identityId", filter.identityId);
|
||||||
|
}
|
||||||
|
})
|
||||||
.join(
|
.join(
|
||||||
TableName.IdentityProjectMembershipRole,
|
TableName.IdentityProjectMembershipRole,
|
||||||
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
`${TableName.IdentityProjectMembershipRole}.projectMembershipId`,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { TIdentityProjectMembershipRoleDALFactory } from "./identity-project-mem
|
|||||||
import {
|
import {
|
||||||
TCreateProjectIdentityDTO,
|
TCreateProjectIdentityDTO,
|
||||||
TDeleteProjectIdentityDTO,
|
TDeleteProjectIdentityDTO,
|
||||||
|
TGetProjectIdentityByIdentityIdDTO,
|
||||||
TListProjectIdentityDTO,
|
TListProjectIdentityDTO,
|
||||||
TUpdateProjectIdentityDTO
|
TUpdateProjectIdentityDTO
|
||||||
} from "./identity-project-types";
|
} from "./identity-project-types";
|
||||||
@@ -282,10 +283,33 @@ export const identityProjectServiceFactory = ({
|
|||||||
return identityMemberships;
|
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 {
|
return {
|
||||||
createProjectIdentity,
|
createProjectIdentity,
|
||||||
updateProjectIdentity,
|
updateProjectIdentity,
|
||||||
deleteProjectIdentity,
|
deleteProjectIdentity,
|
||||||
listProjectIdentities
|
listProjectIdentities,
|
||||||
|
getProjectIdentityByIdentityId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,3 +41,7 @@ export type TDeleteProjectIdentityDTO = {
|
|||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TListProjectIdentityDTO = 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);
|
const projectMemberOrm = ormify(db, TableName.ProjectMembership);
|
||||||
|
|
||||||
// special query
|
// special query
|
||||||
const findAllProjectMembers = async (projectId: string) => {
|
const findAllProjectMembers = async (projectId: string, filter: { usernames?: string[]; username?: string } = {}) => {
|
||||||
try {
|
try {
|
||||||
const docs = await db(TableName.ProjectMembership)
|
const docs = await db(TableName.ProjectMembership)
|
||||||
.where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId })
|
.where({ [`${TableName.ProjectMembership}.projectId` as "projectId"]: projectId })
|
||||||
.join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`)
|
.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>(
|
.join<TUserEncryptionKeys>(
|
||||||
TableName.UserEncryptionKey,
|
TableName.UserEncryptionKey,
|
||||||
`${TableName.UserEncryptionKey}.userId`,
|
`${TableName.UserEncryptionKey}.userId`,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
TAddUsersToWorkspaceNonE2EEDTO,
|
TAddUsersToWorkspaceNonE2EEDTO,
|
||||||
TDeleteProjectMembershipOldDTO,
|
TDeleteProjectMembershipOldDTO,
|
||||||
TDeleteProjectMembershipsDTO,
|
TDeleteProjectMembershipsDTO,
|
||||||
|
TGetProjectMembershipByUsernameDTO,
|
||||||
TGetProjectMembershipDTO,
|
TGetProjectMembershipDTO,
|
||||||
TUpdateProjectMembershipDTO
|
TUpdateProjectMembershipDTO
|
||||||
} from "./project-membership-types";
|
} from "./project-membership-types";
|
||||||
@@ -89,6 +90,28 @@ export const projectMembershipServiceFactory = ({
|
|||||||
return projectMembershipDAL.findAllProjectMembers(projectId);
|
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 ({
|
const addUsersToProject = async ({
|
||||||
projectId,
|
projectId,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -510,6 +533,7 @@ export const projectMembershipServiceFactory = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
getProjectMemberships,
|
getProjectMemberships,
|
||||||
|
getProjectMembershipByUsername,
|
||||||
updateProjectMembership,
|
updateProjectMembership,
|
||||||
addUsersToProjectNonE2EE,
|
addUsersToProjectNonE2EE,
|
||||||
deleteProjectMemberships,
|
deleteProjectMemberships,
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ export type TInviteUserToProjectDTO = {
|
|||||||
emails: string[];
|
emails: string[];
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
|
export type TGetProjectMembershipByUsernameDTO = {
|
||||||
|
username: string;
|
||||||
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TUpdateProjectMembershipDTO = {
|
export type TUpdateProjectMembershipDTO = {
|
||||||
membershipId: string;
|
membershipId: string;
|
||||||
roles: (
|
roles: (
|
||||||
|
|||||||
Reference in New Issue
Block a user