Merge pull request #745 from strongloop/feature/allow-acls-settings-config

Allows ACLs/settings in model config
This commit is contained in:
Raymond Feng 2014-11-07 11:15:08 -08:00
commit 747da886c9
2 changed files with 166 additions and 5 deletions

View File

@ -145,6 +145,25 @@ function buildModelOptionsFromConfig(config) {
return options; return options;
} }
/*
* Add the acl entry to the acls
* @param {Object[]} acls
* @param {Object} acl
*/
function addACL(acls, acl) {
for (var i = 0, n = acls.length; i < n; i++) {
// Check if there is a matching acl to be overriden
if (acls[i].property === acl.property &&
acls[i].accessType === acl.accessType &&
acls[i].principalType === acl.principalType &&
acls[i].principalId === acl.principalId) {
acls[i] = acl;
return;
}
}
acls.push(acl);
}
/** /**
* Alter an existing Model class. * Alter an existing Model class.
* @param {Model} ModelCtor The model constructor to alter. * @param {Model} ModelCtor The model constructor to alter.
@ -157,12 +176,51 @@ function buildModelOptionsFromConfig(config) {
registry.configureModel = function(ModelCtor, config) { registry.configureModel = function(ModelCtor, config) {
var settings = ModelCtor.settings; var settings = ModelCtor.settings;
var modelName = ModelCtor.modelName;
if (config.relations) { // Relations
if (typeof config.relations === 'object' && config.relations !== null) {
var relations = settings.relations = settings.relations || {}; var relations = settings.relations = settings.relations || {};
Object.keys(config.relations).forEach(function(key) { Object.keys(config.relations).forEach(function(key) {
// FIXME: [rfeng] We probably should check if the relation exists
relations[key] = extend(relations[key] || {}, config.relations[key]); relations[key] = extend(relations[key] || {}, config.relations[key]);
}); });
} else if (config.relations != null) {
console.warn('The relations property of `%s` configuration ' +
'must be an object', modelName);
}
// ACLs
if (Array.isArray(config.acls)) {
var acls = settings.acls = settings.acls || [];
config.acls.forEach(function(acl) {
addACL(acls, acl);
});
} else if (config.acls != null) {
console.warn('The acls property of `%s` configuration ' +
'must be an array of objects', modelName);
}
// Settings
var excludedProperties = {
base: true,
'super': true,
relations: true,
acls: true,
dataSource: true
};
if (typeof config.options === 'object' && config.options !== null) {
for (var p in config.options) {
if (!(p in excludedProperties)) {
settings[p] = config.options[p];
} else {
console.warn('Property `%s` cannot be reconfigured for `%s`',
p, modelName);
}
}
} else if (config.options != null) {
console.warn('The options property of `%s` configuration ' +
'must be an object', modelName);
} }
// It's important to attach the datasource after we have updated // It's important to attach the datasource after we have updated
@ -173,17 +231,17 @@ registry.configureModel = function(ModelCtor, config) {
': config.dataSource must be an instance of DataSource'); ': config.dataSource must be an instance of DataSource');
ModelCtor.attachTo(config.dataSource); ModelCtor.attachTo(config.dataSource);
debug('Attached model `%s` to dataSource `%s`', debug('Attached model `%s` to dataSource `%s`',
ModelCtor.definition.name, config.dataSource.name); modelName, config.dataSource.name);
} else if (config.dataSource === null) { } else if (config.dataSource === null) {
debug('Model `%s` is not attached to any DataSource by configuration.', debug('Model `%s` is not attached to any DataSource by configuration.',
ModelCtor.definition.name); modelName);
} else { } else {
debug('Model `%s` is not attached to any DataSource, possibly by a mistake.', debug('Model `%s` is not attached to any DataSource, possibly by a mistake.',
ModelCtor.definition.name); modelName);
console.warn( console.warn(
'The configuration of `%s` is missing `dataSource` property.\n' + 'The configuration of `%s` is missing `dataSource` property.\n' +
'Use `null` or `false` to mark models not attached to any data source.', 'Use `null` or `false` to mark models not attached to any data source.',
ModelCtor.definition.name); modelName);
} }
}; };

View File

@ -249,6 +249,109 @@ describe('loopback', function() {
expect(owner, 'model.prototype.owner').to.be.a('function'); expect(owner, 'model.prototype.owner').to.be.a('function');
expect(owner._targetClass).to.equal('User'); expect(owner._targetClass).to.equal('User');
}); });
it('adds new acls', function() {
var model = loopback.Model.extend(uniqueModelName, {}, {
acls: [
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'DENY'
}
]
});
loopback.configureModel(model, {
dataSource: null,
acls: [
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: 'admin',
permission: 'ALLOW'
}
]
});
expect(model.settings.acls).eql([
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'DENY'
},
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: 'admin',
permission: 'ALLOW'
}
]);
});
it('updates existing acls', function() {
var model = loopback.Model.extend(uniqueModelName, {}, {
acls: [
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'DENY'
}
]
});
loopback.configureModel(model, {
dataSource: null,
acls: [
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW'
}
]
});
expect(model.settings.acls).eql([
{
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$everyone',
permission: 'ALLOW'
}
]);
});
it('updates existing settings', function() {
var model = loopback.Model.extend(uniqueModelName, {}, {
ttl: 10,
emailVerificationRequired: false
});
loopback.configureModel(model, {
dataSource: null,
options: {
ttl: 20,
realmRequired: true,
base: 'X'
}
});
expect(model.settings).to.have.property('ttl', 20);
expect(model.settings).to.have.property('emailVerificationRequired',
false);
expect(model.settings).to.have.property('realmRequired', true);
expect(model.settings).to.not.have.property('base');
});
}); });
describe('loopback object', function() { describe('loopback object', function() {