Merge pull request #2576 from nxglabs/sync-to-public_repo-27677371306

Merge pull request #2575 from nxglabs/staging
This commit is contained in:
raktima-opensignlabs
2026-06-17 09:08:15 +00:00
parent 6271fedcbf
commit be0c4a2cbf
25 changed files with 1079 additions and 671 deletions
@@ -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();
@@ -116,13 +116,24 @@ async function sendMail(document, publicUrl) {
: senderEmail;
if (document.SendinOrder) {
signerMail = signerMail.slice();
signerMail.splice(1);
const getRole = signer => signer?.SignerRole || signer?.signer_role || signer?.role || 'signer';
const firstSignerIndex = signerMail.findIndex(signer => getRole(signer) === 'signer');
signerMail = signerMail.filter((signer, idx) => {
const role = getRole(signer);
return role === 'viewer' || idx === firstSignerIndex;
});
if (signerMail.length === 0 && document?.Placeholders?.length > 0) {
signerMail = document.Placeholders.filter(x => x?.Role !== 'prefill').slice(0, 1);
}
}
for (let i = 0; i < signerMail.length; i++) {
try {
let url = `${serverUrl}/functions/sendmailv3`;
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId };
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
};
const objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
let encodeBase64;
@@ -43,6 +43,8 @@ async function fetchDocumentsByName(
query.notEqualTo('IsArchive', true);
query.descending('updatedAt');
query.exclude('AuditTrail');
query.exclude('OriginalDocument');
query.exclude('SignedDocument');
query.notEqualTo('Type', 'Folder');
try {
return await query.find({ useMasterKey: true });
@@ -36,6 +36,8 @@ export default async function getDrive(request) {
query.skip(skip);
query.limit(limit);
query.exclude('AuditTrail');
query.exclude('OriginalDocument');
query.exclude('SignedDocument');
const res = await query.find({ useMasterKey: true });
return res;
} catch (err) {
@@ -9,6 +9,39 @@ export default async function getUserListByOrg(req) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
if (!OrganizationId) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide organizationId.');
}
// Authorize the requested organization against the caller's server-side
// tenant/organization. This prevents an authenticated user from
// enumerating users in an arbitrary organization or tenant.
const callerQuery = new Parse.Query('contracts_Users');
callerQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: req.user.id,
});
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 callerTenantId = callerExtUser.get('TenantId')?.id;
const callerOrgId = callerExtUser.get('OrganizationId')?.id;
const callerRole = callerExtUser.get('UserRole');
const isAdmin = callerRole === 'contracts_Admin' || callerRole === 'contracts_OrgAdmin';
const orgQuery = new Parse.Query('contracts_Organizations');
const targetOrg = await orgQuery.get(OrganizationId, { useMasterKey: true });
// Must belong to the caller's tenant.
if (!callerTenantId || targetOrg.get('TenantId')?.id !== callerTenantId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
// Non-admins may only list their own organization.
if (!isAdmin && OrganizationId !== callerOrgId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('OrganizationId', orgPtr);
extUser.include('TeamIds');
@@ -8,6 +8,30 @@ export default async function updateTenant(request) {
if (!request.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized');
}
// Derive the caller's tenant and role server-side. Never trust the
// client-supplied tenantId: an authenticated user may only update their own
// tenant and must hold an admin role to do so.
const callerQuery = new Parse.Query('contracts_Users');
callerQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
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');
if (callerRole !== 'contracts_Admin' && callerRole !== 'contracts_OrgAdmin') {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const callerTenantId = callerExtUser.get('TenantId')?.id;
if (!callerTenantId || callerTenantId !== tenantId) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Unauthorized.');
}
const validKeys = ['CompletionBody', 'CompletionSubject', 'RequestBody', 'RequestSubject'];
try {
const tenant = new Parse.Object('partners_Tenant');