diff --git a/.travis.yml b/.travis.yml index 7572c4f..9b745d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: node_js node_js: - "0.10" - "0.12" - - "iojs" sudo: false diff --git a/README.md b/README.md index fa8db31..a4b2b1e 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ User = sequelize.define('user', { email: { type: Sequelize.STRING, roles: { - admin: {get: true}, - self: true + default: 'client', + system: {get: true}, + client: true } }, password: { @@ -37,16 +38,17 @@ User = sequelize.define('user', { rank: { type: Sequelize.STRING, roles: { - self: {set: false, get: true} - admin: true + default: 'client', + client: {set: false, get: true} + system: true } } }); -user.get(); // Will never include email or password -user.get({role: 'admin'}); // Will include email but not password -user.get({raw: true}); // Ignores roles, will include all +user.get(); // Applies default roles and include all the rest +user.get({role: 'system'}); // Will include email and rank but not password -user.set({rank: 'UBER'}, {role: 'self'||undefined}); // Will be ignored -user.set({rank: 'UBER'}, {role: 'admin'}); // Will be set +user.set({rank: 'UBER'}, {role: 'client'}); // Will be ignored +user.set({rank: 'UBER'}, {role: 'system'}); // Will be set +user.set({rank: 'UBER'}); // Will be ignored, default is client ``` diff --git a/lib/index.js b/lib/index.js index 97322b8..51b6c06 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,28 +10,44 @@ init = function(target) { Object.keys(target.rawAttributes).forEach(function (attr) { if (target.rawAttributes[attr].roles !== undefined) { if (target.rawAttributes[attr].roles === false) { - target.rawAttributes[attr].roles = {}; - return; + return; } - Object.keys(target.rawAttributes[attr].roles).forEach(function (role) { - if (typeof target.rawAttributes[attr].roles[role] === "boolean") { - target.rawAttributes[attr].roles[role] = { - set: target.rawAttributes[attr].roles[role], - get: target.rawAttributes[attr].roles[role] - }; + Object.keys(target.rawAttributes[attr].roles).forEach(function (roleName) { + var role = target.rawAttributes[attr].roles[roleName]; + + if (roleName === 'default') { + if(typeof role === "string"){ + role = target.rawAttributes[attr].roles.default = target.rawAttributes[attr].roles[role]; + } + } + + if (typeof role === "boolean") { + target.rawAttributes[attr].roles[roleName] = { set: role, get: role }; return; } - if (target.rawAttributes[attr].roles[role].set === undefined) { - target.rawAttributes[attr].roles[role].set = false; + if (role.set === undefined) { + role.set = true; } - if (target.rawAttributes[attr].roles[role].get === undefined) { - target.rawAttributes[attr].roles[role].get = false; + if (role.get === undefined) { + role.get = true; } }); } }); + + var accessGranted = function(method, attr, options){ + return !attr + // If no role is given apply default if any + || !options.role && (typeof attr.roles !== 'object' || (attr.roles.default||{})[method] !== false) + // If no roles defined in attribute or set to true + || attr.roles === undefined || attr.roles === true + // If role is given but not defined in attribute apply default if any + || options.role && (typeof attr.roles === 'object' && attr.roles[options.role] === undefined && (attr.roles.default||{})[method] !== false) + // Apply given role if defined in attribute + || (attr.roles[options.role]||{})[method] === true; + }; target.Instance.prototype.get = function(key, options) { if (typeof key === "object" && !options) { @@ -43,12 +59,11 @@ init = function(target) { options = {}; } - if (options.raw === true) { - return $get.call(this, key, options); - } if (key !== undefined) { var attr = target.rawAttributes[key]; - if (!attr || !attr.roles || attr.roles && attr.roles[options.role] && attr.roles[options.role].get) { + + if (accessGranted('get', attr, options)) { + return $get.call(this, key, options); } else { return undefined; @@ -57,7 +72,7 @@ init = function(target) { var values = $get.call(this, options) , response = {}; - + Object.keys(values).forEach(function (key) { var attr = target.rawAttributes[key]; @@ -65,7 +80,8 @@ init = function(target) { if(!attr && val instanceof Sequelize.Instance) { response[key] = val.get.call(val, options); } - else if (!attr || !attr.roles || attr.roles && attr.roles[options.role] && attr.roles[options.role].get) { + else if ( accessGranted('get', attr, options) ) { + response[key] = val; } }); diff --git a/test/get.test.js b/test/get.test.js index 80e2254..6c69052 100644 --- a/test/get.test.js +++ b/test/get.test.js @@ -4,15 +4,15 @@ var ssaclAttributeRoles = require('../lib') , expect = require('expect.js') , Sequelize = require('sequelize') , sequelize = new Sequelize(null, null, null, { - dialect: 'sqlite' - }); + dialect: 'sqlite' +}); describe('get', function () { it('should include attributes with no roles defined', function () { var Model = sequelize.define('model', { - attr1: Sequelize.STRING, - attr2: Sequelize.STRING - }) + attr1: Sequelize.STRING, + attr2: Sequelize.STRING + }) , instance , values; @@ -37,18 +37,14 @@ describe('get', function () { expect(instance.get('attr1', {role: Math.random().toString()})).to.be.ok(); }); - it('should not include attributes with roles: false or roles: {}', function () { + it('should include all attributes when no role is given to get', function(){ var Model = sequelize.define('model', { - attr1: Sequelize.STRING, - attr2: { - type: Sequelize.STRING, - roles: false - }, - attr3: { - type: Sequelize.STRING, - roles: {} - } - }) + attr1: Sequelize.STRING, + attr2: { + type: Sequelize.STRING, + roles: false + } + }) , instance , values; @@ -56,62 +52,162 @@ describe('get', function () { instance = Model.build({ attr1: Math.random().toString(), - attr2: Math.random().toString(), - attr3: Math.random().toString() + attr2: Math.random().toString() }); values = instance.get(); expect(values.attr1).to.be.ok(); - expect(values.attr2).not.to.be.ok(); - expect(values.attr3).not.to.be.ok(); + expect(values.attr2).to.be.ok(); + }); + + it('should not include attributes with roles: false', function () { + var Model = sequelize.define('model', { + attr1: Sequelize.STRING, + attr2: { + type: Sequelize.STRING, + roles: false + }, + attr3: { + type: Sequelize.STRING, + roles: {} + } + }) + , instance + , values; + + ssaclAttributeRoles(Model); + + instance = Model.build({ + attr1: Math.random().toString(), + attr2: Math.random().toString(), + attr3: Math.random().toString() + }); values = instance.get({role: Math.random().toString()}); expect(values.attr1).to.be.ok(); - expect(values.attr2).not.to.be.ok(); - expect(values.attr3).not.to.be.ok(); + expect(values.attr2).to.not.be.ok(); + expect(values.attr3).to.be.ok(); + + values = instance.get(); - values = instance.get({raw: true}); - expect(values.attr1).to.be.ok(); expect(values.attr2).to.be.ok(); expect(values.attr3).to.be.ok(); - expect(instance.get('attr2')).not.to.be.ok(); - - expect(instance.get('attr2', {raw: true})).to.be.ok(); - expect(instance.get('attr3', {role: Math.random().toString()})).not.to.be.ok(); + expect(instance.get('attr1', {role: Math.random().toString()})).to.be.ok(); + expect(instance.get('attr2', {role: Math.random().toString()})).to.not.be.ok(); + expect(instance.get('attr3', {role: Math.random().toString()})).to.be.ok(); }); - it('should only include attributes with the specific role', function () { + it('should apply role "default" if none is given to get', function(){ var Model = sequelize.define('model', { - attr1: { - type: Sequelize.STRING, - roles: { - rolea: true + attr1: Sequelize.STRING, + attr2: { + type: Sequelize.STRING, + roles: { + default: false + } + }, + attr3: { + type: Sequelize.STRING, + roles: { + default: { + get: false } - }, - attr2: { - type: Sequelize.STRING, - roles: { - rolea: {get: true}, - roleb: true + } + }, + attr4: { + type: Sequelize.STRING, + roles: { + default: { + get: true } - }, - attr3: { - type: Sequelize.STRING, - roles: { - roleb: { - get: true, set: true - }, - rolec: true, - rolea: { - set: true - } + } + } + }) + , instance + , values; + + ssaclAttributeRoles(Model); + + instance = Model.build({ + attr1: Math.random().toString(), + attr2: Math.random().toString(), + attr3: Math.random().toString(), + attr4: Math.random().toString() + }); + + values = instance.get(); + + expect(values.attr1).to.be.ok(); + expect(values.attr2).to.not.be.ok(); + expect(values.attr3).to.not.be.ok(); + expect(values.attr4).to.be.ok(); + }); + + it('should apply role referenced by "default" role if none is given to get', function(){ + var Model = sequelize.define('model', { + attr1: { + type: Sequelize.STRING, + roles: { + default: 'otherRole', + otherRole: false + } + }, + attr2: { + type: Sequelize.STRING, + roles: { + default: 'otherRole', + otherRole: true + } + } + }) + , instance + , values; + + ssaclAttributeRoles(Model); + + instance = Model.build({ + attr1: Math.random().toString(), + attr2: Math.random().toString() + }); + + values = instance.get(); + + expect(values.attr1).to.not.be.ok(); + expect(values.attr2).to.be.ok(); + }); + + it('should only apply the given role on attributes containing the role', function () { + var Model = sequelize.define('model', { + attr1: { + type: Sequelize.STRING, + roles: { + rolea: false + } + }, + attr2: { + type: Sequelize.STRING, + roles: { + rolea: {get: false}, + roleb: false + } + }, + attr3: { + type: Sequelize.STRING, + roles: { + roleb: { + get: false, set: false + }, + rolec: false, + rolea: { + set: false } } - }) + } + }) , instance , values; @@ -123,23 +219,23 @@ describe('get', function () { attr3: Math.random().toString() }); - expect(instance.get('attr1', {role: 'rolea'})).to.be.ok(); - expect(instance.get('attr1', {role: 'roleb'})).not.to.be.ok(); + expect(instance.get('attr1', {role: 'rolea'})).to.not.be.ok(); + expect(instance.get('attr1', {role: 'roleb'})).to.be.ok(); values = instance.get({role: 'rolea'}); - expect(values.attr1).to.be.ok(); - expect(values.attr2).to.be.ok(); - expect(values.attr3).not.to.be.ok(); + expect(values.attr1).to.not.be.ok(); + expect(values.attr2).to.not.be.ok(); + expect(values.attr3).to.be.ok(); values = instance.get({role: 'roleb'}); - expect(values.attr1).not.to.be.ok(); - expect(values.attr2).to.be.ok(); - expect(values.attr3).to.be.ok(); + expect(values.attr1).to.be.ok(); + expect(values.attr2).to.not.be.ok(); + expect(values.attr3).to.not.be.ok(); values = instance.get({role: 'rolec'}); - expect(values.attr1).not.to.be.ok(); - expect(values.attr2).not.to.be.ok(); - expect(values.attr3).to.be.ok(); + expect(values.attr1).to.be.ok(); + expect(values.attr2).to.be.ok(); + expect(values.attr3).to.not.be.ok(); }); it('should work with includes', function () { @@ -166,7 +262,7 @@ describe('get', function () { price: { type: Sequelize.FLOAT, roles: { - admin: true + system: false } }, name: Sequelize.STRING @@ -187,25 +283,22 @@ describe('get', function () { price: Math.random(), name: Math.random().toString() } - }, { - role: 'system', - include: [Company] - }); + }, { include: [Company] }); var values = user.get({role: 'self'}); - expect(values.companyId).not.to.be.ok(); + expect(values.companyId).to.be.ok(); expect(values.email).to.be.ok(); - expect(values.password).not.to.be.ok(); + expect(values.password).to.not.be.ok(); expect(values.company).to.be.ok(); expect(values.company.name).to.be.ok(); - expect(values.company.price).not.to.be.ok(); + expect(values.company.price).to.be.ok(); values = user.get({role: 'admin'}); - expect(values.companyId).not.to.be.ok(); - expect(values.email).not.to.be.ok(); - expect(values.password).not.to.be.ok(); + expect(values.companyId).to.be.ok(); + expect(values.email).to.be.ok(); + expect(values.password).to.not.be.ok(); expect(values.company).to.be.ok(); expect(values.company.name).to.be.ok(); expect(values.company.price).to.be.ok(); @@ -222,7 +315,7 @@ describe('get', function () { this.setDataValue('active', value ? 1 : 0); }, roles: { - admin: true + client: false } } }); @@ -231,14 +324,12 @@ describe('get', function () { var account = Account.build({ active: true - }, { - role: 'admin' }); - var values = account.get({role: 'admin'}); + var values = account.get({role: 'client'}); - expect(values.active).to.equal(true); + expect(values.active).to.be.undefined; - expect(account.get('active', {role: 'admin'})).to.equal(true); + expect(account.get('active')).to.equal(true); }); }); \ No newline at end of file