diff --git a/lib/loopback.js b/lib/loopback.js index ab0a79e2..724e9835 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -203,6 +203,23 @@ loopback.getModel = function(modelName) { return loopback.Model.modelBuilder.models[modelName]; }; +/** + * Look up a model class by the base model class. The method can be used by LoopBack + * to find configured models in models.json over the base model. + * @param {Model} The base model class + * @return {Model} The subclass if found or the base class + */ +loopback.getModelByType = function(modelType) { + assert(typeof modelType === 'function', 'The model type must be a constructor'); + var models = loopback.Model.modelBuilder.models; + for(var m in models) { + if(models[m].prototype instanceof modelType) { + return models[m]; + } + } + return modelType; +}; + /** * Set the default `dataSource` for a given `type`. * @param {String} type The datasource type diff --git a/lib/models/acl.js b/lib/models/acl.js index 4e4a161c..1bb5b921 100644 --- a/lib/models/acl.js +++ b/lib/models/acl.js @@ -365,6 +365,7 @@ ACL.checkAccess = function (context, callback) { var staticACLs = this.getStaticACLs(model.modelName, property); var self = this; + var roleModel = loopback.getModelByType(Role); this.find({where: {model: model.modelName, property: propertyQuery, accessType: accessTypeQuery}}, function (err, acls) { if (err) { @@ -389,7 +390,7 @@ ACL.checkAccess = function (context, callback) { // Check role matches if (acl.principalType === ACL.ROLE) { inRoleTasks.push(function (done) { - Role.isInRole(acl.principalId, context, + roleModel.isInRole(acl.principalId, context, function (err, inRole) { if (!err && inRole) { effectiveACLs.push(acl); diff --git a/lib/models/role.js b/lib/models/role.js index c9cee824..7d583043 100644 --- a/lib/models/role.js +++ b/lib/models/role.js @@ -53,7 +53,8 @@ RoleMapping.ROLE = 'ROLE'; */ RoleMapping.prototype.application = function (callback) { if (this.principalType === RoleMapping.APPLICATION) { - var applicationModel = this.constructor.Application || loopback.Application; + var applicationModel = this.constructor.Application + || loopback.getModelByType(loopback.Application); applicationModel.findById(this.principalId, callback); } else { process.nextTick(function () { @@ -70,8 +71,9 @@ RoleMapping.prototype.application = function (callback) { */ RoleMapping.prototype.user = function (callback) { if (this.principalType === RoleMapping.USER) { - var userModel = this.constructor.User || loopback.User; - loopback.User.findById(this.principalId, callback); + var userModel = this.constructor.User + || loopback.getModelByType(loopback.User); + userModel.findById(this.principalId, callback); } else { process.nextTick(function () { callback && callback(null, null); @@ -87,7 +89,7 @@ RoleMapping.prototype.user = function (callback) { */ RoleMapping.prototype.childRole = function (callback) { if (this.principalType === RoleMapping.ROLE) { - var roleModel = this.constructor.Role || Role; + var roleModel = this.constructor.Role || loopback.getModelByType(Role); roleModel.findById(this.principalId, callback); } else { process.nextTick(function () { @@ -114,7 +116,7 @@ Role.RoleMapping = RoleMapping; // Set up the connection to users/applications/roles once the model Role.once('dataSourceAttached', function () { - var roleMappingModel = this.RoleMapping || RoleMapping; + var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping); Role.prototype.users = function (callback) { roleMappingModel.find({where: {roleId: this.id, principalType: RoleMapping.USER}}, function (err, mappings) { @@ -411,7 +413,7 @@ Role.getRoles = function (context, callback) { }); }); - var roleMappingModel = this.RoleMapping || RoleMapping; + var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping); context.principals.forEach(function (p) { // Check against the role mappings var principalType = p.type || undefined; diff --git a/test/loopback.test.js b/test/loopback.test.js index d3650953..2fd4d363 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -58,5 +58,39 @@ describe('loopback', function() { assert(MyCustomModel.super_.modelName === MyModel.modelName); }); }); + + describe('loopback.getModel and getModelByType', function () { + it('should be able to get model by name', function () { + var MyModel = loopback.createModel('MyModel', {}, { + foo: { + bar: 'bat' + } + }); + var MyCustomModel = loopback.createModel('MyCustomModel', {}, { + base: 'MyModel', + foo: { + bat: 'baz' + } + }); + assert(loopback.getModel('MyModel') === MyModel); + assert(loopback.getModel('MyCustomModel') === MyCustomModel); + assert(loopback.getModel('Invalid') === undefined); + }); + it('should be able to get model by type', function () { + var MyModel = loopback.createModel('MyModel', {}, { + foo: { + bar: 'bat' + } + }); + var MyCustomModel = loopback.createModel('MyCustomModel', {}, { + base: 'MyModel', + foo: { + bat: 'baz' + } + }); + assert(loopback.getModelByType(MyModel) === MyCustomModel); + assert(loopback.getModelByType(MyCustomModel) === MyCustomModel); + }); + }); }); }); diff --git a/test/role.test.js b/test/role.test.js index 4417fd10..163e5610 100644 --- a/test/role.test.js +++ b/test/role.test.js @@ -12,6 +12,14 @@ function checkResult(err, result) { } describe('role model', function () { + var ds; + + beforeEach(function() { + ds = loopback.createDataSource({connector: 'memory'}); + User.attachTo(ds); + Role.attachTo(ds); + RoleMapping.attachTo(ds); + }); it("should define role/role relations", function () { Role.create({name: 'user'}, function (err, userRole) { @@ -38,6 +46,7 @@ describe('role model', function () { }); it("should define role/user relations", function () { + User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function (err, user) { // console.log('User: ', user.id); Role.create({name: 'userRole'}, function (err, role) { @@ -113,10 +122,6 @@ describe('role model', function () { }); it("should support owner role resolver", function () { - var ds = loopback.createDataSource({connector: 'memory'}); - User.attachTo(ds); - Role.attachTo(ds); - RoleMapping.attachTo(ds); var Album = ds.createModel('Album', { name: String,