UNPKG

formio

Version:

The formio server application.

1,169 lines (970 loc) • 454 kB
/* eslint-env mocha */ 'use strict'; const request = require('./formio-supertest'); var assert = require('assert'); var async = require('async'); var _ = require('lodash'); var docker = process.env.DOCKER; // Request a 401. var request401 = function(request, done, user) { if (user) { request.set('x-jwt-token', user.token); } request .expect(401) .expect('Content-Type', /text\/plain/) .end(function(err, res) { if (err) { return done(err); } assert.equal(res.text, 'Unauthorized'); if (user) { // Store the JWT for future API calls. user.token = res.headers['x-jwt-token']; } done(); } ); }; module.exports = function(app, template, hook) { var Helper = require('./helper')(app); describe('Submissions', function() { describe('Submission Level Permissions (Project Owner)', function() { describe('Submission CRUD', function() { // Store the temp form for this test suite. var tempForm = { title: 'Project owner access check', name: 'access', path: 'accessowner', type: 'form', access: [], submissionAccess: [], components: [ { type: 'textfield', validate: { custom: '', pattern: '', maxLength: '', minLength: '', required: false }, defaultValue: '', multiple: false, suffix: '', prefix: '', placeholder: 'value', key: 'value', label: 'value', inputMask: '', inputType: 'text', input: true } ] }; // Store the temp submission for this test suite. var tempSubmission = {data: {value: 'foo'}}; var tempSubmissions = []; describe('Bootstrap', function() { it('Create a Form for a Submission level Access Check - Project Owner', function(done) { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(tempForm) .expect('Content-Type', /json/) .expect(201) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('access'), 'The response should contain an the `access`.'); assert.equal(response.title, tempForm.title); assert.equal(response.name, tempForm.name); assert.equal(response.path, tempForm.path); assert.equal(response.type, 'form'); assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 3); assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, tempForm.components); tempForm = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); describe('Project Owner Submission - Delete all submissions', function() { var deleteTest = {data: {value: 'foo'}}; it('The Project Owner should be able to Create a submission without explicit permissions using the Form alias', function(done) { request(app) .post(hook.alter('url', '/' + tempForm.path + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .send(deleteTest) .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); assert.equal(response.data.value, deleteTest.data.value); assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); assert.equal(response.form, tempForm._id); assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); assert.deepEqual(response.roles, []); assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); // Update the submission data. deleteTest = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Delete all form submissions with the confirmation header', function(done) { request(app) .delete(hook.alter('url', '/' + tempForm.path + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .set('x-delete-confirm', tempForm._id) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should not be able to Delete all form submissions without the confirmation header', function(done) { request(app) .delete(hook.alter('url', '/' + tempForm.path + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .expect(400) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {error: 'No confirmation header provided'}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); describe('Project Owner Submission', function() { it('The Project Owner should be able to Create a submission without explicit permissions', function(done) { // Test that roles can not be added on creation. tempSubmission.roles = [template.roles.administrator._id.toString()]; request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); assert.equal(response.data.value, tempSubmission.data.value); assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); assert.equal(response.form, tempForm._id); assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); assert.deepEqual(response.roles, []); assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); // Update the submission data. tempSubmission = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Read a submission without explicit permissions', function(done) { request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Update a submission without explicit permissions', function(done) { var updatedSubmission = _.clone(tempSubmission); updatedSubmission.data.value = 'bar'; request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .send({data: {value: updatedSubmission.data.value}}) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Update the modified timestamp for response comparison. updatedSubmission.modified = response.modified; assert.deepEqual(response, updatedSubmission); // Update the submission data. tempSubmission = updatedSubmission; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should not be able to add roles to a submission', (done) => { var updatedSubmission = _.clone(tempSubmission); updatedSubmission.data.value = 'bar'; updatedSubmission.roles = [template.roles.administrator._id.toString()]; request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .send(updatedSubmission) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Update the modified timestamp for response comparison. updatedSubmission.modified = response.modified; updatedSubmission.roles = []; assert.deepEqual(response, updatedSubmission); // Update the submission data. tempSubmission = updatedSubmission; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Read the Index of submissions without explicit permissions', function(done) { request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.equal(response.length, 1, 'The response should contain 1 element'); assert.deepEqual(response[0], tempSubmission); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Read the Index of submissions without explicit permissions without data', function(done) { request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission?list=1', template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.equal(response.length, 1, 'The response should contain 1 element'); assert(!response[0].hasOwnProperty('data')); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Read a submission without explicit permissions using the Form alias', function(done) { request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Update a submission without explicit permissions using the Form alias', function(done) { var updatedSubmission = _.clone(tempSubmission); updatedSubmission.data.value = 'bar2'; request(app) .put(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .send({data: {value: updatedSubmission.data.value}}) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Update the modified timestamp for response comparison. updatedSubmission.modified = response.modified; assert.deepEqual(response, updatedSubmission); tempSubmission = updatedSubmission; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Read the Index of submissions without explicit permissions using the Form alias', function(done) { request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.equal(response.length, 1); assert.deepEqual(response[0], tempSubmission); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('A user with full permissions should not be able to edit data outside the submission.data object', function(done) { var updatedSubmission = _.clone(tempSubmission); updatedSubmission.data.value = 'bar3'; request(app) .put(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .send({ other: 'this should not save', // try to add a field that is not present externalIds: [{foo: 'bar'}], // try to edit a field that exists on the submissions w/ timestamp plugin roles: [].concat(tempSubmission.roles, template.users.admin._id), // try to edit a field that exists on the submissions data: {value: updatedSubmission.data.value} }) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Update the modified timestamp for response comparison. updatedSubmission.modified = response.modified; // Confirm that other data was not saved. assert.equal(response.hasOwnProperty('other'), false); // Confirm that the externalIds were not modified by the user request. assert.deepEqual(response.externalIds, tempSubmission.externalIds); // Confirm that the roles were not modified by the user request. assert.deepEqual(response.roles, tempSubmission.roles); // Confirm that nothing else was changes (besides the automatic modified timestamp). assert.deepEqual(response, updatedSubmission); // Update the submission data. tempSubmission = updatedSubmission; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); var deleteTest = {data: {value: 'foo'}}; it('The Project Owner should be able to Create a submission without explicit permissions using the Form alias', function(done) { request(app) .post(hook.alter('url', '/' + tempForm.path + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .send(deleteTest) .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); assert.equal(response.data.value, deleteTest.data.value); assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); assert.equal(response.form, tempForm._id); assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); assert.deepEqual(response.roles, []); assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); // Update the submission data. deleteTest = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to Delete a submission without explicit permissions using the Form alias', function(done) { request(app) .delete(hook.alter('url', '/' + tempForm.path + '/submission/' + deleteTest._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); if (!docker) it('A deleted Submission should remain in the database', async function() { var formio = hook.alter('formio', app.formio); let submission = await formio.resources.submission.model.findOne({_id: deleteTest._id}); if (!submission) { throw('No submission found with _id: ' + deleteTest._id + ', expected 1.'); } submission = submission.toObject(); assert.notEqual(submission.deleted, null); }); it('Cant access a submission without a valid Submission Id', function(done) { request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission/2342342344234', template)) .set('x-jwt-token', template.users.admin.token) .expect(400) .end(function(err, res) { if (err) { return done(err); } // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); describe('Authenticated User Submission', function() { it('A Registered user should not be able to Create a submission without explicit permissions', function(done) { var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send(tempSubmission); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Read a submission without explicit permissions', function(done) { var req = request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Update a submission without explicit permissions', function(done) { var req = request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .send({foo: 'bar'}); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Read the Index of submissions without explicit permissions', function(done) { var req = request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Read a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Update a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .put(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)) .send({foo: 'bar'}); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Read the Index of submissions without explicit permissions using the Form alias', function(done) { var req = request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission', template)); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Create a submissions without explicit permissions using the Form alias', function(done) { var req = request(app) .post(hook.alter('url', '/' + tempForm.path + '/submission', template)) .send(tempSubmission); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Delete a submissions without explicit permissions using the Form alias', function(done) { var req = request(app) .delete(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)); request401(req, done, template.users.user1); }); it('A Registered user should not be able to Delete a submission without explicit permissions', function(done) { var req = request(app) .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)); request401(req, done, template.users.user1); }); }); describe('Anonymous User Submission', function() { it('An Anonymous user should not be able to Create a submission without explicit permissions', function(done) { var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send(tempSubmission); request401(req, done); }); it('An Anonymous user should not be able to Read a submission without explicit permissions', function(done) { var req = request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)); request401(req, done); }); it('An Anonymous user should not be able to Update a submission without explicit permissions', function(done) { var req = request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .send({foo: 'bar'}); request401(req, done); }); it('An Anonymous user should not be able to Read the Index of submissions without explicit permissions', function(done) { var req = request(app) .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)); request401(req, done); }); it('An Anonymous user should not be able to Read a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)); request401(req, done); }); it('An Anonymous user should not be able to Update a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .put(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)) .send({foo: 'bar'}); request401(req, done); }); it('An Anonymous user should not be able to Read the Index of submissions without explicit permissions using the Form alias', function(done) { var req = request(app) .get(hook.alter('url', '/' + tempForm.path + '/submission', template)); request401(req, done); }); it('An Anonymous user should not be able to Create a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .post(hook.alter('url', '/' + tempForm.path + '/submission', template)) .send({foo: 'bar'}); request401(req, done); }); it('An Anonymous user should not be able to Delete a submission without explicit permissions using the Form alias', function(done) { var req = request(app) .delete(hook.alter('url', '/' + tempForm.path + '/submission/' + tempSubmission._id, template)); request401(req, done); }); it('An Anonymous user should not be able to Delete a submission without explicit permissions', function(done) { var req = request(app) .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)); request401(req, done); }); }); describe('Submission Normalization', function() { it('The Project owner should be able to Delete a submission with explicit Own permissions', function(done) { request(app) .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); if (!docker) it('A deleted Submission should remain in the database', async function() { var formio = hook.alter('formio', app.formio); let submission = await formio.resources.submission.model.findOne({_id: tempSubmission._id}) .exec(); if (!submission) { throw('No submission found w/ _id: ' + submission._id + ', expected 1.'); } submission = submission.toObject(); assert.notEqual(submission.deleted, null); }); it('Delete the Submissions created for Ownership Checks', function(done) { tempSubmissions.forEach(function(submission) { request(app) .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + submission._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; }); }); tempSubmissions = []; done(); }); }); describe('Form Normalization', function() { it('Delete the form created for Access Checks', function(done) { request(app) .delete(hook.alter('url', '/form/' + tempForm._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); if (!docker) it('A deleted Form should not have active submissions in the database', async function() { var formio = hook.alter('formio', app.formio); const submissions = await formio.resources.submission.model.findOne({form: tempForm._id, deleted: {$eq: null}}) .exec(); if (submissions && submissions.length !== 0) { throw(submissions.length + ' submissions found with the form: ' + tempForm._id + ', expected 0.'); } }); }); }); describe('Submission Ownership', function() { // Store the temp form for this test suite. var tempForm = { title: 'dummyForm', name: 'dummyForm', path: 'dummy/form', type: 'form', access: [], submissionAccess: [], components: [ { type: 'textfield', validate: { custom: '', pattern: '', maxLength: '', minLength: '', required: false }, defaultValue: '', multiple: false, suffix: '', prefix: '', placeholder: 'value', key: 'value', label: 'value', inputMask: '', inputType: 'text', input: true } ] }; // Store the temp submissions for this test suite. var tempSubmissions = []; var temp = {}; describe('Bootstrap', function() { it('Create the Form for Ownership Checks', function(done) { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(tempForm) .expect('Content-Type', /json/) .expect(201) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('access'), 'The response should contain an the `access`.'); assert.equal(response.title, tempForm.title); assert.equal(response.name, tempForm.name); assert.equal(response.path, tempForm.path); assert.equal(response.type, 'form'); assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 3); assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, tempForm.components); tempForm = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); describe('Project Owner', function() { it('The Project Owner should create a submission in their name, when the owner is not specified, without permissions', function(done) { var tempSubmission = {data: {value: 'foo'}}; request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); assert.equal(response.data.value, tempSubmission.data.value); assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); assert.equal(response.form, tempForm._id); assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); assert.deepEqual(response.roles, []); assert(response.hasOwnProperty('owner'), 'The response should contain the resource `owner`.'); assert.equal(response.owner, template.users.admin._id); assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); // Update the submission data. tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to create a submission in someones name, without permissions', function(done) { var tempSubmission = {data: {value: 'foo'}, owner: template.users.user1._id}; request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); assert.equal(response.data.value, tempSubmission.data.value); assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); assert.equal(response.form, tempForm._id); assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); assert.deepEqual(response.roles, []); assert(response.hasOwnProperty('owner'), 'The response should contain the resource `owner`.'); assert.notEqual(response.owner, null); assert.equal(response.owner, tempSubmission.owner); assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); // Update the submission data. tempSubmissions.push(response); // Store the response for an update test. temp = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('The Project Owner should be able to update the owner of a submission, without explicit permissions', function(done) { request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + temp._id, template)) .set('x-jwt-token', template.users.admin.token) .send({owner: template.users.admin._id}) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Update the owner of temp for comparison. temp.owner = template.users.admin._id; // Remove the modified timestamp for comparison. assert.deepEqual(_.omit(response, 'modified'), _.omit(temp, 'modified')); // Update the temp form contents. temp = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); it('Updating a submission with explicit empty data, will remove all the data', function(done) { request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + temp._id, template)) .set('x-jwt-token', template.users.admin.token) .send({data: {}}) .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) { return done(err); } var response = res.body; // Compare the previous and current data contents before deep comparison. assert.equal(Object.keys(response.data).length, 0); assert.notEqual(temp.data, response.data); temp.data = {}; // Remove the modified timestamp for comparison. assert.deepEqual(_.omit(response, 'modified'), _.omit(temp, 'modified')); // Update the temp form contents. temp = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); describe('Authenticated User', function() { it('An Authenticated User should not be able create a submission in their name, without permissions', function(done) { var submission = {data: {value: 'foo'}, owner: template.users.user1._id}; var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send(submission); request401(req, done, template.users.user1); }); it('An Authenticated User should not be able to create a submission in someones name, without permissions', function(done) { var submission = {data: {value: 'foo'}, owner: template.users.user2._id}; var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send(submission); request401(req, done, template.users.user1); }); it('An Authenticated User should not be able to update the owner of a submission, without permissions', function(done) { var req = request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + temp._id, template)) .send({owner: template.users.user1._id}); request401(req, done, template.users.user1); }); }); describe('Anonymous User', function() { it('An Anonymous User should not be able create a submission in their name, without permissions', function(done) { var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send({data: temp.data}); request401(req, done); }); it('An Anonymous User should not be able to create a submission in someones name, without permissions', function(done) { var req = request(app) .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) .send({data: {value: 'foo'}, owner: template.users.user2._id}); request401(req, done); }); it('An Anonymous User should not be able to update the owner of a submission, without permissions', function(done) { var req = request(app) .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + temp._id, template)) .send({data: temp.data, owner: template.users.admin._id}); request401(req, done); }); }); describe('Submission Normalization', function() { it('Delete the Submissions created for Ownership Checks', function(done) { tempSubmissions.forEach(function(submission) { request(app) .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + submission._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; }); }); tempSubmissions = []; done(); }); }); describe('Form Normalization', function() { it('Delete the Form created for Ownership Checks', function(done) { request(app) .delete(hook.alter('url', '/form/' + tempForm._id, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(function(err, res) { if (err) { return done(err); } var response = res.body; assert.deepEqual(response, {}); tempForm = response; // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; done(); }); }); }); }); }); describe('Submission Level Permissions (Authenticated User)', function() { describe('Submission CRUD - _own', function() { // Store the temp form for this test suite. var tempForm = { title: 'Authenticated access check', name: 'access', path: 'accessauthenticated', type: 'form', components: [ { type: 'textfield', validate: { custom: '', pattern: '', maxLength: '', minLength: '', required: true }, defaultValue: '', multiple: false, suffix: '', prefix: '', placeholder: 'value', key: 'value', label: 'value', inputMask: '', inputType: 'text', input: true } ] }; // Store the template submission for this test suite. var templateSubmission = {data: {value: 'foo'}}; // Store the user1 temp submission for this test suite. var tempSubmissionUser1 = {}; // Store the user2 temp submission for this test suite. v