diff --git a/lib/application.js b/lib/application.js index f421f706..eb01d398 100644 --- a/lib/application.js +++ b/lib/application.js @@ -141,7 +141,7 @@ app.model = function (Model, config) { configureModel(Model, config, this); isPublic = config.public !== false; } else { - assert(Model.prototype instanceof loopback.Model, + assert(Model.prototype instanceof registry.Model, 'Model must be a descendant of loopback.Model'); } @@ -320,6 +320,7 @@ app.dataSources = app.datasources = {}; app.enableAuth = function() { var remotes = this.remotes(); + var app = this; remotes.before('**', function(ctx, next, method) { var req = ctx.req; @@ -327,6 +328,12 @@ app.enableAuth = function() { var modelInstance = ctx.instance; var modelId = modelInstance && modelInstance.id || req.param('id'); + var modelSettings = Model.settings || {}; + var errStatusCode = modelSettings.aclErrorStatus || app.get('aclErrorStatus') || 401; + if(!req.accessToken){ + errStatusCode = 401; + } + if(Model.checkAccess) { // Pause the request before checking access // See https://github.com/strongloop/loopback-storage-service/issues/7 @@ -344,8 +351,15 @@ app.enableAuth = function() { } else if(allowed) { next(); } else { - var e = new Error('Access Denied'); - e.statusCode = 401; + + var messages = { + 403:'Access Denied', + 404: ('could not find a model with id ' + modelId), + 401:'Authorization Required' + }; + + var e = new Error(messages[errStatusCode] || messages[403]); + e.statusCode = errStatusCode; next(e); } } diff --git a/test/access-token.test.js b/test/access-token.test.js index a9e47aab..75b8874b 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -81,13 +81,38 @@ describe('app.enableAuth()', function() { beforeEach(createTestingToken); - it('should prevent remote method calls if the accessToken doesnt have access', function (done) { + it('prevents remote call with 401 status on denied ACL', function (done) { createTestAppAndRequest(this.token, done) .del('/tests/123') .expect(401) .set('authorization', this.token.id) .end(done); }); + + it('prevent remote call with app setting status on denied ACL', function (done) { + createTestAppAndRequest(this.token, {app:{aclErrorStatus:403}}, done) + .del('/tests/123') + .expect(403) + .set('authorization', this.token.id) + .end(done); + }); + + it('prevent remote call with app setting status on denied ACL', function (done) { + createTestAppAndRequest(this.token, {model:{aclErrorStatus:404}}, done) + .del('/tests/123') + .expect(404) + .set('authorization', this.token.id) + .end(done); + }); + + it('prevent remote call if the accessToken is missing and required', function (done) { + createTestAppAndRequest(null, done) + .del('/tests/123') + .expect(401) + .set('authorization', null) + .end(done); + }); + }); function createTestingToken(done) { @@ -99,12 +124,19 @@ function createTestingToken(done) { }); } -function createTestAppAndRequest(testToken, done) { - var app = createTestApp(testToken, done); +function createTestAppAndRequest(testToken, settings, done) { + var app = createTestApp(testToken, settings, done); return request(app); } -function createTestApp(testToken, done) { +function createTestApp(testToken, settings, done) { + done = arguments[arguments.length-1]; + if(settings == done) settings = {}; + settings = settings || {}; + + var appSettings = settings.app || {}; + var modelSettings = settings.model || {}; + var app = loopback(); app.use(loopback.cookieParser('secret')); @@ -125,7 +157,11 @@ function createTestApp(testToken, done) { app.use(loopback.rest()); app.enableAuth(); - var TestModel = loopback.PersistedModel.extend('test', {}, { + Object.keys(appSettings).forEach(function(key){ + app.set(key, appSettings[key]); + }); + + var modelOptions = { acls: [ { principalType: "ROLE", @@ -135,8 +171,14 @@ function createTestApp(testToken, done) { property: 'removeById' } ] + }; + + Object.keys(modelSettings).forEach(function(key){ + modelOptions[key] = modelSettings[key]; }); + var TestModel = loopback.PersistedModel.extend('test', {}, modelOptions); + TestModel.attachTo(loopback.memory()); app.model(TestModel);