diff --git a/README.md b/README.md index b35683e..9584945 100644 --- a/README.md +++ b/README.md @@ -91,11 +91,11 @@ const context = { dataRetrieverRouter.setContext(context); const policy = { - target: [{ 'credentials:username': 'francisco' }, { 'credentials:group': 'admin' }], // if username is 'francisco' OR group is 'admin' + target: [{ 'credentials:username': 'francisco' }, { 'credentials:group': /^articles\:.*$/ }], // if username is 'francisco' OR group matches 'articles:*' (using native javascript RegExp) apply: 'deny-overrides', // permit, unless one denies rules: [ { - target: { 'credentials:group': 'admin', 'credentials:validated': false }, // if group is 'admin' AND is not validated + target: { 'credentials:group': 'articles:admin', 'credentials:validated': false }, // if group is 'articles:admin' AND is not validated effect: 'deny' // then deny (deny access to users that are not validated) }, { diff --git a/lib/index.js b/lib/index.js index 448e863..6c02ddf 100644 --- a/lib/index.js +++ b/lib/index.js @@ -212,27 +212,35 @@ internals.evaluateTargetElementKey = (dataRetriever, element, key) => { /** * If target has more than one value, all of them should match **/ -internals._targetApplies = (target, value) => { +internals._targetApplies = (targets, values) => { - if (target === value) { - return true; + if (!Array.isArray(targets)) { + targets = [targets]; } - if (!(value instanceof Array)) { - value = [value]; + if (!Array.isArray(values)) { + values = [values]; } - if (!(target instanceof Array)) { - target = [target]; - } + // Should match all + // So: continue looping unless one doesn't + for (const index in targets) { + const target = targets[index]; + const matches = values.filter((value) => { - for (const index in target) { - if (value.indexOf(target[index]) === -1) { - // At least one doesn't match + if (target instanceof RegExp) { + return target.test(value); + } + + return value === target; + }); + + if (matches.length === 0) { return false; } } + // All targets are matched return true; }; diff --git a/test/target.js b/test/target.js index 3f85e8c..b01d9a4 100644 --- a/test/target.js +++ b/test/target.js @@ -80,6 +80,91 @@ experiment('Target unit tests (AND)', () => { }); +experiment('Target unit tests (AND with RegExp)', () => { + + const target = { 'credentials:group': /^articles\:(writer|reader)$/, 'credentials:premium': true }; + + // Register mocked data retriever + const dataRetriever = new DataRetrievalRouter(); + dataRetriever.register('credentials', (source, key, context) => { + + return context[key]; + }, { override: true }); + + test('should apply (full match: articles:writer)', (done) => { + + const information = { + username: 'user00001', + group: ['articles:writer'], + premium: true + }; + + Rbac.evaluateTarget(target, dataRetriever.createChild(information), (err, applies) => { + + expect(err).to.not.exist(); + + expect(applies).to.exist().and.to.equal(true); + + done(); + }); + }); + + test('should apply (full match: articles:reader)', (done) => { + + const information = { + username: 'user00002', + group: ['articles:reader'], + premium: true + }; + + Rbac.evaluateTarget(target, dataRetriever.createChild(information), (err, applies) => { + + expect(err).to.not.exist(); + + expect(applies).to.exist().and.to.equal(true); + + done(); + }); + }); + + test('should not apply (partial match)', (done) => { + + const information = { + username: 'user00003', + group: ['articles:other'], + premium: true + }; + + Rbac.evaluateTarget(target, dataRetriever.createChild(information), (err, applies) => { + + expect(err).to.not.exist(); + + expect(applies).to.exist().and.to.equal(false); + + done(); + }); + }); + + test('should not apply (no match)', (done) => { + + const information = { + username: 'user00004', + group: ['articles:other'], + premium: false + }; + + Rbac.evaluateTarget(target, dataRetriever.createChild(information), (err, applies) => { + + expect(err).to.not.exist(); + + expect(applies).to.exist().and.to.equal(false); + + done(); + }); + }); + +}); + experiment('Target unit tests (OR)', () => { const target = [