diff --git a/lib/model.js b/lib/model.js index 3ea73d9b..dd76b437 100644 --- a/lib/model.js +++ b/lib/model.js @@ -314,6 +314,25 @@ Model._getAccessTypeForMethod = function(method) { var ACL = Model._ACL(); + // Check the explicit setting of accessType + if (method.accessType) { + assert(method.accessType === ACL.READ || + method.accessType === ACL.WRITE || + method.accessType === ACL.EXECUTE, 'invalid accessType ' + + method.accessType + + '. It must be "READ", "WRITE", or "EXECUTE"'); + return method.accessType; + } + + // Default GET requests to READ + var verb = method.http && method.http.verb; + if (typeof verb === 'string') { + verb = verb.toUpperCase(); + } + if (verb === 'GET' || verb === 'HEAD') { + return ACL.READ; + } + switch (method.name) { case'create': return ACL.WRITE; @@ -406,6 +425,7 @@ Model.belongsToRemoting = function(relationName, relation, define) { isStatic: false, http: {verb: 'get', path: '/' + pathName}, accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}}, + accessType: 'READ', description: 'Fetches belongsTo relation ' + relationName, returns: {arg: relationName, type: modelName, root: true} }, fn); @@ -419,6 +439,7 @@ Model.hasOneRemoting = function(relationName, relation, define) { http: {verb: 'get', path: '/' + pathName}, accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}}, description: 'Fetches hasOne relation ' + relationName, + accessType: 'READ', returns: {arg: relationName, type: relation.modelTo.modelName, root: true} }, fn); }; @@ -445,6 +466,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, description: 'Find a related item by id for ' + relationName, + accessType: 'READ', returns: {arg: 'result', type: toModelName, root: true}, rest: {after: convertNullToNotFoundError} }, findByIdFunc); @@ -457,6 +479,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, description: 'Delete a related item by id for ' + relationName, + accessType: 'WRITE', returns: [] }, destroyByIdFunc); @@ -471,6 +494,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { {arg: 'data', type: toModelName, http: {source: 'body'}} ], description: 'Update a related item by id for ' + relationName, + accessType: 'WRITE', returns: {arg: 'result', type: toModelName, root: true} }, updateByIdFunc); @@ -491,6 +515,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}].concat(accepts), description: 'Add a related item by id for ' + relationName, + accessType: 'WRITE', returns: {arg: relationName, type: modelThrough.modelName, root: true} }, addFunc); @@ -502,6 +527,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, description: 'Remove the ' + relationName + ' relation to an item by id', + accessType: 'WRITE', returns: [] }, removeFunc); @@ -515,6 +541,7 @@ Model.hasManyRemoting = function(relationName, relation, define) { description: 'Foreign key for ' + relationName, required: true, http: {source: 'path'}}, description: 'Check the existence of ' + relationName + ' relation to an item by id', + accessType: 'READ', returns: {arg: 'exists', type: 'boolean', root: true}, rest: { // After hook to map exists to 200/404 for HEAD @@ -556,6 +583,7 @@ Model.scopeRemoting = function(scopeName, scope, define) { http: {verb: 'get', path: '/' + pathName}, accepts: {arg: 'filter', type: 'object'}, description: 'Queries ' + scopeName + ' of ' + this.modelName + '.', + accessType: 'READ', returns: {arg: scopeName, type: [toModelName], root: true} }); @@ -564,13 +592,15 @@ Model.scopeRemoting = function(scopeName, scope, define) { http: {verb: 'post', path: '/' + pathName}, accepts: {arg: 'data', type: toModelName, http: {source: 'body'}}, description: 'Creates a new instance in ' + scopeName + ' of this model.', + accessType: 'WRITE', returns: {arg: 'data', type: toModelName, root: true} }); define('__delete__' + scopeName, { isStatic: isStatic, http: {verb: 'delete', path: '/' + pathName}, - description: 'Deletes all ' + scopeName + ' of this model.' + description: 'Deletes all ' + scopeName + ' of this model.', + accessType: 'WRITE' }); define('__count__' + scopeName, { @@ -578,6 +608,7 @@ Model.scopeRemoting = function(scopeName, scope, define) { http: {verb: 'get', path: '/' + pathName + '/count'}, accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'}, description: 'Counts ' + scopeName + ' of ' + this.modelName + '.', + accessType: 'READ', returns: {arg: 'count', type: 'number'} }); @@ -653,6 +684,7 @@ Model.nestRemoting = function(relationName, options, cb) { opts.accepts = acceptArgs.concat(method.accepts || []); opts.returns = [].concat(method.returns || []); opts.description = method.description; + opts.accessType = method.accessType; opts.rest = extend({}, method.rest || {}); opts.rest.delegateTo = method; diff --git a/lib/persisted-model.js b/lib/persisted-model.js index 9bba528d..c6c60374 100644 --- a/lib/persisted-model.js +++ b/lib/persisted-model.js @@ -485,6 +485,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'create', { description: 'Create a new instance of the model and persist it into the data source', + accessType: 'WRITE', accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, returns: {arg: 'data', type: typeName, root: true}, http: {verb: 'post', path: '/'} @@ -493,6 +494,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'upsert', { aliases: ['updateOrCreate'], description: 'Update an existing model instance or insert a new one into the data source', + accessType: 'WRITE', accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, returns: {arg: 'data', type: typeName, root: true}, http: {verb: 'put', path: '/'} @@ -500,6 +502,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'exists', { description: 'Check whether a model instance exists in the data source', + accessType: 'READ', accepts: {arg: 'id', type: 'any', description: 'Model id', required: true}, returns: {arg: 'exists', type: 'boolean'}, http: [ @@ -529,6 +532,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'findById', { description: 'Find a model instance by id from the data source', + accessType: 'READ', accepts: { arg: 'id', type: 'any', description: 'Model id', required: true, http: {source: 'path'} @@ -540,6 +544,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'find', { description: 'Find all instances of the model matched by filter from the data source', + accessType: 'READ', accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, orderBy, offset, and limit'}, returns: {arg: 'data', type: [typeName], root: true}, http: {verb: 'get', path: '/'} @@ -547,6 +552,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'findOne', { description: 'Find first instance of the model matched by filter from the data source', + accessType: 'READ', accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, orderBy, offset, and limit'}, returns: {arg: 'data', type: typeName, root: true}, http: {verb: 'get', path: '/findOne'}, @@ -555,6 +561,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'destroyAll', { description: 'Delete all matching records', + accessType: 'WRITE', accepts: {arg: 'where', type: 'object', description: 'filter.where object'}, http: {verb: 'del', path: '/'}, shared: false @@ -563,6 +570,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'updateAll', { aliases: ['update'], description: 'Update instances of the model matched by where from the data source', + accessType: 'WRITE', accepts: [ {arg: 'where', type: 'object', http: {source: 'query'}, description: 'Criteria to match model instances'}, @@ -575,6 +583,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'deleteById', { aliases: ['destroyById', 'removeById'], description: 'Delete a model instance by id from the data source', + accessType: 'WRITE', accepts: {arg: 'id', type: 'any', description: 'Model id', required: true, http: {source: 'path'}}, http: {verb: 'del', path: '/:id'} @@ -582,6 +591,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'count', { description: 'Count instances of the model matched by where from the data source', + accessType: 'READ', accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'}, returns: {arg: 'count', type: 'number'}, http: {verb: 'get', path: '/count'} @@ -589,6 +599,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel.prototype, 'updateAttributes', { description: 'Update attributes for a model instance and persist it into the data source', + accessType: 'WRITE', accepts: {arg: 'data', type: 'object', http: {source: 'body'}, description: 'An object of model property name/value pairs'}, returns: {arg: 'data', type: typeName, root: true}, http: {verb: 'put', path: '/'} @@ -597,6 +608,7 @@ PersistedModel.setupRemoting = function() { if (options.trackChanges) { setRemoting(PersistedModel, 'diff', { description: 'Get a set of deltas and conflicts since the given checkpoint', + accessType: 'READ', accepts: [ {arg: 'since', type: 'number', description: 'Find deltas since this checkpoint'}, {arg: 'remoteChanges', type: 'array', description: 'an array of change objects', @@ -609,6 +621,7 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'changes', { description: 'Get the changes to a model since a given checkpoint.' + 'Provide a filter object to reduce the number of results returned.', + accessType: 'READ', accepts: [ {arg: 'since', type: 'number', description: 'Only return changes since this checkpoint'}, {arg: 'filter', type: 'object', description: 'Only include changes that match this filter'} @@ -619,18 +632,21 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'checkpoint', { description: 'Create a checkpoint.', + accessType: 'WRITE', returns: {arg: 'checkpoint', type: 'object', root: true}, http: {verb: 'post', path: '/checkpoint'} }); setRemoting(PersistedModel, 'currentCheckpoint', { description: 'Get the current checkpoint.', + accessType: 'READ', returns: {arg: 'checkpoint', type: 'object', root: true}, http: {verb: 'get', path: '/checkpoint'} }); setRemoting(PersistedModel, 'createUpdates', { description: 'Create an update list from a delta list', + accessType: 'WRITE', accepts: {arg: 'deltas', type: 'array', http: {source: 'body'}}, returns: {arg: 'updates', type: 'array', root: true}, http: {verb: 'post', path: '/create-updates'} @@ -638,17 +654,20 @@ PersistedModel.setupRemoting = function() { setRemoting(PersistedModel, 'bulkUpdate', { description: 'Run multiple updates at once. Note: this is not atomic.', + accessType: 'WRITE', accepts: {arg: 'updates', type: 'array'}, http: {verb: 'post', path: '/bulk-update'} }); setRemoting(PersistedModel, 'rectifyAllChanges', { description: 'Rectify all Model changes.', + accessType: 'WRITE', http: {verb: 'post', path: '/rectify-all'} }); setRemoting(PersistedModel, 'rectifyChange', { description: 'Tell loopback that a change to the model with the given id has occurred.', + accessType: 'WRITE', accepts: {arg: 'id', type: 'any', http: {source: 'path'}}, http: {verb: 'post', path: '/:id/rectify-change'} });