Skip to content

Commit

Permalink
test: FORMS-1265 rewrite current user tests (bcgov#1361)
Browse files Browse the repository at this point in the history
  • Loading branch information
WalterMoar authored May 17, 2024
1 parent 8d7ba54 commit 25cef62
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 252 deletions.
324 changes: 162 additions & 162 deletions app/src/forms/auth/middleware/userAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@ const Roles = require('../../common/constants').Roles;
const service = require('../service');
const rbacService = require('../../rbac/service');

/**
* Gets the form metadata for the given formId from the forms available to the
* current user.
*
* @param {*} currentUser the user that is currently logged in; may be public.
* @param {uuid} formId the ID of the form to retrieve for the current user.
* @param {boolean} includeDeleted if active form not found, look for a deleted
* form.
* @returns the form metadata if the currentUser has access, or undefined.
*/
const _getForm = async (currentUser, formId, includeDeleted) => {
if (!uuid.validate(formId)) {
throw new Problem(400, {
detail: 'Bad formId',
});
}

const forms = await service.getUserForms(currentUser, {
active: true,
formId: formId,
});
let form = forms.find((f) => f.formId === formId);

if (!form && includeDeleted) {
const deletedForms = await service.getUserForms(currentUser, {
active: false,
formId: formId,
});
form = deletedForms.find((f) => f.formId === formId);
}

return form;
};

/**
* Checks that the user's permissions contains every required permission.
*
Expand Down Expand Up @@ -52,38 +86,25 @@ const _hasAnyPermission = (userPermissions, requiredPermissions) => {
return intersection.length > 0;
};

/**
* Gets the form metadata for the given formId from the forms available to the
* current user.
*
* @param {*} currentUser the user that is currently logged in; may be public.
* @param {uuid} formId the ID of the form to retrieve for the current user.
* @param {boolean} includeDeleted if active form not found, look for a deleted
* form.
* @returns the form metadata if the currentUser has access, or undefined.
*/
const _getForm = async (currentUser, formId, includeDeleted) => {
if (!uuid.validate(formId)) {
throw new Problem(400, {
detail: 'Bad formId',
});
}
const _hasFormRole = async (formId, user, role) => {
let hasRole = false;

const forms = await service.getUserForms(currentUser, {
const forms = await service.getUserForms(user, {
active: true,
formId: formId,
});
let form = forms.find((f) => f.formId === formId);
const form = forms.find((f) => f.formId === formId);

if (!form && includeDeleted) {
const deletedForms = await service.getUserForms(currentUser, {
active: false,
formId: formId,
});
form = deletedForms.find((f) => f.formId === formId);
if (form) {
for (let j = 0; j < form.roles.length; j++) {
if (form.roles[j] === role) {
hasRole = true;
break;
}
}
}

return form;
return hasRole;
};

/**
Expand Down Expand Up @@ -121,128 +142,6 @@ const currentUser = async (req, _res, next) => {
}
};

/**
* Express middleware to check that a user has all the given permissions for a
* form. This falls through if everything is OK, otherwise it calls next() with
* a Problem describing the error.
*
* @param {string[]} permissions the form permissions that the user must have.
* @returns nothing
*/
const hasFormPermissions = (permissions) => {
return async (req, _res, next) => {
try {
// Skip permission checks if req is already validated using an API key.
if (req.apiUser) {
next();

return;
}

// If the currentUser does not exist it means that the route is not set up
// correctly - the currentUser middleware must be called before this
// middleware.
if (!req.currentUser) {
throw new Problem(500, {
detail: 'Current user not found on request',
});
}

// The request must include a formId, either in params or query, but give
// precedence to params.
const form = await _getForm(req.currentUser, req.params.formId || req.query.formId, true);

// If the form doesn't exist, or its permissions don't exist, then access
// will be denied - otherwise check to see if permissions is a subset.
if (!_hasAllPermissions(form?.permissions, permissions)) {
throw new Problem(401, {
detail: 'You do not have access to this form.',
});
}

next();
} catch (error) {
next(error);
}
};
};

/**
* Express middleware to check that the caller has the given permissions for the
* submission identified by params.formSubmissionId or query.formSubmissionId.
* This falls through if everything is OK, otherwise it calls next() with a
* Problem describing the error.
*
* @param {string[]} permissions the access the user needs for the submission.
* @returns nothing
*/
const hasSubmissionPermissions = (permissions) => {
return async (req, _res, next) => {
try {
// Skip permission checks if req is already authorized using an API key.
if (req.apiUser) {
next();

return;
}

// The request must include a formSubmissionId, either in params or query,
// but give precedence to params.
const submissionId = req.params.formSubmissionId || req.query.formSubmissionId;
if (!uuid.validate(submissionId)) {
throw new Problem(400, {
detail: 'Bad formSubmissionId',
});
}

// Get the submission results so we know what form this submission is for.
const submissionForm = await service.getSubmissionForm(submissionId);

// If the current user has elevated permissions on the form, they may have
// access to all submissions for the form.
if (req.currentUser) {
const formFromCurrentUser = await _getForm(req.currentUser, submissionForm.form.id, false);

// Do they have the submission permissions requested on this form?
if (_hasAllPermissions(formFromCurrentUser?.permissions, permissions)) {
req.formIdWithDeletePermission = submissionForm.form.id;
next();

return;
}
}

// Deleted submissions are only accessible to users with the form
// permissions above.
if (submissionForm.submission.deleted) {
throw new Problem(401, {
detail: 'You do not have access to this submission.',
});
}

// Public (anonymous) forms are publicly viewable.
const publicAllowed = submissionForm.form.identityProviders.find((p) => p.code === 'public') !== undefined;
if (permissions.length === 1 && permissions.includes(Permissions.SUBMISSION_READ) && publicAllowed) {
next();

return;
}

// check against the submission level permissions assigned to the user...
const submissionPermission = await service.checkSubmissionPermission(req.currentUser, submissionId, permissions);
if (!submissionPermission) {
throw new Problem(401, {
detail: 'You do not have access to this submission.',
});
}

next();
} catch (error) {
next(error);
}
};
};

const filterMultipleSubmissions = () => {
return async (req, _res, next) => {
try {
Expand Down Expand Up @@ -303,25 +202,50 @@ const filterMultipleSubmissions = () => {
};
};

const _hasFormRole = async (formId, user, role) => {
let hasRole = false;
/**
* Express middleware to check that a user has all the given permissions for a
* form. This falls through if everything is OK, otherwise it calls next() with
* a Problem describing the error.
*
* @param {string[]} permissions the form permissions that the user must have.
* @returns nothing
*/
const hasFormPermissions = (permissions) => {
return async (req, _res, next) => {
try {
// Skip permission checks if req is already validated using an API key.
if (req.apiUser) {
next();

const forms = await service.getUserForms(user, {
active: true,
formId: formId,
});
const form = forms.find((f) => f.formId === formId);
return;
}

if (form) {
for (let j = 0; j < form.roles.length; j++) {
if (form.roles[j] === role) {
hasRole = true;
break;
// If the currentUser does not exist it means that the route is not set up
// correctly - the currentUser middleware must be called before this
// middleware.
if (!req.currentUser) {
throw new Problem(500, {
detail: 'Current user not found on request',
});
}
}
}

return hasRole;
// The request must include a formId, either in params or query, but give
// precedence to params.
const form = await _getForm(req.currentUser, req.params.formId || req.query.formId, true);

// If the form doesn't exist, or its permissions don't exist, then access
// will be denied - otherwise check to see if permissions is a subset.
if (!_hasAllPermissions(form?.permissions, permissions)) {
throw new Problem(401, {
detail: 'You do not have access to this form.',
});
}

next();
} catch (error) {
next(error);
}
};
};

/**
Expand Down Expand Up @@ -457,6 +381,82 @@ const hasRolePermissions = (removingUsers = false) => {
};
};

/**
* Express middleware to check that the caller has the given permissions for the
* submission identified by params.formSubmissionId or query.formSubmissionId.
* This falls through if everything is OK, otherwise it calls next() with a
* Problem describing the error.
*
* @param {string[]} permissions the access the user needs for the submission.
* @returns nothing
*/
const hasSubmissionPermissions = (permissions) => {
return async (req, _res, next) => {
try {
// Skip permission checks if req is already authorized using an API key.
if (req.apiUser) {
next();

return;
}

// The request must include a formSubmissionId, either in params or query,
// but give precedence to params.
const submissionId = req.params.formSubmissionId || req.query.formSubmissionId;
if (!uuid.validate(submissionId)) {
throw new Problem(400, {
detail: 'Bad formSubmissionId',
});
}

// Get the submission results so we know what form this submission is for.
const submissionForm = await service.getSubmissionForm(submissionId);

// If the current user has elevated permissions on the form, they may have
// access to all submissions for the form.
if (req.currentUser) {
const formFromCurrentUser = await _getForm(req.currentUser, submissionForm.form.id, false);

// Do they have the submission permissions requested on this form?
if (_hasAllPermissions(formFromCurrentUser?.permissions, permissions)) {
req.formIdWithDeletePermission = submissionForm.form.id;
next();

return;
}
}

// Deleted submissions are only accessible to users with the form
// permissions above.
if (submissionForm.submission.deleted) {
throw new Problem(401, {
detail: 'You do not have access to this submission.',
});
}

// Public (anonymous) forms are publicly viewable.
const publicAllowed = submissionForm.form.identityProviders.find((p) => p.code === 'public') !== undefined;
if (permissions.length === 1 && permissions.includes(Permissions.SUBMISSION_READ) && publicAllowed) {
next();

return;
}

// check against the submission level permissions assigned to the user...
const submissionPermission = await service.checkSubmissionPermission(req.currentUser, submissionId, permissions);
if (!submissionPermission) {
throw new Problem(401, {
detail: 'You do not have access to this submission.',
});
}

next();
} catch (error) {
next(error);
}
};
};

module.exports = {
currentUser,
filterMultipleSubmissions,
Expand Down
Loading

0 comments on commit 25cef62

Please sign in to comment.