Initial auth implementation
This commit is contained in:
parent
3eb04f64c3
commit
2f9403016c
|
@ -43,12 +43,6 @@ app.disuse = function (route) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* App models.
|
||||
*/
|
||||
|
||||
app._models = [];
|
||||
|
||||
/**
|
||||
* Expose a model.
|
||||
*
|
||||
|
@ -60,7 +54,7 @@ app.model = function (Model, config) {
|
|||
assert(typeof Model === 'function', 'app.model(Model) => Model must be a function / constructor');
|
||||
assert(Model.pluralModelName, 'Model must have a "pluralModelName" property');
|
||||
this.remotes().exports[Model.pluralModelName] = Model;
|
||||
this._models.push(Model);
|
||||
this.models().push(Model);
|
||||
Model.shared = true;
|
||||
Model.app = this;
|
||||
Model.emit('attached', this);
|
||||
|
@ -82,20 +76,12 @@ app.model = function (Model, config) {
|
|||
return Model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Model by name.
|
||||
*/
|
||||
|
||||
app.getModel = function (modelName) {
|
||||
this.models
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all exposed models.
|
||||
*/
|
||||
|
||||
app.models = function () {
|
||||
return this._models;
|
||||
return this._models || (this._models = []);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +130,6 @@ app.docs = function (options) {
|
|||
swagger(remotes, options);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Get a handler of the specified type from the handler cache.
|
||||
*/
|
||||
|
@ -166,6 +151,52 @@ app.handler = function (type) {
|
|||
|
||||
app.dataSources = app.datasources = {};
|
||||
|
||||
/**
|
||||
* Enable app wide authentication.
|
||||
*/
|
||||
|
||||
app.enableAuth = function() {
|
||||
var remotes = this.remotes();
|
||||
|
||||
remotes.before('**', function(ctx, next, method) {
|
||||
var req = ctx.req;
|
||||
var Model = method.ctor;
|
||||
var modelInstance = ctx.instance;
|
||||
var modelId = modelInstance && modelInstance.id;
|
||||
|
||||
// TODO(ritch) - this fallback could be less express dependent
|
||||
if(modelInstance && !modelId) {
|
||||
modelId = req.param('id');
|
||||
}
|
||||
|
||||
if(req.accessToken) {
|
||||
Model.checkAccess(
|
||||
req.accessToken,
|
||||
modelId,
|
||||
method.name,
|
||||
function(err, allowed) {
|
||||
if(err) {
|
||||
next(err);
|
||||
} else if(allowed) {
|
||||
next();
|
||||
} else {
|
||||
var e = new Error('Access Denied');
|
||||
e.statusCode = 401;
|
||||
next(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if(method.fn && method.fn.requireToken === false) {
|
||||
next();
|
||||
} else {
|
||||
var e = new Error('Access Denied');
|
||||
e.statusCode = 401;
|
||||
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the app using JSON and JavaScript files.
|
||||
*
|
||||
|
|
|
@ -252,7 +252,6 @@ loopback.RoleMapping = require('./models/role').RoleMapping;
|
|||
loopback.ACL = require('./models/acl').ACL;
|
||||
loopback.Scope = require('./models/acl').Scope;
|
||||
|
||||
|
||||
/**
|
||||
* Automatically attach these models to dataSources
|
||||
*/
|
||||
|
|
|
@ -8,7 +8,8 @@ var Model = require('../loopback').Model
|
|||
, crypto = require('crypto')
|
||||
, uid = require('uid2')
|
||||
, DEFAULT_TTL = 1209600 // 2 weeks in seconds
|
||||
, DEFAULT_TOKEN_LEN = 64;
|
||||
, DEFAULT_TOKEN_LEN = 64
|
||||
, ACL = require('./acl').ACL;
|
||||
|
||||
/**
|
||||
* Default AccessToken properties.
|
||||
|
|
|
@ -274,6 +274,10 @@ Scope.checkPermission = function (scope, model, property, accessType, callback)
|
|||
ACL.checkAccess = function (context, callback) {
|
||||
context = context || {};
|
||||
var principals = context.principals || [];
|
||||
|
||||
// add ROLE.EVERYONE
|
||||
principals.unshift({principalType: ACL.ROLE, principalId: Role.EVERYONE});
|
||||
|
||||
var model = context.model;
|
||||
model = ('string' === typeof model) ? loopback.getModel(model) : model;
|
||||
var id = context.id;
|
||||
|
@ -369,7 +373,7 @@ ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
|
|||
callback && callback(err);
|
||||
return;
|
||||
}
|
||||
callback && callback(access.permission !== ACL.DENY);
|
||||
callback && callback(null, access.permission !== ACL.DENY);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var loopback = require('../');
|
||||
var Token = loopback.AccessToken.extend('MyToken');
|
||||
var ACL = loopback.ACL;
|
||||
|
||||
describe('loopback.token(options)', function() {
|
||||
beforeEach(createTestingToken);
|
||||
|
@ -54,6 +55,27 @@ describe('AccessToken', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('app.enableAuth()', function() {
|
||||
this.timeout(0);
|
||||
|
||||
beforeEach(createTestingToken);
|
||||
|
||||
it('should prevent all remote method calls without an accessToken', function (done) {
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.get('/tests')
|
||||
.expect(401)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
it('should prevent remote method calls if the accessToken doesnt have access', function (done) {
|
||||
createTestAppAndRequest(this.token, done)
|
||||
.del('/tests/123')
|
||||
.expect(401)
|
||||
.set('authorization', this.token.id)
|
||||
.end(done);
|
||||
});
|
||||
});
|
||||
|
||||
function createTestingToken(done) {
|
||||
var test = this;
|
||||
Token.create({}, function (err, token) {
|
||||
|
@ -86,6 +108,23 @@ function createTestApp(testToken, done) {
|
|||
}
|
||||
res.send('ok');
|
||||
});
|
||||
app.use(loopback.rest());
|
||||
app.enableAuth();
|
||||
|
||||
var TestModel = loopback.Model.extend('test', {}, {
|
||||
acls: [
|
||||
{
|
||||
principalType: "ROLE",
|
||||
principalId: "$everyone",
|
||||
accessType: ACL.ALL,
|
||||
permission: ACL.DENY,
|
||||
property: 'removeById'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
TestModel.attachTo(loopback.memory());
|
||||
app.model(TestModel);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ describe('User', function(){
|
|||
User.setMaxListeners(0);
|
||||
|
||||
before(function () {
|
||||
debugger;
|
||||
User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'});
|
||||
});
|
||||
|
||||
|
@ -257,8 +256,7 @@ describe('User', function(){
|
|||
assert(result.token);
|
||||
|
||||
|
||||
var lines = result.email.message.split('\n');
|
||||
assert(lines[3].indexOf('To: bar@bat.com') === 0);
|
||||
assert(~result.email.message.indexOf('To: bar@bat.com'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue