Merge pull request #2628 from benkroeger/master

Fix acl related model resolution

Close #2628
This commit is contained in:
Miroslav Bajtoš 2016-08-25 13:10:59 +02:00
commit 98eed7238d
5 changed files with 53 additions and 10 deletions

View File

@ -395,7 +395,9 @@ module.exports = function(ACL) {
*/ */
ACL.checkAccessForContext = function(context, callback) { ACL.checkAccessForContext = function(context, callback) {
var registry = this.registry; var self = this;
self.resolveRelatedModels();
var roleModel = self.roleModel;
if (!(context instanceof AccessContext)) { if (!(context instanceof AccessContext)) {
context = new AccessContext(context); context = new AccessContext(context);
@ -418,10 +420,8 @@ module.exports = function(ACL) {
var req = new AccessRequest(modelName, property, accessType, ACL.DEFAULT, methodNames); var req = new AccessRequest(modelName, property, accessType, ACL.DEFAULT, methodNames);
var effectiveACLs = []; var effectiveACLs = [];
var staticACLs = this.getStaticACLs(model.modelName, property); var staticACLs = self.getStaticACLs(model.modelName, property);
var self = this;
var roleModel = registry.getModelByType(Role);
this.find({ where: { model: model.modelName, property: propertyQuery, this.find({ where: { model: model.modelName, property: propertyQuery,
accessType: accessTypeQuery }}, function(err, acls) { accessType: accessTypeQuery }}, function(err, acls) {
if (err) { if (err) {
@ -508,10 +508,10 @@ module.exports = function(ACL) {
ACL.resolveRelatedModels = function() { ACL.resolveRelatedModels = function() {
if (!this.roleModel) { if (!this.roleModel) {
var reg = this.registry; var reg = this.registry;
this.roleModel = reg.getModelByType(loopback.Role); this.roleModel = reg.getModelByType('Role');
this.roleMappingModel = reg.getModelByType(loopback.RoleMapping); this.roleMappingModel = reg.getModelByType('RoleMapping');
this.userModel = reg.getModelByType(loopback.User); this.userModel = reg.getModelByType('User');
this.applicationModel = reg.getModelByType(loopback.Application); this.applicationModel = reg.getModelByType('Application');
} }
}; };

View File

@ -142,6 +142,34 @@ describe('access control - integration', function() {
}); });
describe('/banks', function() { describe('/banks', function() {
var SPECIAL_USER = { email: 'special@test.test', password: 'test' };
// define dynamic role that would only grant access when the authenticated user's email is equal to
// SPECIAL_USER's email
before(function() {
var roleModel = app.registry.getModel('Role');
var userModel = app.registry.getModel('user');
roleModel.registerResolver('$dynamic-role', function(role, context, callback) {
if (!(context && context.accessToken && context.accessToken.userId)) {
return process.nextTick(function() {
callback && callback(null, false);
});
}
var accessToken = context.accessToken;
userModel.findById(accessToken.userId, function(err, user) {
if (err) {
return callback(err, false);
}
if (user && user.email === SPECIAL_USER.email) {
return callback(null, true);
}
return callback(null, false);
});
});
});
lt.beforeEach.givenModel('bank'); lt.beforeEach.givenModel('bank');
lt.it.shouldBeAllowedWhenCalledAnonymously('GET', '/api/banks'); lt.it.shouldBeAllowedWhenCalledAnonymously('GET', '/api/banks');
@ -163,6 +191,7 @@ describe('access control - integration', function() {
lt.it.shouldBeDeniedWhenCalledAnonymously('DELETE', urlForBank); lt.it.shouldBeDeniedWhenCalledAnonymously('DELETE', urlForBank);
lt.it.shouldBeDeniedWhenCalledUnauthenticated('DELETE', urlForBank); lt.it.shouldBeDeniedWhenCalledUnauthenticated('DELETE', urlForBank);
lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForBank); lt.it.shouldBeDeniedWhenCalledByUser(CURRENT_USER, 'DELETE', urlForBank);
lt.it.shouldBeAllowedWhenCalledByUser(SPECIAL_USER, 'DELETE', urlForBank);
function urlForBank() { function urlForBank() {
return '/api/banks/' + this.bank.id; return '/api/banks/' + this.bank.id;

View File

@ -22,6 +22,12 @@
"permission": "ALLOW", "permission": "ALLOW",
"principalType": "ROLE", "principalType": "ROLE",
"principalId": "$everyone" "principalId": "$everyone"
},
{
"accessType": "WRITE",
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "$dynamic-role"
} }
], ],
"properties": {} "properties": {}

View File

@ -5,8 +5,12 @@
var loopback = require('../../../..'); var loopback = require('../../../..');
var boot = require('loopback-boot'); var boot = require('loopback-boot');
var app = module.exports = loopback({ localRegistry: true }); var app = module.exports = loopback({
localRegistry: true,
loadBuiltinModels: true,
});
var errorHandler = require('strong-error-handler'); var errorHandler = require('strong-error-handler');
boot(app, __dirname); boot(app, __dirname);
var apiPath = '/api'; var apiPath = '/api';

View File

@ -5,8 +5,12 @@
var loopback = require('../../../../index'); var loopback = require('../../../../index');
var boot = require('loopback-boot'); var boot = require('loopback-boot');
var app = module.exports = loopback({ localRegistry: true }); var app = module.exports = loopback({
localRegistry: true,
loadBuiltinModels: true,
});
var errorHandler = require('strong-error-handler'); var errorHandler = require('strong-error-handler');
app.enableAuth(); app.enableAuth();
boot(app, __dirname); boot(app, __dirname);
app.use(loopback.token({ model: app.models.AccessToken })); app.use(loopback.token({ model: app.models.AccessToken }));