mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: made project role multi support for org invite
This commit is contained in:
@@ -21,7 +21,7 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
projects: z
|
projects: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
projectRoleSlug: z.string().default(ProjectMembershipRole.Member)
|
projectRoleSlug: z.string().array().default([ProjectMembershipRole.Member])
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
id: req.params.projectId,
|
id: req.params.projectId,
|
||||||
projectRoleSlug: ProjectMembershipRole.Member
|
projectRoleSlug: [ProjectMembershipRole.Member]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
TableName,
|
TableName,
|
||||||
TProjectMemberships,
|
TProjectMemberships,
|
||||||
|
TProjectUserMembershipRolesInsert,
|
||||||
TUsers
|
TUsers
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { TProjects } from "@app/db/schemas/projects";
|
import { TProjects } from "@app/db/schemas/projects";
|
||||||
@@ -88,7 +89,7 @@ type TOrgServiceFactoryDep = {
|
|||||||
"getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer"
|
"getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer"
|
||||||
>;
|
>;
|
||||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
|
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findUserGroupMembershipsInProject">;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findUserGroupMembershipsInProject">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
|
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
|
||||||
@@ -652,12 +653,15 @@ export const orgServiceFactory = ({
|
|||||||
if (!userWithEncryptionKeyInvitedToProject.length) continue;
|
if (!userWithEncryptionKeyInvitedToProject.length) continue;
|
||||||
|
|
||||||
// validate custom project role
|
// validate custom project role
|
||||||
const invitedProjectRole =
|
const invitedProjectRoles = invitedProjects.find((el) => el.id === project.id)?.projectRoleSlug || [
|
||||||
invitedProjects.find((el) => el.id === project.id)?.projectRoleSlug || ProjectMembershipRole.Member;
|
ProjectMembershipRole.Member
|
||||||
const isCustomProjectRole = !Object.values(ProjectMembershipRole).includes(
|
];
|
||||||
invitedProjectRole as ProjectMembershipRole
|
|
||||||
|
const customProjectRoles = invitedProjectRoles.filter(
|
||||||
|
(role) => !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole)
|
||||||
);
|
);
|
||||||
if (isCustomProjectRole) {
|
const hasCustomRole = Boolean(customProjectRoles.length);
|
||||||
|
if (hasCustomRole) {
|
||||||
if (!plan?.rbac)
|
if (!plan?.rbac)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message:
|
message:
|
||||||
@@ -665,6 +669,17 @@ export const orgServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customRoles = hasCustomRole
|
||||||
|
? await projectRoleDAL.find({
|
||||||
|
projectId,
|
||||||
|
$in: { slug: customProjectRoles.map((role) => role) }
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
if (customRoles.length !== customProjectRoles.length)
|
||||||
|
throw new BadRequestError({ message: "Custom role not found" });
|
||||||
|
|
||||||
|
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||||
|
|
||||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||||
if (!ghostUser) {
|
if (!ghostUser) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -698,7 +713,7 @@ export const orgServiceFactory = ({
|
|||||||
userPrivateKey: botPrivateKey,
|
userPrivateKey: botPrivateKey,
|
||||||
members: userWithEncryptionKeyInvitedToProject.map((userEnc) => ({
|
members: userWithEncryptionKeyInvitedToProject.map((userEnc) => ({
|
||||||
orgMembershipId: userEnc.userId,
|
orgMembershipId: userEnc.userId,
|
||||||
projectMembershipRole: invitedProjectRole as ProjectMembershipRole,
|
projectMembershipRole: ProjectMembershipRole.Admin,
|
||||||
userPublicKey: userEnc.publicKey
|
userPublicKey: userEnc.publicKey
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
@@ -712,17 +727,18 @@ export const orgServiceFactory = ({
|
|||||||
);
|
);
|
||||||
newProjectMemberships.push(...projectMemberships);
|
newProjectMemberships.push(...projectMemberships);
|
||||||
|
|
||||||
let customRoleId: string;
|
const sanitizedProjectMembershipRoles: TProjectUserMembershipRolesInsert[] = [];
|
||||||
const projectRole = isCustomProjectRole ? ProjectMembershipRole.Custom : invitedProjectRole;
|
invitedProjectRoles.forEach((projectRole) => {
|
||||||
if (isCustomProjectRole) {
|
const isCustomRole = Boolean(customRolesGroupBySlug?.[projectRole]?.[0]);
|
||||||
const customRole = await projectRoleDAL.findOne({ slug: invitedProjectRole, projectId });
|
projectMemberships.forEach((membership) => {
|
||||||
if (!customRole) throw new BadRequestError({ name: "Invite membership", message: "Project role not found" });
|
sanitizedProjectMembershipRoles.push({
|
||||||
customRoleId = customRole.id;
|
projectMembershipId: membership.id,
|
||||||
}
|
role: isCustomRole ? ProjectMembershipRole.Custom : projectRole,
|
||||||
await projectUserMembershipRoleDAL.insertMany(
|
customRoleId: customRolesGroupBySlug[projectRole] ? customRolesGroupBySlug[projectRole][0].id : null
|
||||||
projectMemberships.map(({ id }) => ({ projectMembershipId: id, role: projectRole, customRoleId })),
|
});
|
||||||
tx
|
});
|
||||||
);
|
});
|
||||||
|
await projectUserMembershipRoleDAL.insertMany(sanitizedProjectMembershipRoles, tx);
|
||||||
|
|
||||||
await projectKeyDAL.insertMany(
|
await projectKeyDAL.insertMany(
|
||||||
newWsMembers.map((el) => ({
|
newWsMembers.map((el) => ({
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export type TInviteUserToOrgDTO = {
|
|||||||
organizationRoleSlug: string;
|
organizationRoleSlug: string;
|
||||||
projects?: {
|
projects?: {
|
||||||
id: string;
|
id: string;
|
||||||
projectRoleSlug?: string;
|
projectRoleSlug?: string[];
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ export type DeletOrgMembershipDTO = {
|
|||||||
|
|
||||||
export type AddUserToOrgDTO = {
|
export type AddUserToOrgDTO = {
|
||||||
inviteeEmails: string[];
|
inviteeEmails: string[];
|
||||||
projects?: { id: string; projectRoleSlug: string }[];
|
projects?: { id: string; projectRoleSlug: string[] }[];
|
||||||
organizationRoleSlug: string;
|
organizationRoleSlug: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export const AddOrgMemberModal = ({
|
|||||||
organizationId: currentOrg?.id,
|
organizationId: currentOrg?.id,
|
||||||
inviteeEmails: emails.split(",").map((email) => email.trim()),
|
inviteeEmails: emails.split(",").map((email) => email.trim()),
|
||||||
organizationRoleSlug,
|
organizationRoleSlug,
|
||||||
projects: projectIds.map((id) => ({ id, projectRoleSlug }))
|
projects: projectIds.map((id) => ({ id, projectRoleSlug: [projectRoleSlug] }))
|
||||||
});
|
});
|
||||||
|
|
||||||
setCompleteInviteLinks(data?.completeInviteLinks ?? null);
|
setCompleteInviteLinks(data?.completeInviteLinks ?? null);
|
||||||
|
|||||||
@@ -101,9 +101,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
members?.forEach((member) => {
|
members?.forEach((member) => {
|
||||||
wsUserUsernames.set(member.user.username, true);
|
wsUserUsernames.set(member.user.username, true);
|
||||||
});
|
});
|
||||||
return (orgUsers || []).filter(
|
return (orgUsers || []).filter(({ user: u }) => !wsUserUsernames.has(u.username));
|
||||||
({ status, user: u }) => status === "accepted" && !wsUserUsernames.has(u.username)
|
|
||||||
);
|
|
||||||
}, [orgUsers, members]);
|
}, [orgUsers, members]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user