mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-22 15:42:31 +02:00
Merge pull request #2576 from nxglabs/sync-to-public_repo-27677371306
Merge pull request #2575 from nxglabs/staging
This commit is contained in:
@@ -7,6 +7,59 @@ export default async function addUser(request) {
|
||||
const currentUser = { __type: 'Pointer', className: '_User', objectId: request.user.id };
|
||||
if (name && email && password && organization && team && role && tenantId) {
|
||||
try {
|
||||
// Derive the caller's tenant/organization/role from the server-side
|
||||
// record rather than trusting client-supplied identifiers. This
|
||||
// prevents an authenticated low-privileged user from creating an admin
|
||||
// or assigning the new user to an arbitrary tenant/organization/team.
|
||||
const callerQuery = new Parse.Query('contracts_Users');
|
||||
callerQuery.equalTo('UserId', currentUser);
|
||||
callerQuery.notEqualTo('IsDisabled', true);
|
||||
const callerExtUser = await callerQuery.first({ useMasterKey: true });
|
||||
if (!callerExtUser) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
const callerRole = callerExtUser.get('UserRole');
|
||||
const isAdmin = callerRole === 'contracts_Admin';
|
||||
const isOrgAdmin = callerRole === 'contracts_OrgAdmin';
|
||||
if (!isAdmin && !isOrgAdmin) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
|
||||
}
|
||||
const callerTenantId = callerExtUser.get('TenantId')?.id;
|
||||
const callerOrgId = callerExtUser.get('OrganizationId')?.id;
|
||||
// Enforce tenant-bound writes for all admins and require org scope for OrgAdmin callers.
|
||||
if (!callerTenantId || tenantId !== callerTenantId || (isOrgAdmin && !callerOrgId)) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
|
||||
}
|
||||
|
||||
// Only allow creating non-admin roles; never allow elevating to a
|
||||
// tenant Admin through this endpoint.
|
||||
const allowedRoles = ['OrgAdmin', 'Editor', 'User'];
|
||||
if (!allowedRoles.includes(role)) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid role.');
|
||||
}
|
||||
|
||||
// Resolve and authorize the target organization within the caller's tenant.
|
||||
const targetOrgId = organization.objectId;
|
||||
if (!targetOrgId) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all required fields.');
|
||||
}
|
||||
const orgQuery = new Parse.Query('contracts_Organizations');
|
||||
const targetOrg = await orgQuery.get(targetOrgId, { useMasterKey: true });
|
||||
if (targetOrg.get('TenantId')?.id !== callerTenantId) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
|
||||
}
|
||||
// An OrgAdmin may only add users to their own organization.
|
||||
if (isOrgAdmin && targetOrgId !== callerOrgId) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
|
||||
}
|
||||
|
||||
// Authorize the target team belongs to the target organization.
|
||||
const teamQuery = new Parse.Query('contracts_Teams');
|
||||
const targetTeam = await teamQuery.get(team, { useMasterKey: true });
|
||||
if (targetTeam.get('OrganizationId')?.id !== targetOrgId) {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
|
||||
}
|
||||
|
||||
const extUser = new Parse.Object('contracts_Users');
|
||||
extUser.set('Name', name);
|
||||
if (phone) {
|
||||
@@ -14,33 +67,27 @@ export default async function addUser(request) {
|
||||
}
|
||||
extUser.set('Email', email);
|
||||
extUser.set('UserRole', `contracts_${role}`);
|
||||
if (team) {
|
||||
extUser.set('TeamIds', [
|
||||
{
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Teams',
|
||||
objectId: team,
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (organization.objectId) {
|
||||
extUser.set('OrganizationId', {
|
||||
extUser.set('TeamIds', [
|
||||
{
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Organizations',
|
||||
objectId: organization.objectId,
|
||||
});
|
||||
}
|
||||
className: 'contracts_Teams',
|
||||
objectId: team,
|
||||
},
|
||||
]);
|
||||
extUser.set('OrganizationId', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Organizations',
|
||||
objectId: targetOrgId,
|
||||
});
|
||||
if (organization.company) {
|
||||
extUser.set('Company', organization.company);
|
||||
}
|
||||
|
||||
if (tenantId) {
|
||||
extUser.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: tenantId,
|
||||
});
|
||||
}
|
||||
extUser.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: callerTenantId,
|
||||
});
|
||||
if (timezone) {
|
||||
extUser.set('Timezone', timezone);
|
||||
}
|
||||
@@ -61,10 +108,10 @@ export default async function addUser(request) {
|
||||
|
||||
extUser.set('UserId', user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(request.user.id, true);
|
||||
acl.setWriteAccess(request.user.id, true);
|
||||
acl.setReadAccess(user.id, true);
|
||||
acl.setWriteAccess(user.id, true);
|
||||
extUser.setACL(acl);
|
||||
const extUserRes = await extUser.save();
|
||||
|
||||
@@ -82,10 +129,10 @@ export default async function addUser(request) {
|
||||
extUser.set('CreatedBy', currentUser);
|
||||
extUser.set('UserId', { __type: 'Pointer', className: '_User', objectId: userRes.id });
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(request.user.id, true);
|
||||
acl.setWriteAccess(request.user.id, true);
|
||||
acl.setReadAccess(userRes.id, true);
|
||||
acl.setWriteAccess(userRes.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
Reference in New Issue
Block a user