Skip to content

Commit

Permalink
support NotResource/NotAction
Browse files Browse the repository at this point in the history
  • Loading branch information
monken committed Aug 14, 2015
1 parent e793a50 commit d57ca42
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Use the power and flexibility of the AWS IAM Policy syntax in your own application to manage access control. For more details on AWS IAM Policies have a look at https://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html.

**Note:** The policy elements `Principal`, `NotPrincipal`, `NotResource` and conditions `ArnEquals`, `ArnNotEquals`, `ArnLike`, `ArnNotLike` are not supported at the moment.
**Note:** The policy elements `Principal`, `NotPrincipal` and conditions `ArnEquals`, `ArnNotEquals`, `ArnLike`, `ArnNotLike` are not supported at the moment.

## Installation

Expand Down
2 changes: 1 addition & 1 deletion output/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Use the power and flexibility of the AWS IAM Policy syntax in your own application to manage access control. For more details on AWS IAM Policies have a look at https://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html.

**Note:** The policy elements `Principal`, `NotPrincipal`, `NotResource` and conditions `ArnEquals`, `ArnNotEquals`, `ArnLike`, `ArnNotLike` are not supported at the moment.
**Note:** The policy elements `Principal`, `NotPrincipal` and conditions `ArnEquals`, `ArnNotEquals`, `ArnLike`, `ArnNotLike` are not supported at the moment.

## Installation

Expand Down
54 changes: 20 additions & 34 deletions pbac.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ var _ = require('lodash'),
ZSchema = require('z-schema'),
util = require('util');



var PBAC = function constructor(policies, options) {
options = _.isPlainObject(options) ? options : {};
var myconditions = _.isPlainObject(options.conditions) ? _.extend(options.conditions, conditions) : conditions;
Expand All @@ -18,36 +16,31 @@ var PBAC = function constructor(policies, options) {
conditions: myconditions,
});
this.addConditionsToSchema();
if(this.validateSchema) this._validateSchema();
if (this.validateSchema) this._validateSchema();
this.add(policies);
};

_.extend(PBAC.prototype, {
add: function add(policies) {
policies = _.isArray(policies) ? policies : [policies];
if(this.validatePolicies) this.validate(policies);
if (this.validatePolicies) this.validate(policies);
this.policies.push.apply(this.policies, policies);
},
addConditionsToSchema: function addConditionsToSchema() {
var definition = _.get(this.schema, 'definitions.Condition');
if(!definition) return;
if (!definition) return;
var props = definition.properties = {};
_.forEach(this.conditions, function(condition, name) {
props[name] = { type: 'object' };
props[name] = {
type: 'object'
};
}, this);
},
_validateSchema: function() {
var validator = new ZSchema();
if (!validator.validateSchema(this.schema))
this.throw('schema validation failed with', validator.getLastError());
},
/**
* Validates one or many policies against the schema provided in the constructor.
* Will throw an error if validation fails.
*
* @param {object} policy - Array of policies or single policy object
* @return {boolean} Returns `true` if the policies are valid
*/
validate: function validate(policies) {
policies = _.isArray(policies) ? policies : [policies];
var validator = new ZSchema({
Expand All @@ -60,17 +53,6 @@ _.extend(PBAC.prototype, {
return result;
}.bind(this));
},
/**
* Tests an object against the policies and determines if the object passes.
* The method will first try to find a policy with an explicit `Deny` for the combination of
* `resource`, `action` and `condition` (matching policy). If such policy exists, `evaulate` returns false.
* If there is no explicit deny the method will look for a matching policy with an explicit `Allow`.
* `evaulate` will return `true` if such a policy is found. If no matching can be found at all,
* `evaluate` will return `false`.
*
* @param {object} object - Object to test against the policies
* @return {boolean} Returns `true` if the object passes, `false` otherwise
*/
evaluate: function evaluate(options) {
options = _.extend({
action: '',
Expand All @@ -96,15 +78,14 @@ _.extend(PBAC.prototype, {
return _(this.policies).pluck('Statement').flatten().find(function(statement, idx) {
if (statement.Effect !== options.effect) return false;
var actionApplies = false;
if (!this.evaluateResource(statement.Resource, options.resource, options.variables))
if (statement.Resource && !this.evaluateResource(statement.Resource, options.resource, options.variables))
return false;
if (statement.NotResource && this.evaluateResource(statement.NotResource, options.resource, options.variables))
return false;
if (statement.Action && !this.evaluateAction(statement.Action, options.action))
return false;
if (statement.NotAction && this.evaluateAction(statement.NotAction, options.action))
return false;
if (statement.Action) actionApplies = _.find(statement.Action, function(action) {
return this.conditions.StringLike(action, options.action);
}.bind(this)) ? true : false;
if (statement.NotAction) actionApplies = _.all(statement.NotAction, function(action) {
return this.conditions.StringNotLike(action, options.action);
}.bind(this));
if (!actionApplies) return false;
return this.evaluateCondition(statement.Condition, options.variables);
}.bind(this));
},
Expand All @@ -113,12 +94,17 @@ _.extend(PBAC.prototype, {
return this.getVariableValue(variable, variables);
}.bind(this));
},
getVariableValue: function(variable, variables) {
getVariableValue: function getVariableValue(variable, variables) {
var parts = variable.split(':');
if (_.isPlainObject(variables[parts[0]]) && !_.isUndefined(variables[parts[0]][parts[1]]))
return variables[parts[0]][parts[1]];
else return variable;
},
evaluateAction: function evaluateAction(actions, reference) {
return _.find(actions, function(action) {
return this.conditions.StringLike.call(this, action, reference);
}.bind(this));
},
evaluateResource: function evaluateResource(resources, reference, variables) {
resources = _.isArray(resources) ? resources : [resources];
return _.find(resources, function(resource) {
Expand All @@ -139,7 +125,7 @@ _.extend(PBAC.prototype, {
}.bind(this));
}.bind(this));
},
throw: function(name, message) {
throw: function (name, message) {
var args = [].slice.call(arguments, 2);
args.unshift(message);
var e = new Error();
Expand Down
90 changes: 90 additions & 0 deletions t/notaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
var assert = require('assert'),
PBAC = require('../pbac');

var tests = [{
name: 'explicit deny overwrites allow',
policies: [{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": [
"iam:CreateUser"
],
"Resource": [
"*"
]
}, {
"Effect": "Allow",
"Action": [
"iam:*User"
],
"Resource": [
"abc*"
]
}]
}],
tests: [{
params: {
action: 'iam:CreateUser',
resource: 'abcfoo',
},
result: false,
}, {
params: {
action: 'iam:UpdateUser',
resource: 'abcfoo',
},
result: true,
}, {
params: {
action: 'iam:UpdateUser',
resource: 'foo',
},
result: false,
}]
}, {
name: 'implicit deny with NotAction',
policies: [{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"NotAction": [
"iam:CreateUser"
],
"NotResource": [
"abc*"
]
}]
}],
tests: [{
params: {
action: 'iam:CreateUser',
},
result: false,
}, {
params: {
action: 'iam:UpdateUser',
},
result: true,
}, {
params: {
resource: 'abcfoo',
action: 'iam:UpdateUser',
},
result: false,
}]
}];


describe('policies', function() {
tests.forEach(function(test, idx) {
var pbac = new PBAC(test.policies);
it(test.name, function() {
test.tests.forEach(function(params) {
assert.equal(!!pbac.evaluate(params.params), params.result);
});
});

});

});

0 comments on commit d57ca42

Please sign in to comment.