Merge pull request #745 from strongloop/feature/allow-acls-settings-config
Allows ACLs/settings in model config
This commit is contained in:
commit
747da886c9
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue