mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 09:25:08 +10:00
fix: resolve permission issues
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/organisations';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
|
||||
import { getMemberOrganisationRole } from '@documenso/lib/server-only/team/get-member-roles';
|
||||
import { buildOrganisationWhereQuery, isOrganisationRoleWithinUserHierarchy } from '@documenso/lib/utils/organisations';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { OrganisationGroupType } from '@prisma/client';
|
||||
|
||||
@@ -59,6 +60,22 @@ export const deleteOrganisationGroupRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
const currentUserOrganisationRole = await getMemberOrganisationRole({
|
||||
organisationId,
|
||||
reference: {
|
||||
type: 'User',
|
||||
id: user.id,
|
||||
},
|
||||
});
|
||||
|
||||
// A user cannot delete a group whose role is higher than their own
|
||||
// (e.g. a manager deleting an admin-role group).
|
||||
if (!isOrganisationRoleWithinUserHierarchy(currentUserOrganisationRole, group.organisationRole)) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You are not allowed to delete this organisation group',
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.organisationGroup.delete({
|
||||
where: {
|
||||
id: groupId,
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { syncMemberCountWithStripeSeatPlan } from '@documenso/ee/server-only/stripe/update-subscription-item-quantity';
|
||||
import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/organisations';
|
||||
import {
|
||||
ORGANISATION_MEMBER_ROLE_HIERARCHY,
|
||||
ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP,
|
||||
} from '@documenso/lib/constants/organisations';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { jobs } from '@documenso/lib/jobs/client';
|
||||
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
|
||||
import {
|
||||
buildOrganisationWhereQuery,
|
||||
getHighestOrganisationRoleInGroup,
|
||||
isOrganisationRoleWithinUserHierarchy,
|
||||
} from '@documenso/lib/utils/organisations';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { OrganisationMemberInviteStatus } from '@documenso/prisma/client';
|
||||
|
||||
@@ -60,9 +67,12 @@ export const deleteOrganisationMembers = async ({
|
||||
},
|
||||
},
|
||||
members: {
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
include: {
|
||||
organisationGroupMembers: {
|
||||
include: {
|
||||
group: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
invites: {
|
||||
@@ -84,6 +94,41 @@ export const deleteOrganisationMembers = async ({
|
||||
|
||||
const membersToDelete = organisation.members.filter((member) => organisationMemberIds.includes(member.id));
|
||||
|
||||
const currentUserMember = organisation.members.find((member) => member.userId === userId);
|
||||
|
||||
if (!currentUserMember) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
const currentUserOrganisationRole = getHighestOrganisationRoleInGroup(
|
||||
currentUserMember.organisationGroupMembers.map(({ group }) => group),
|
||||
);
|
||||
|
||||
// The roles the current user is allowed to act on (their own role and below).
|
||||
const manageableOrganisationRoles = ORGANISATION_MEMBER_ROLE_HIERARCHY[currentUserOrganisationRole];
|
||||
|
||||
for (const member of membersToDelete) {
|
||||
// The organisation owner can never be removed via this route. Ownership must
|
||||
// be transferred first (mirrors the admin and update-member routes).
|
||||
if (member.userId === organisation.ownerUserId) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Cannot remove the organisation owner',
|
||||
});
|
||||
}
|
||||
|
||||
const memberOrganisationRole = getHighestOrganisationRoleInGroup(
|
||||
member.organisationGroupMembers.map(({ group }) => group),
|
||||
);
|
||||
|
||||
// A user cannot remove a member whose role is higher than their own
|
||||
// (e.g. a manager removing an admin).
|
||||
if (!isOrganisationRoleWithinUserHierarchy(currentUserOrganisationRole, memberOrganisationRole)) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Cannot remove a member with a higher role',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const inviteCount = organisation.invites.length;
|
||||
const newMemberCount = organisation.members.length + inviteCount - membersToDelete.length;
|
||||
|
||||
@@ -123,6 +168,18 @@ export const deleteOrganisationMembers = async ({
|
||||
in: organisationMemberIds,
|
||||
},
|
||||
organisationId,
|
||||
userId: {
|
||||
not: organisation.ownerUserId,
|
||||
},
|
||||
organisationGroupMembers: {
|
||||
none: {
|
||||
group: {
|
||||
organisationRole: {
|
||||
notIn: manageableOrganisationRoles,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,6 +51,14 @@ export const leaveOrganisationRoute = authenticatedProcedure
|
||||
throw new AppError(AppErrorCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
// The organisation owner cannot leave their own organisation. Ownership must
|
||||
// be transferred to another member first.
|
||||
if (organisation.ownerUserId === userId) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You cannot leave an organisation you own. Please transfer ownership first.',
|
||||
});
|
||||
}
|
||||
|
||||
const { organisationClaim } = organisation;
|
||||
|
||||
const inviteCount = organisation.invites.length;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/organisations';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { sendOrganisationMemberInviteEmail } from '@documenso/lib/server-only/organisation/create-organisation-member-invites';
|
||||
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
|
||||
import { getMemberOrganisationRole } from '@documenso/lib/server-only/team/get-member-roles';
|
||||
import { buildOrganisationWhereQuery, isOrganisationRoleWithinUserHierarchy } from '@documenso/lib/utils/organisations';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
@@ -97,6 +98,21 @@ export const resendOrganisationMemberInvitation = async ({
|
||||
});
|
||||
}
|
||||
|
||||
const currentUserOrganisationRole = await getMemberOrganisationRole({
|
||||
organisationId: organisation.id,
|
||||
reference: {
|
||||
type: 'User',
|
||||
id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
// A user cannot interact with an invitation that is not within their own hierarchy.
|
||||
if (!isOrganisationRoleWithinUserHierarchy(currentUserOrganisationRole, invitation.organisationRole)) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You cannot resend an invite for a member with a higher role',
|
||||
});
|
||||
}
|
||||
|
||||
await sendOrganisationMemberInviteEmail({
|
||||
email: invitation.email,
|
||||
token: invitation.token,
|
||||
|
||||
Reference in New Issue
Block a user