diff --git a/dadi/lib/auth/index.js b/dadi/lib/auth/index.js index 23cc4283..0c5f8571 100644 --- a/dadi/lib/auth/index.js +++ b/dadi/lib/auth/index.js @@ -100,7 +100,7 @@ AuthMiddleware.prototype.generateToken = function (req, res, next) { return this.handleInvalidCredentials(req, res, next) } - let client = clientModel.sanitise(results[0]) + let client = clientModel.formatForOutput(results[0]) let payload = { clientId, accessType: client.accessType diff --git a/dadi/lib/controller/clients.js b/dadi/lib/controller/clients.js index 998e293c..26955928 100644 --- a/dadi/lib/controller/clients.js +++ b/dadi/lib/controller/clients.js @@ -148,7 +148,7 @@ Clients.prototype.get = function (req, res, next) { } let sanitisedClients = clients.results.map(client => { - return model.sanitise(client) + return model.formatForOutput(client) }) help.sendBackJSON(200, res, next)(null, { @@ -236,7 +236,7 @@ Clients.prototype.post = function (req, res, next) { }).then(({results}) => { help.sendBackJSON(201, res, next)(null, { results: [ - model.sanitise(results[0]) + model.formatForOutput(results[0]) ] }) }).catch(this.handleError(res, next)) @@ -270,23 +270,27 @@ Clients.prototype.postResource = function (req, res, next) { ) } - return acl.access.get(req.dadiApiClient, req.body.name).then(access => { - let forbiddenType = Object.keys(req.body.access).find(type => { - return access[type] !== true - }) - - if (forbiddenType) { - return Promise.reject( - acl.createError(req.dadiApiClient) - ) - } + // If the client does not have admin access, we need to ensure that + // they have each of the access types they are trying to assign. + if (!model.isAdmin(req.dadiApiClient)) { + return acl.access.get(req.dadiApiClient, req.body.name).then(access => { + let forbiddenType = Object.keys(req.body.access).find(type => { + return access[type] !== true + }) - return model.resourceAdd( - req.params.clientId, - req.body.name, - req.body.access - ) - }) + if (forbiddenType) { + return Promise.reject( + acl.createError(req.dadiApiClient) + ) + } + }) + } + }).then(() => { + return model.resourceAdd( + req.params.clientId, + req.body.name, + req.body.access + ) }).then(({results}) => { help.sendBackJSON(201, res, next)(null, {results}) }).catch(this.handleError(res, next)) @@ -361,23 +365,27 @@ Clients.prototype.putResource = function (req, res, next) { ) } - return acl.access.get(req.dadiApiClient, req.params.resource).then(access => { - let forbiddenType = Object.keys(req.body).find(type => { - return access[type] !== true - }) - - if (forbiddenType) { - return Promise.reject( - acl.createError(req.dadiApiClient) - ) - } + // If the client does not have admin access, we need to ensure that + // they have each of the access types they are trying to assign. + if (!model.isAdmin(req.dadiApiClient)) { + return acl.access.get(req.dadiApiClient, req.params.resource).then(access => { + let forbiddenType = Object.keys(req.body).find(type => { + return access[type] !== true + }) - return model.resourceUpdate( - req.params.clientId, - req.params.resource, - req.body - ) - }) + if (forbiddenType) { + return Promise.reject( + acl.createError(req.dadiApiClient) + ) + } + }) + } + }).then(() => { + return model.resourceUpdate( + req.params.clientId, + req.params.resource, + req.body + ) }).then(({results}) => { help.sendBackJSON(200, res, next)(null, {results}) }).catch(this.handleError(res, next)) diff --git a/dadi/lib/controller/roles.js b/dadi/lib/controller/roles.js index ae46b47b..9da89424 100644 --- a/dadi/lib/controller/roles.js +++ b/dadi/lib/controller/roles.js @@ -132,7 +132,7 @@ Roles.prototype.get = function (req, res, next) { help.sendBackJSON(200, res, next)(null, { results: roles.results.map(client => { - return acl.role.sanitise(client) + return acl.role.formatForOutput(client) }).sort((a, b) => { return a.name < b.name ? -1 @@ -214,7 +214,7 @@ Roles.prototype.post = function (req, res, next) { }).then(({results}) => { help.sendBackJSON(201, res, next)(null, { results: [ - acl.role.sanitise(results[0]) + acl.role.formatForOutput(results[0]) ] }) }).catch(this.handleError(res, next)) @@ -329,7 +329,7 @@ Roles.prototype.put = function (req, res, next) { }).then(({results}) => { help.sendBackJSON(200, res, next)(null, { results: [ - acl.role.sanitise(results[0]) + acl.role.formatForOutput(results[0]) ] }) }).catch(this.handleError(res, next)) diff --git a/dadi/lib/model/acl/access.js b/dadi/lib/model/acl/access.js index 95891127..7394d46d 100644 --- a/dadi/lib/model/acl/access.js +++ b/dadi/lib/model/acl/access.js @@ -1,7 +1,9 @@ -const ACCESS_TYPES = require('./matrix').ACCESS_TYPES +const ACLMatrix = require('./matrix') const clientModel = require('./client') const roleModel = require('./role') +const ACCESS_TYPES = ACLMatrix.ACCESS_TYPES + const Access = function () { clientModel.setWriteCallback( this.write.bind(this) @@ -152,30 +154,25 @@ Access.prototype.get = function ({clientId = null, accessType = null} = {}, reso return this.model.get({ query }).then(({results}) => { - if (!resource) { - let accessByResource = results.reduce((output, result) => { - output[result.resource] = resolveOwnTypes - ? this.resolveOwnTypes(result.access, clientId) - : result.access + if (results.length === 0) { + return {} + } - return output - }, {}) + let accessMap = new ACLMatrix() - return accessByResource - } + results.forEach(result => { + accessMap.set(result.resource, result.access) + }) - if ( - results.length > 0 && - typeof results[0].access === 'object' - ) { - if (resolveOwnTypes) { - return this.resolveOwnTypes(results[0].access, clientId) - } + if (resource) { + let matrix = accessMap.get(resource) - return results[0].access + return resolveOwnTypes + ? this.resolveOwnTypes(matrix, clientId) + : matrix } - return {} + return accessMap.getAll() }) } @@ -389,6 +386,7 @@ Access.prototype.write = function () { return this.getRoleChain(client.roles, roleCache).then(chain => { // Start with the resources assigned to the client directly. let clientResources = client.resources || {} + let clientResourcesMap = new ACLMatrix(clientResources) // Take the resources associated with each role and extend // the corresponding entry in `clientResources`. @@ -397,19 +395,31 @@ Access.prototype.write = function () { if (!role) return + let roleMap = new ACLMatrix(role) + Object.keys(role).forEach(resource => { - clientResources[resource] = this.combineAccessMatrices( - clientResources[resource], - role[resource] + let combinedMatrix = this.combineAccessMatrices( + clientResourcesMap.get(resource, { + parseObjects: true + }), + roleMap.get(resource, { + parseObjects: true + }) ) + + clientResourcesMap.set(resource, combinedMatrix) }) }) - Object.keys(clientResources).forEach(resource => { + let combinedResources = clientResourcesMap.getAll({ + stringifyObjects: true + }) + + Object.keys(combinedResources).forEach(resource => { entries.push({ client: client.clientId, resource, - access: clientResources[resource] + access: combinedResources[resource] }) }) }) diff --git a/dadi/lib/model/acl/client.js b/dadi/lib/model/acl/client.js index 95ee4d8a..c39fb7b8 100644 --- a/dadi/lib/model/acl/client.js +++ b/dadi/lib/model/acl/client.js @@ -98,6 +98,36 @@ Client.prototype.delete = function (clientId) { }) } +/** + * Sanitises a client, preparing it for output. It removes all + * internal properties as well as sensitive information, such as + * the client secret. + * + * @param {Object} client + * @return {Object} + */ +Client.prototype.formatForOutput = function (client) { + let sanitisedClient = Object.keys(this.schema).reduce((output, key) => { + if (!this.schema[key].hidden) { + let value = client[key] || this.schema[key].default + + if (key === 'resources') { + let resources = new ACLMatrix(value) + + value = resources.getAll({ + addFalsyTypes: true + }) + } + + output[key] = value + } + + return output + }, {}) + + return sanitisedClient +} + /** * Retrieves clients by ID. * @@ -179,7 +209,7 @@ Client.prototype.resourceAdd = function (clientId, resource, access) { rawOutput: true, update: { resources: resources.getAll({ - formatForInput: true + stringifyObjects: true }) }, validate: false @@ -188,7 +218,7 @@ Client.prototype.resourceAdd = function (clientId, resource, access) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -243,7 +273,7 @@ Client.prototype.resourceRemove = function (clientId, resource) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -293,7 +323,7 @@ Client.prototype.resourceUpdate = function (clientId, resource, access) { rawOutput: true, update: { resources: resources.getAll({ - formatForInput: true + stringifyObjects: true }) }, validate: false @@ -302,7 +332,7 @@ Client.prototype.resourceUpdate = function (clientId, resource, access) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -366,7 +396,7 @@ Client.prototype.roleAdd = function (clientId, roles) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -426,41 +456,11 @@ Client.prototype.roleRemove = function (clientId, roles) { }).then(({results}) => { return { removed: rolesRemoved, - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } -/** - * Sanitises a client, preparing it for output. It removes all - * internal properties as well as sensitive information, such as - * the client secret. - * - * @param {Object} client - * @return {Object} - */ -Client.prototype.sanitise = function (client) { - let sanitisedClient = Object.keys(this.schema).reduce((output, key) => { - if (!this.schema[key].hidden) { - let value = client[key] || this.schema[key].default - - if (key === 'resources') { - let resources = new ACLMatrix(value) - - value = resources.getAll({ - formatForOutput: true - }) - } - - output[key] = value - } - - return output - }, {}) - - return sanitisedClient -} - Client.prototype.setModel = function (model) { this.model = model } diff --git a/dadi/lib/model/acl/matrix.js b/dadi/lib/model/acl/matrix.js index cdb1f69e..9dba9127 100644 --- a/dadi/lib/model/acl/matrix.js +++ b/dadi/lib/model/acl/matrix.js @@ -11,24 +11,9 @@ const ACCESS_TYPES = [ ] const Matrix = function (map) { - this.map = map || {} -} - -/** - * Returns the resource map with access matrices formatted - * for insertion in the database. - * - * @param {Object} map - * @return {Object} - */ -Matrix.prototype._formatForInput = function (map) { - return Object.keys(map).reduce((output, resource) => { - output[resource] = this._formatMatrixForInput( - this.map[resource] - ) - - return output - }, {}) + this.map = this._convertACLObjects(map || {}, { + shouldStringify: false + }) } /** @@ -40,34 +25,12 @@ Matrix.prototype._formatForInput = function (map) { * @param {Object} map * @return {Object} */ -Matrix.prototype._formatForOutput = function (map) { +Matrix.prototype._addFalsyTypes = function (map) { return Object.keys(map).reduce((output, resource) => { let formattedResource = {} ACCESS_TYPES.forEach(type => { - let value = map[resource][type] - - if (typeof value === 'object') { - value = Object.keys(value).reduce((sanitised, key) => { - if (typeof value[key] === 'string') { - try { - let parsedValue = JSON.parse(value[key]) - - sanitised[key] = parsedValue - } catch (error) { - log.error({ - module: 'ACL matrix' - }, error) - } - } else { - sanitised[key] = value[key] - } - - return sanitised - }, {}) - } - - formattedResource[type] = value || false + formattedResource[type] = map[resource][type] || false }) output[resource] = formattedResource @@ -77,18 +40,64 @@ Matrix.prototype._formatForOutput = function (map) { } /** - * Returns the access matrix formatted for insertion in the database. + * Takes a map and converts any ACL objects (e.g. `fields` or `filter`) + * inside each matrix from their object form to a JSON string if + * `shouldStringify` is `true`, or vice-versa if it's `false`. * - * @param {Object} map + * @param {Object} map + * @param {Boolean} options.shouldStringify + * @return {Object} + */ +Matrix.prototype._convertACLObjects = function (map, { + shouldStringify +} = {}) { + return Object.keys(map).reduce((output, resource) => { + output[resource] = this._convertACLObjectsInMatrix( + map[resource], + {shouldStringify} + ) + + return output + }, {}) +} + +/** + * Takes a matrix and converts any ACL objects (e.g. `fields` or + * `filter`) from their object form to a JSON string if `shouldStringify` + * is `true`, or vice-versa if it's `false`. + * + * @param {Object} matrix + * @param {Boolean} options.shouldStringify * @return {Object} */ -Matrix.prototype._formatMatrixForInput = function (matrix) { +Matrix.prototype._convertACLObjectsInMatrix = function (matrix, { + shouldStringify +} = {}) { + let fromType = shouldStringify ? 'object' : 'string' + let transformFn = shouldStringify ? JSON.stringify : JSON.parse + return Object.keys(matrix).reduce((sanitised, accessType) => { if (typeof matrix[accessType] === 'object') { sanitised[accessType] = Object.keys(matrix[accessType]).reduce((value, key) => { - value[key] = typeof matrix[accessType][key] === 'object' - ? JSON.stringify(matrix[accessType][key]) - : matrix[accessType][key] + if (value === false) { + return value + } + + if (typeof matrix[accessType][key] === fromType) { + try { + let transformedValue = transformFn(matrix[accessType][key]) + + value[key] = transformedValue + } catch (error) { + log.error({ + module: 'ACL matrix' + }, error) + + value = false + } + } else { + value[key] = matrix[accessType][key] + } return value }, {}) @@ -103,22 +112,22 @@ Matrix.prototype._formatMatrixForInput = function (matrix) { /** * Returns the access matrix for a particular resource. * - * @param {String} name The name of the resource to retrieve - * @param {Boolean} options.formatForInput Whether to format the result for input - * @param {Boolean} options.formatForOutput} Whether to format the result for input + * @param {String} name The name of the resource to retrieve + * @param {Boolean} options.addFalsyTypes Add `false` to missing access types + * @param {Boolean} options.parseObjects Get parsed version of ACL objects + * @param {Boolean} options.stringifyObjects Get stringified version of ACL objects * @return {Object} */ Matrix.prototype.get = function (name, { - formatForInput, - formatForOutput + addFalsyTypes, + parseObjects, + stringifyObjects } = {}) { - let map = this.map - - if (formatForInput) { - map = this._formatForInput(map) - } else if (formatForOutput) { - map = this._formatForOutput(map) - } + let map = this.getAll({ + addFalsyTypes, + parseObjects, + stringifyObjects + }) return map[name] } @@ -126,23 +135,33 @@ Matrix.prototype.get = function (name, { /** * Returns the entire resource map. * - * @param {Boolean} options.formatForInput Whether to format the result for input - * @param {Boolean} options.formatForOutput} Whether to format the result for input + * @param {Boolean} options.addFalsyTypes Add `false` to missing access types + * @param {Boolean} options.parseObjects Get parsed version of ACL objects + * @param {Boolean} options.stringifyObjects Get stringified version of ACL objects * @return {Object} */ Matrix.prototype.getAll = function ({ - formatForInput, - formatForOutput + addFalsyTypes, + parseObjects, + stringifyObjects } = {}) { - if (formatForInput) { - return this._formatForInput(this.map) + let map = this.map + + if (addFalsyTypes) { + map = this._addFalsyTypes(map) } - if (formatForOutput) { - return this._formatForOutput(this.map) + if (parseObjects) { + map = this._convertACLObjects(map, { + shouldStringify: false + }) + } else if (stringifyObjects) { + map = this._convertACLObjects(map, { + shouldStringify: true + }) } - return this.map + return map } /** @@ -161,9 +180,16 @@ Matrix.prototype.remove = function (name) { * @param {Object} matrix */ Matrix.prototype.set = function (name, matrix) { - this.validate(matrix) - - this.map[name] = Object.assign({}, this.map[name], matrix) + let sanitisedMatrix = this._convertACLObjectsInMatrix( + matrix, + {shouldStringify: false} + ) + + this.map[name] = Object.assign( + {}, + this.map[name], + sanitisedMatrix + ) } /** diff --git a/dadi/lib/model/acl/role.js b/dadi/lib/model/acl/role.js index e49ff9ae..488ad71b 100644 --- a/dadi/lib/model/acl/role.js +++ b/dadi/lib/model/acl/role.js @@ -94,6 +94,32 @@ Role.prototype.delete = function (name) { }) } +/** + * Sanitises a role, preparing it for output. + * + * @param {Object} role + * @return {Object} + */ +Role.prototype.formatForOutput = function (role) { + let sanitisedRole = Object.keys(this.schema).reduce((output, key) => { + if (!this.schema[key].hidden) { + output[key] = role[key] || this.schema[key].default + + if (key === 'resources') { + let resources = new ACLMatrix(output[key]) + + output[key] = resources.getAll({ + addFalsyTypes: true + }) + } + } + + return output + }, {}) + + return sanitisedRole +} + /** * Retrieves the roles that match `name` if it is * supplied; otherwise, all roles are returned. @@ -154,6 +180,7 @@ Role.prototype.resourceAdd = function (role, resource, access) { ) } + resources.validate(access) resources.set(resource, access) return this.model.update({ @@ -163,7 +190,7 @@ Role.prototype.resourceAdd = function (role, resource, access) { rawOutput: true, update: { resources: resources.getAll({ - formatForInput: true + stringifyObjects: true }) }, validate: false @@ -172,7 +199,7 @@ Role.prototype.resourceAdd = function (role, resource, access) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -227,7 +254,7 @@ Role.prototype.resourceRemove = function (role, resource) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } @@ -267,6 +294,7 @@ Role.prototype.resourceUpdate = function (role, resource, access) { ) } + resources.validate(access) resources.set(resource, access) return this.model.update({ @@ -276,7 +304,7 @@ Role.prototype.resourceUpdate = function (role, resource, access) { rawOutput: true, update: { resources: resources.getAll({ - formatForInput: true + stringifyObjects: true }) }, validate: false @@ -285,37 +313,11 @@ Role.prototype.resourceUpdate = function (role, resource, access) { return this.broadcastWrite(result) }).then(({results}) => { return { - results: results.map(client => this.sanitise(client)) + results: results.map(client => this.formatForOutput(client)) } }) } -/** - * Sanitises a role, preparing it for output. - * - * @param {Object} role - * @return {Object} - */ -Role.prototype.sanitise = function (role) { - let sanitisedRole = Object.keys(this.schema).reduce((output, key) => { - if (!this.schema[key].hidden) { - output[key] = role[key] || this.schema[key].default - - if (key === 'resources') { - let resources = new ACLMatrix(output[key]) - - output[key] = resources.getAll({ - formatForOutput: true - }) - } - } - - return output - }, {}) - - return sanitisedRole -} - Role.prototype.setModel = function (model) { this.model = model } diff --git a/test/acceptance/acl/clients-api/resources-post.js b/test/acceptance/acl/clients-api/resources-post.js index fb9eff07..f4dbf8a2 100644 --- a/test/acceptance/acl/clients-api/resources-post.js +++ b/test/acceptance/acl/clients-api/resources-post.js @@ -299,15 +299,7 @@ module.exports = () => { let testClient = { clientId: 'apiClient', secret: 'someSecret', - resources: { - clients: { - update: true - }, - 'collection:library_book': { - invalidType: true, - read: true - } - } + accessType: 'admin' } let resource = { name: 'collection:library_book', diff --git a/test/acceptance/acl/clients-api/resources-put.js b/test/acceptance/acl/clients-api/resources-put.js index 15cb69a2..ad59a8fc 100644 --- a/test/acceptance/acl/clients-api/resources-put.js +++ b/test/acceptance/acl/clients-api/resources-put.js @@ -183,15 +183,7 @@ module.exports = () => { let testClient = { clientId: 'apiClient', secret: 'someSecret', - resources: { - clients: { - update: true - }, - [resource]: { - invalidType: true, - read: true - } - } + accessType: 'admin' } help.createACLClient(testClient).then(() => { diff --git a/test/acceptance/acl/collections-api.js b/test/acceptance/acl/collections-api.js index cb6b35ee..2fcde1a0 100644 --- a/test/acceptance/acl/collections-api.js +++ b/test/acceptance/acl/collections-api.js @@ -12,8 +12,7 @@ const PERMISSIONS = { UPDATE: { create: false, read: false, update: true, delete: false }, DELETE: { create: false, read: false, update: false, delete: true }, READ_EXCLUDE_FIELDS: { read: { fields: { title: 0 } } }, - NO_FILTER: { filter: { } }, - FILTER: { read: { filter: { title: 'very long title' } } } + FILTER: { read: { filter: '{"title":"very long title"}' } } } let client = request(`http://${config.get('server.host')}:${config.get('server.port')}`) @@ -365,43 +364,6 @@ describe('Collections API', () => { }) }) }) - }) - - it('should return 403 with an empty filter permission', function (done) { - let testClient = { - clientId: 'apiClient', - secret: 'someSecret', - resources: { 'collection:testdb_test-schema': PERMISSIONS.NO_FILTER } - } - - let params = { - filter: JSON.stringify({ title: { '$ne': 'xyz' } }) - } - - help.createACLClient(testClient).then(() => { - client - .post(config.get('auth.tokenUrl')) - .set('content-type', 'application/json') - .send(testClient) - .expect(200) - .end((err, res) => { - if (err) return done(err) - - let bearerToken = res.body.accessToken - let query = require('querystring').stringify(params) - - client - .get(`/vtest/testdb/test-schema/?${query}`) - .set('content-type', 'application/json') - .set('Authorization', `Bearer ${bearerToken}`) - .end((err, res) => { - if (err) return done(err) - res.statusCode.should.eql(403) - - done() - }) - }) - }) }) it('should return 200 with a filter permission', function (done) { @@ -728,9 +690,9 @@ describe('Collections API', () => { resources: { 'collection:testdb_test-schema': { read: { - filter: { + filter: JSON.stringify({ field1: 'Value one' - } + }) } } } @@ -795,9 +757,9 @@ describe('Collections API', () => { resources: { 'collection:testdb_test-schema': { read: { - filter: { + filter: JSON.stringify({ field1: 'Value one' - } + }) } } } @@ -1330,9 +1292,9 @@ describe('Collections API', () => { 'collection:testdb_test-schema': { read: true, update: { - filter: { + filter: JSON.stringify({ title: 'some title' - } + }) } } } @@ -1645,9 +1607,9 @@ describe('Collections API', () => { 'collection:testdb_test-schema': { read: true, delete: { - filter: { + filter: JSON.stringify({ title: 'some title' - } + }) } } } diff --git a/test/acceptance/acl/index.js b/test/acceptance/acl/index.js index 0b37ef55..07e52a0e 100644 --- a/test/acceptance/acl/index.js +++ b/test/acceptance/acl/index.js @@ -214,9 +214,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - filter: { + filter: JSON.stringify({ fieldOne: 'valueOne' - } + }) } } } @@ -248,10 +248,10 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1, fieldTwo: 1 - } + }) } } } @@ -352,10 +352,10 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1, fieldTwo: 2 - } + }) } } } @@ -366,9 +366,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldThree: 1 - } + }) } } } @@ -381,10 +381,10 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldThree: 0, fieldFour: 0 - } + }) } } } @@ -410,9 +410,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 0 - } + }) } } } @@ -423,9 +423,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1 - } + }) } } } @@ -438,10 +438,10 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldThree: 1, fieldFour: 1 - } + }) } } } @@ -463,9 +463,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1 - } + }) } } } @@ -476,9 +476,9 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldTwo: 1 - } + }) } } } @@ -491,10 +491,10 @@ describe('ACL', () => { resources: { 'some:resource': { read: { - fields: { + fields: JSON.stringify({ fieldThree: 1, fieldFour: 1 - } + }) } } } @@ -529,16 +529,16 @@ describe('ACL', () => { }, 'collection:three': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1 - } + }) } }, 'collection:four': { read: { - filter: { + filter: JSON.stringify({ fieldOne: 'value' - } + }) } } } @@ -555,10 +555,10 @@ describe('ACL', () => { }, 'collection:three': { read: { - fields: { + fields: JSON.stringify({ fieldTwo: 1, fieldThree: 1 - } + }) } }, 'collection:four': { diff --git a/test/acceptance/collections-endpoint.js b/test/acceptance/collections-endpoint.js index b2e64a77..329c3108 100644 --- a/test/acceptance/collections-endpoint.js +++ b/test/acceptance/collections-endpoint.js @@ -78,9 +78,9 @@ describe('Collections endpoint', function () { }, 'collection:radio_articles': { read: { - fields: { + fields: JSON.stringify({ fieldOne: 1 - } + }) } } } diff --git a/test/test-connector/index.js b/test/test-connector/index.js index 44ec0a87..89c4a02e 100644 --- a/test/test-connector/index.js +++ b/test/test-connector/index.js @@ -255,11 +255,6 @@ DataStore.prototype.find = function ({ query, collection, options = {}, schema, return new Promise((resolve, reject) => { const collName = collection this.getCollection(collection).then(collection => { - this._debug('find', { - collection: collName, - query - }) - let results const sort = this.getSortParameters(options) @@ -288,6 +283,12 @@ DataStore.prototype.find = function ({ query, collection, options = {}, schema, .value() } + this._debug('find', { + collection: collName, + query, + results + }) + let returnData = {} returnData.results = results.map(this.formatDocumentForOutput.bind(this)) returnData.metadata = this.getMetadata(options, count) diff --git a/test/unit/acl/matrix.js b/test/unit/acl/matrix.js index 3fa5815e..6558fc01 100644 --- a/test/unit/acl/matrix.js +++ b/test/unit/acl/matrix.js @@ -62,7 +62,27 @@ describe('ACL access matrix', function () { ) }) - it('should return the access matrix for a given resource and format it for input', () => { + it('should return the access matrix for a given resource and add missing access types', () => { + let map = { + 'collection:db_one': { + create: true, + readOwn: true + }, + 'collection:db_two': { + create: false, + updateOwn: true + } + } + let matrix = new Matrix(map) + + matrix.get('collection:db_one', { + addFalsyTypes: true + }).should.eql( + Object.assign({}, EMPTY_MATRIX, map['collection:db_one']) + ) + }) + + it('should return the access matrix for a given resource and stringify ACL objects', () => { let map = { 'collection:db_one': { create: true, @@ -79,7 +99,7 @@ describe('ACL access matrix', function () { let matrix = new Matrix(map) matrix.get('collection:db_one', { - formatForInput: true + stringifyObjects: true }).should.eql( Object.assign( {}, @@ -93,7 +113,7 @@ describe('ACL access matrix', function () { ) }) - it('should return the access matrix for a given resource and format it for output', () => { + it('should return the access matrix for a given resource and parse ACL objects', () => { let map = { 'collection:db_one': { create: true, @@ -110,11 +130,10 @@ describe('ACL access matrix', function () { let matrix = new Matrix(map) matrix.get('collection:db_one', { - formatForOutput: true + parseObjects: true }).should.eql( Object.assign( {}, - EMPTY_MATRIX, map['collection:db_one'], { delete: { @@ -138,11 +157,10 @@ describe('ACL access matrix', function () { let matrix = new Matrix(map) matrix.get('collection:db_one', { - formatForOutput: true + parseObjects: true }).should.eql( Object.assign( {}, - EMPTY_MATRIX, map['collection:db_one'], { delete: false @@ -169,7 +187,36 @@ describe('ACL access matrix', function () { matrix.getAll().should.eql(map) }) - it('should return the access matrix for all resources and format them for output', () => { + it('should return the access matrix for all resources and add missing access types', () => { + let map = { + 'collection:db_one': { + create: true, + readOwn: true + }, + 'collection:db_two': { + create: false, + updateOwn: true + } + } + let matrix = new Matrix(map) + + matrix.getAll({ + addFalsyTypes: true + }).should.eql({ + 'collection:db_one': Object.assign( + {}, + EMPTY_MATRIX, + map['collection:db_one'] + ), + 'collection:db_two': Object.assign( + {}, + EMPTY_MATRIX, + map['collection:db_two'] + ) + }) + }) + + it('should return the access matrix for all resources and stringify ACL objects', () => { let map = { 'collection:db_one': { create: true, @@ -197,7 +244,7 @@ describe('ACL access matrix', function () { let matrix = new Matrix(map) matrix.getAll({ - formatForInput: true + stringifyObjects: true }).should.eql({ 'collection:db_one': Object.assign( {}, @@ -215,12 +262,12 @@ describe('ACL access matrix', function () { delete: { filter: JSON.stringify(map['collection:db_two'].delete.filter) } - } + } ) }) }) - it('should return the access matrix for all resources and format them for output', () => { + it('should return the access matrix for all resources and parse ACL objects', () => { let map = { 'collection:db_one': { create: true, @@ -240,11 +287,10 @@ describe('ACL access matrix', function () { let matrix = new Matrix(map) matrix.getAll({ - formatForOutput: true + parseObjects: true }).should.eql({ 'collection:db_one': Object.assign( {}, - EMPTY_MATRIX, map['collection:db_one'], { delete: { @@ -254,7 +300,6 @@ describe('ACL access matrix', function () { ), 'collection:db_two': Object.assign( {}, - EMPTY_MATRIX, map['collection:db_two'], { delete: {