From b8026648bf4596a2e1343dcfe88720b17eac6f94 Mon Sep 17 00:00:00 2001 From: Hao Jiang Date: Wed, 12 Apr 2017 13:13:36 +0200 Subject: [PATCH 01/18] Bug fixed: response with obligations from PDP --- src/main/components/policyEngine/Policy.js | 2 +- src/main/components/policyEngine/PolicySet.js | 2 +- src/main/components/policyEngine/Response.js | 20 +++++++++------- src/main/components/policyEngine/Rule.js | 2 +- .../policyEngine/algorithm/AllowOverrides.js | 18 +++++++------- .../policyEngine/algorithm/BlockOverrides.js | 24 +++++++++---------- .../policyEngine/algorithm/FirstApplicable.js | 6 +++-- src/main/components/policyEngine/pep/Pep.js | 7 ++---- .../policyEngine/prp/policy/policy.json | 20 ++++++++-------- 9 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/main/components/policyEngine/Policy.js b/src/main/components/policyEngine/Policy.js index 1266fd9..2fd985d 100644 --- a/src/main/components/policyEngine/Policy.js +++ b/src/main/components/policyEngine/Policy.js @@ -41,7 +41,7 @@ class Policy { } let response = this.ruleCombiningAlgorithm.combine(results); let obligations = this.obligations[response.effect]; - if (obligations) response.addObligations(obligations); + if (obligations) response.addObligations(new Map().set(this.name.substr(4), obligations)); return response; } diff --git a/src/main/components/policyEngine/PolicySet.js b/src/main/components/policyEngine/PolicySet.js index d2cebaa..65893d7 100644 --- a/src/main/components/policyEngine/PolicySet.js +++ b/src/main/components/policyEngine/PolicySet.js @@ -46,7 +46,7 @@ class PolicySet { } let response = this.policyCombiningAlgorithm.combine(results); let obligations = this.obligations[response.effect]; - if (obligations) response.addObligations(obligations); + if (obligations) response.addObligations(new Map().set(this.name.substr(4), obligations)); return response; } diff --git a/src/main/components/policyEngine/Response.js b/src/main/components/policyEngine/Response.js index 9e3f571..c7a7108 100644 --- a/src/main/components/policyEngine/Response.js +++ b/src/main/components/policyEngine/Response.js @@ -8,21 +8,25 @@ class Response { this.effect = effect; this.obligations = new Map(); this.info = info; - this.source = source; + this.source = source.substr(4); } addObligations(obligations) { - if (Object.keys(obligations).length){ - for (let key in obligations) { - if (!obligations.hasOwnProperty(key)) continue; - this.obligations.set(key, obligations[key]); - } + for (let [key,value] of obligations) { + this.obligations.set(key, value); } - } appendSource(child){ - this.source = this.source + '/' + child.substr(3); + this.source = this.source + '/' + child; + } + + pushSource(parent){ + this.source = parent + '/' + this.source; + } + + popSource(){ + this.source = this.source.substr(0, this.source.lastIndexOf("/")) } setEffect(effect){ diff --git a/src/main/components/policyEngine/Rule.js b/src/main/components/policyEngine/Rule.js index 299ad10..92d01ab 100644 --- a/src/main/components/policyEngine/Rule.js +++ b/src/main/components/policyEngine/Rule.js @@ -34,7 +34,7 @@ class Rule { let isApplicable = this.condition.isApplicable(message); if (isApplicable) { res.setEffect(this.effect); - res.addObligations(this.obligations); + res.addObligations(new Map().set(this.name.substr(4),this.obligations)); return res; } return res; diff --git a/src/main/components/policyEngine/algorithm/AllowOverrides.js b/src/main/components/policyEngine/algorithm/AllowOverrides.js index 097a9cb..03c7734 100644 --- a/src/main/components/policyEngine/algorithm/AllowOverrides.js +++ b/src/main/components/policyEngine/algorithm/AllowOverrides.js @@ -43,21 +43,19 @@ class AllowOverrides { * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `resulted from allow-overrides algorithm of ${this.name}`); - for (let i in responses){ - let res = responses[i]; - response.addObligations(res.obligations); - } + let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); let decisions = responses.map(res=>{return res.effect}); let idxPer = decisions.indexOf("permit"); + let idxDen = decisions.indexOf("deny"); if (idxPer !== -1) { - response.setEffect("permit"); - response.appendSource(responses[idxPer].source); - } else if (decisions.indexOf("deny") !== -1) { - response.setEffect("deny"); + response = responses[idxPer]; + } else if (idxDen !== -1) { + response = responses[idxDen]; } else { - response.setInfo("not applicable to the targeted message"); + return response; } + response.pushSource(this.name.substr(4)); + response.setInfo(`resulted from allow-overrides algorithm of ${this.name}`); return response; } diff --git a/src/main/components/policyEngine/algorithm/BlockOverrides.js b/src/main/components/policyEngine/algorithm/BlockOverrides.js index cb0f1ee..b801eeb 100644 --- a/src/main/components/policyEngine/algorithm/BlockOverrides.js +++ b/src/main/components/policyEngine/algorithm/BlockOverrides.js @@ -30,33 +30,31 @@ let Response = require("../Response"); class BlockOverrides { - constructor (context, name){ this.name = name; this.context = context; this.logger = this.context.registry.getLogger(); } + /** * Given an array of individual authorization decisions, prioritizes a positive one. * @param {boolean[]} responses * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `resulted from block-overrides algorithm of ${this.name}`); - for (let i in responses){ - let res = responses[i]; - response.addObligations(res.obligations); - } + let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); let decisions = responses.map(res=>{return res.effect}); - let idxDeny = decisions.indexOf("deny"); - if (idxDeny !== -1) { - response.setEffect("deny"); - response.appendSource(responses[idxDeny].source); - } else if (decisions.indexOf("permit") !== -1) { - response.setEffect("permit"); + let idxDen = decisions.indexOf("deny"); + let idxPer = decisions.indexOf("permit"); + if (idxDen !== -1) { + response = responses[idxDen]; + } else if (idxPer !== -1) { + response = responses[idxPer]; } else { - response.setInfo("not applicable to the targeted message"); + return response; } + response.pushSource(this.name.substr(4)); + response.setInfo(`resulted from block-overrides algorithm of ${this.name}`); return response; } diff --git a/src/main/components/policyEngine/algorithm/FirstApplicable.js b/src/main/components/policyEngine/algorithm/FirstApplicable.js index 7af7a44..845c818 100644 --- a/src/main/components/policyEngine/algorithm/FirstApplicable.js +++ b/src/main/components/policyEngine/algorithm/FirstApplicable.js @@ -41,15 +41,17 @@ class FirstApplicable { * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `${this.name} not applicable to the targeted message`); + let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); for (let i in responses) { if (responses[i].effect !== 'notApplicable') { response = responses[i]; + response.pushSource(this.name.substr(4)); + response.setInfo(`resulted from first-applicable algorithm of ${this.name}`); + return response; } } return response; } - } module.exports = FirstApplicable; diff --git a/src/main/components/policyEngine/pep/Pep.js b/src/main/components/policyEngine/pep/Pep.js index ad0bbb0..d1de8ff 100644 --- a/src/main/components/policyEngine/pep/Pep.js +++ b/src/main/components/policyEngine/pep/Pep.js @@ -63,11 +63,8 @@ class PEP { _enforce(response) { // Todo: take actions according to/specified in the decision from PDP - if (Object.keys(response.obligations).length){ - for (let key in response.obligations) { - if (!response.obligations.hasOwnProperty(key)) continue; - this.logger.info(`[${this.name}] ${key} ${response.obligations[key]}`); - } + if (response.obligations.size){ + this.logger.info(`[${this.name}] Obligation`, response.obligations); } return response; } diff --git a/src/main/components/policyEngine/prp/policy/policy.json b/src/main/components/policyEngine/prp/policy/policy.json index c97baff..e8742d3 100644 --- a/src/main/components/policyEngine/prp/policy/policy.json +++ b/src/main/components/policyEngine/prp/policy/policy.json @@ -4,7 +4,7 @@ "update" : "2017-03-14 17:18:31", "target": {}, "policyCombiningAlgorithm": "allowOverrides", - "obligations": {}, + "obligations": {"permit": {"info": "determines to permit"}}, "priority": 0, "policies": [ { @@ -12,20 +12,20 @@ "target": {}, "ruleCombiningAlgorithm": "blockOverrides", "priority": 0, - "obligations": {"permit": {"emailto": "admin@imt-atlantic.fr"}}, + "obligations": {"deny": {"info": "determines to deny"}}, "rules": [ { "id": 1, "target": {}, "condition": {"srcIDP":{"equals":"google.com"}}, "effect": "permit", - "obligations": {"emailto": "admin@imt-atlantic.fr"}, + "obligations": {"info": "determines to permit"}, "priority": 0 }, { "id": 2, "target":{}, - "condition": {"srcScheme":{"equals": "hello"}}, + "condition": {"srcScheme":{"equals": ["hello","runtime"]}}, "effect": "deny", - "obligations": {"emailto": "admin@imt-atlantic.fr"}, + "obligations": {"info": "determines to deny"}, "priority": 0 } ] @@ -35,7 +35,7 @@ "target": {}, "ruleCombiningAlgorithm": "allowOverrides", "priority": 0, - "obligations": {"permit": {"emailto": "admin@imt-atlantic.fr"}}, + "obligations": {"permit": {"info": "determines to permit"}}, "rules": [ { "id": 1, "target": {"srcIDPDomain": {"equals": "gmail.com"}}, @@ -50,28 +50,28 @@ } ], "effect": "deny", - "obligations": {"emailto": "admin@imt-atlantic.fr"}, + "obligations": {"info": "determines to deny"}, "priority": 0 }, { "id": 2, "target":{"actionType": {"in": ["create","update","open","subscribe","response", "handshake"]}}, "condition": {"srcUsername": {"like": "*@gmail.com"}}, "effect": "deny", - "obligations": {"emailto": "admin@imt-atlantic.fr"}, + "obligations": {"info": "determines to deny"}, "priority": 0 }, { "id": 3, "target":{}, "condition": {"msgType": {"in": ["registration","addressAllocation","discovery","globalRegistry","identityManagement","dataSync","p2pConnection"]}}, "effect": "permit", - "obligations": {}, + "obligations": {"info": "determines to permit"}, "priority": 0 }, { "id": 4, "target":{}, "condition": [], "effect": "permit", - "obligations": {}, + "obligations": {"info": "determines to permit"}, "priority": 0 } ] From 667efe8e6a5240c345402bf66508947acea03a9e Mon Sep 17 00:00:00 2001 From: Hao Jiang Date: Wed, 12 Apr 2017 13:27:45 +0200 Subject: [PATCH 02/18] Update readme --- src/main/components/policyEngine/prp/policy/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/components/policyEngine/prp/policy/readme.md b/src/main/components/policyEngine/prp/policy/readme.md index 9403756..0b74166 100644 --- a/src/main/components/policyEngine/prp/policy/readme.md +++ b/src/main/components/policyEngine/prp/policy/readme.md @@ -63,7 +63,7 @@ Each policy, as show in Figure 4, contains id, target, priority, and obligations **Figure 4:** Policy syntax -As show in Figure 5, in addition to a target, a rule includes one or a combination of Boolean conditions that, if evaluated true, have an effect of either Permit or Deny. If the target condition evaluates to True for a Rule and the Rule’s condition fails to evaluate for any reason, the effect of the Rule is Indeterminate. In comparison to the (matching) condition of a target, the conditions of a Rule or Policy are typically more complex and may include functions (e.g., "greater-than", "less-than", "equal") for the comparison of attribute values, and operations (e.g., "not", "and", "or") for the computation of Boolean values. +As show in Figure 5, in addition to a target, a rule includes one or a combination of Boolean conditions that, if evaluated true, have an effect of either Permit or Deny, otherwise the effect is not taken. If the target condition evaluates to True for a Rule and the Rule’s condition fails to evaluate for any reason, the effect of the Rule is Indeterminate. In comparison to the (matching) condition of a target, the conditions of a Rule or Policy are typically more complex and may include functions (e.g., "greater-than", "less-than", "equal") for the comparison of attribute values, and operations (e.g., "not", "and", "or") for the computation of Boolean values. ```json { From ccb9cf3f7411a3d2e3dbceaf5f85ff9ab3d1c868 Mon Sep 17 00:00:00 2001 From: Hao Jiang Date: Wed, 12 Apr 2017 23:14:54 +0200 Subject: [PATCH 03/18] Update: PDL supports to read attribute value for a message as a parameter --- .../policyEngine/AttributeCondition.js | 49 +++++++++++++------ src/main/components/policyEngine/Condition.js | 25 +++++----- .../policyEngine/algorithm/AllowOverrides.js | 15 +++--- .../policyEngine/algorithm/BlockOverrides.js | 15 +++--- .../policyEngine/algorithm/FirstApplicable.js | 7 +-- .../policyEngine/context/NodejsCtx.js | 9 ++++ .../policyEngine/pep/ContextHandler.js | 1 - .../policyEngine/prp/policy/policy.json | 27 ++++++---- 8 files changed, 96 insertions(+), 52 deletions(-) diff --git a/src/main/components/policyEngine/AttributeCondition.js b/src/main/components/policyEngine/AttributeCondition.js index 5208305..ab89fe5 100644 --- a/src/main/components/policyEngine/AttributeCondition.js +++ b/src/main/components/policyEngine/AttributeCondition.js @@ -6,11 +6,11 @@ let Operators = require('./Operators'); class AttributeCondition { - constructor(context, condition) { + constructor(context, attribute, expression) { this.context = context; this.logger = this.context.registry.getLogger(); - this.attribute = Object.keys(condition)[0]; - this.expression = condition[Object.keys(condition)[0]]; + this.attribute = attribute; + this.expression = expression; this.operators = new Operators(); this.name = "PDP AttrCond"; } @@ -23,8 +23,8 @@ class AttributeCondition { * by executing the operator implementation. * @param {Object} message */ - this.context[this.attribute] = {message: message}; - let value = this.context[this.attribute]; + + let value = this._getAttributeValue(this.attribute, message); let final = false; if (expression.constructor === Object) { let results = []; @@ -37,16 +37,22 @@ class AttributeCondition { params = Array.isArray(params)?params:[params]; result = this.operators[operator](params.map(param=>{return this.isApplicable(message, param)})); } - // otherwise it is comparative operator - // if params is an array - else if (operator !== "in" && params.constructor === Array) { - result = params.some(param => { - return this.operators[operator](value, param, this.attribute); - }); - } - // otherwise it is a value + // otherwise it is comparative operator, and params is really params else { - result = this.operators[operator](value, params, this.attribute); + // read attribute value + params = Array.isArray(params) ? + params.map(param=>{return this._getAttributeValue(param, message)}) : + this._getAttributeValue(params, message); + // if params is an array + if (operator !== "in" && params.constructor === Array) { + result = params.some(param => { + return this.operators[operator](value, param, this.attribute); + }); + } + // otherwise it is a value + else { + result = this.operators[operator](value, params, this.attribute); + } } results.push(result); } @@ -60,6 +66,21 @@ class AttributeCondition { } return final; } + + _getAttributeValue(attribute, msg){ + if (attribute !== this.attribute) { + let attr = this.context.isAttribute(attribute); + if (attr) { + this.context[attr] = {message: msg}; + return this.context[attr]; + } else { + return attribute; + } + } else { + this.context[attribute] = {message: msg}; + return this.context[attribute]; + } + } } module.exports = AttributeCondition; \ No newline at end of file diff --git a/src/main/components/policyEngine/Condition.js b/src/main/components/policyEngine/Condition.js index 898957c..4458555 100644 --- a/src/main/components/policyEngine/Condition.js +++ b/src/main/components/policyEngine/Condition.js @@ -43,18 +43,19 @@ class Condition { }); } // if the key is an attribute - else if (key in this.context){ - condition = new AttributeCondition(this.context, condition); - } - // else invalid else { - this.logger.info(`[${this.name}] warning: unrecognized key when building condition: ${key}`); + key = this.context.isAttribute(key); + if (key) { + condition = new AttributeCondition(this.context, key, value); + } else { + this.logger.info(`[${this.name}] warning: unrecognized key when building condition: ${key}!`); + } } } // else the map is empty - else { + // else { // this.logger.info(`[${this.name}] warning: empty ${this.usedFor} implies global applicability`); - } + // } } // if the condition is of ARRAY type, which contains subConditions with OR relations else { @@ -71,12 +72,12 @@ class Condition { } // if the condition is not of Object type else if (condition.constructor !== Object) { - throw new Error(`[${this.name}] syntax error: compiled condition should only be of Object type`); + throw new Error(`[${this.name}] syntax error: compiled condition should only be of Object type!`); } // the condition is not an instance of AttributeCondition // the condition contains multiple keys else if (Object.keys(condition).length > 1){ - throw new Error(`[${this.name}] syntax error: compiled condition contains multiple keys in a map`); + throw new Error(`[${this.name}] syntax error: compiled condition contains multiple keys in a map!`); } // the condition contains only one key else if (Object.keys(condition).length === 1) { @@ -88,10 +89,10 @@ class Condition { return this.isApplicable(message, subCondition); }) ); - } else if (key in this.context){ - throw new Error(`[${this.name}] syntax error: attribute is failed to build attribute condition object: ${key}`); + } else if (this.context.isAttribute(key)){ + throw new Error(`[${this.name}] syntax error: attribute is failed to build attribute condition object: ${key}!`); } else { - throw new Error(`[${this.name}] syntax error: unrecognized key in condition field: ${key}`); + throw new Error(`[${this.name}] syntax error: unrecognized key in condition field: ${key}!`); } } // empty condition diff --git a/src/main/components/policyEngine/algorithm/AllowOverrides.js b/src/main/components/policyEngine/algorithm/AllowOverrides.js index 03c7734..e89bde3 100644 --- a/src/main/components/policyEngine/algorithm/AllowOverrides.js +++ b/src/main/components/policyEngine/algorithm/AllowOverrides.js @@ -43,19 +43,22 @@ class AllowOverrides { * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); - let decisions = responses.map(res=>{return res.effect}); + let response = new Response(this.name); + let decisions = responses.map(res=>{ + this.logger.info(`[${this.name}] ${res.source} evaluated to ${res.effect}`); + return res.effect + }); let idxPer = decisions.indexOf("permit"); let idxDen = decisions.indexOf("deny"); if (idxPer !== -1) { response = responses[idxPer]; + response.pushSource(this.name.substr(4)); } else if (idxDen !== -1) { response = responses[idxDen]; - } else { - return response; + response.pushSource(this.name.substr(4)); } - response.pushSource(this.name.substr(4)); - response.setInfo(`resulted from allow-overrides algorithm of ${this.name}`); + response.setInfo(`resulted from allow-overrides algorithm`); + this.logger.info(`[${this.name}] ${response.getInfo()}`); return response; } diff --git a/src/main/components/policyEngine/algorithm/BlockOverrides.js b/src/main/components/policyEngine/algorithm/BlockOverrides.js index b801eeb..2d3885a 100644 --- a/src/main/components/policyEngine/algorithm/BlockOverrides.js +++ b/src/main/components/policyEngine/algorithm/BlockOverrides.js @@ -42,19 +42,22 @@ class BlockOverrides { * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); - let decisions = responses.map(res=>{return res.effect}); + let response = new Response(this.name); + let decisions = responses.map(res=>{ + this.logger.info(`[${this.name}] ${res.source} evaluated to ${res.effect}`); + return res.effect + }); let idxDen = decisions.indexOf("deny"); let idxPer = decisions.indexOf("permit"); if (idxDen !== -1) { response = responses[idxDen]; + response.pushSource(this.name.substr(4)); } else if (idxPer !== -1) { response = responses[idxPer]; - } else { - return response; + response.pushSource(this.name.substr(4)); } - response.pushSource(this.name.substr(4)); - response.setInfo(`resulted from block-overrides algorithm of ${this.name}`); + response.setInfo(`resulted from block-overrides algorithm`); + this.logger.info(`[${this.name}] ${response.getInfo()}`); return response; } diff --git a/src/main/components/policyEngine/algorithm/FirstApplicable.js b/src/main/components/policyEngine/algorithm/FirstApplicable.js index 845c818..527a885 100644 --- a/src/main/components/policyEngine/algorithm/FirstApplicable.js +++ b/src/main/components/policyEngine/algorithm/FirstApplicable.js @@ -41,15 +41,16 @@ class FirstApplicable { * @returns {Response} */ combine(responses) { - let response = new Response(this.name, `${this.name} resulted no applicable policies for the targeted message`); + let response = new Response(this.name); for (let i in responses) { if (responses[i].effect !== 'notApplicable') { response = responses[i]; response.pushSource(this.name.substr(4)); - response.setInfo(`resulted from first-applicable algorithm of ${this.name}`); - return response; + break; } } + response.setInfo(`resulted from first-applicable algorithm`); + this.logger.info(`[${this.name}] ${response.getInfo()}`); return response; } } diff --git a/src/main/components/policyEngine/context/NodejsCtx.js b/src/main/components/policyEngine/context/NodejsCtx.js index 50c8979..ab3f65a 100644 --- a/src/main/components/policyEngine/context/NodejsCtx.js +++ b/src/main/components/policyEngine/context/NodejsCtx.js @@ -9,6 +9,7 @@ class NodejsCtx extends ReThinkCtx { super(); this.name = 'PDP'; this.registry = registry; + this.logger = this.registry.getLogger(); this.msg = null; this.msgTypes = { registration: function (msg) { @@ -98,5 +99,13 @@ class NodejsCtx extends ReThinkCtx { } } } + + isAttribute(attr){ + if (attr.startsWith("<") && attr.endsWith(">") && (attr.substr(1, attr.length - 2) in this)){ + return attr.substr(1, attr.length - 2); + } else { + return null; + } + } } module.exports = NodejsCtx; \ No newline at end of file diff --git a/src/main/components/policyEngine/pep/ContextHandler.js b/src/main/components/policyEngine/pep/ContextHandler.js index a6beac6..740b8af 100644 --- a/src/main/components/policyEngine/pep/ContextHandler.js +++ b/src/main/components/policyEngine/pep/ContextHandler.js @@ -36,7 +36,6 @@ class ContextHandler { response.msg.body.auth = false; response.result = false; } - this.logger.info(`[${this.name}] ${response.getInfo()}`); return response; } diff --git a/src/main/components/policyEngine/prp/policy/policy.json b/src/main/components/policyEngine/prp/policy/policy.json index e8742d3..0839db3 100644 --- a/src/main/components/policyEngine/prp/policy/policy.json +++ b/src/main/components/policyEngine/prp/policy/policy.json @@ -16,14 +16,14 @@ "rules": [ { "id": 1, "target": {}, - "condition": {"srcIDP":{"equals":"google.com"}}, + "condition": {"":{"equals":"google.com"}}, "effect": "permit", "obligations": {"info": "determines to permit"}, "priority": 0 }, { "id": 2, "target":{}, - "condition": {"srcScheme":{"equals": ["hello","runtime"]}}, + "condition": {"":{"equals": ["hello","runtime"]}}, "effect": "deny", "obligations": {"info": "determines to deny"}, "priority": 0 @@ -38,15 +38,15 @@ "obligations": {"permit": {"info": "determines to permit"}}, "rules": [ { "id": 1, - "target": {"srcIDPDomain": {"equals": "gmail.com"}}, + "target": {"": {"equals": "gmail.com"}}, "condition": [ { - "weekday": {"not": {"in": ["saturday", "sunday"]}}, - "time": {"between": ["06:00:00 12:30:00", "13:00:00 23:00:00"]} + "": {"not": {"in": ["saturday", "sunday"]}}, + "