Obligations and Advice in XACML part 2

In a previous blog post we discussed the use of XACML obligations and advice. I concluded the post with the cliff hanger:
An interesting use of advice is as a means to tell the PEP the reasons why a request has been denied; but to show you how this is done I would need to introduce you to the way the PDP calculates the advice for a decision.
So let’s start then by taking a look at the way the PDP calculates the obligations (or advice) that correspond to a decision.
The XACML specification tells us how it is done (§7.18):
“A rule, policy, or policy set may contain one or more obligation or advice expressions. When such a rule, policy, or policy set is evaluated, the obligation or advice expression SHALL be evaluated to an obligation or advice respectively, which SHALL be passed up to the next level of evaluation (the enclosing or referencing policy, policy set, or authorization decision) only if the result of the rule, policy, or policy set being evaluated matches the value of the FulfillOn attribute of the obligation or the AppliesTo attribute of the advice. If any of the attribute assignment expressions in an obligation or advice expression with a matching FulfillOn or AppliesTo attribute evaluates to “Indeterminate”, then the whole rule, policy, or policy set SHALL be “Indeterminate”. If the FulfillOn or AppliesTo attribute does not match the result of the combining algorithm or the rule evaluation, then any indeterminate in an obligation or advice expression has no effect. As a consequence of this procedure, no obligations or advice SHALL be returned to the PEP if the rule, policies, or policy sets from which they are drawn are not evaluated, or if their evaluated result is “Indeterminate” or “NotApplicable”, or if the decision resulting from evaluating the rule, policy, or policy set does not match the decision resulting from evaluating an enclosing policy set. If the PDP’s evaluation is viewed as a tree of rules, policy sets and policies, each of which returns “Permit” or “Deny”, then the set of obligations and advice returned by the PDP to the PEP will include only the obligations and advice associated with those paths where the result at each level of evaluation is the same as the result being returned by the PDP. In situations where any lack of determinism is unacceptable, a deterministic combining algorithm, such as ordered-deny-overrides, should be used.”
Right… let’s try and make sense of that.
First, let’s see how “advice expressions” are turned into “advice”. The difference between an advice expression and advice is that the arguments of the former are expressions, and the arguments of the latter are values. So, if we have an advice expression
advice notifyDoctor { notificationRecipient = doctorId }
where doctorId is a string attribute with the value “doc42”, the advice expression will evaluate to
advice notifyDoctor { notificationRecipient = "doc42" }
Of course the expressions used as arguments can be complex
advice notifyDoctor { notificationRecipient = stringConcatenate( stringOneAndOnly(hospitalId), stringConcatenate(":",stringOneAndOnly(doctorId))) }
This advice expression, in the same context as before and with hospitalId having the value “krlk”, would evaluate to
advice notifyDoctor { notificationRecipient = "krlk:doc42" }
Notice that if the evaluation of any of the argument expressions results in Indeterminatethen the evaluation of the whole advice expression will result in Indeterminate. For example, evaluating
advice notifyDoctor { notificationRecipient = doctorId copies = 1/0 }
will always result in Indeterminate.
Now, how do we calculate the advice for a rule?
First we check that the rule would actually return a decision, that is to say it would return what we call its “effect”: Permit or Deny (but not NotApplicable or Indeterminate). Then we pick all the advice expressions with AppliesToequal to the effect, and we evaluate them into the corresponding advice. If any of these advice expressions evaluates to Indeterminate, the whole rule evaluates to Indeterminate.Otherwise, the rule evaluates to its effect, accompanied by the evaluated advice.

Next, how do we calculate the advice for a policy?
This is a bit more complicated. Whenever we evaluate a rule in the policy, we collect the resulting advice into two groups: the advice that applies on Permit and the advice that applies on Deny.Once we reach a point in which we are ready to return a decision for the policy that is neither NotApplicablenor Indeterminatewe proceed and evaluate all the policy’s own advice expressions that apply to the decision. If neither of these evaluates to Indeterminate, we return the decision, accompanied by the advice just calculated plus the advice we have collected in the group that corresponds to the decision. If any of these evaluates to Indeterminate the whole policy evaluates to Indeterminate.

Now, for the last bit, how do we calculate the advice for a policy set?
We do it in a similar way as we do it for policies. Whenever we evaluate a policy in the policy set, we collect the resulting advice into two groups: the advice that applies on Permitand the advice that applies on Deny. And again, once we reach the point where we are ready to return a decision for the policy set (that is neither NotApplicable nor Indeterminate)we proceed and evaluate all the policy set’s own advice expressions that apply to the decision. If neither of these evaluates to Indeterminate we return the decision, accompanied by the advice just calculated plus the advice we have collected in the group that corresponds to the decision. If any of these evaluates to Indeterminate the whole policy setevaluates to Indeterminate.

Now that we know all this, let’s see how advice can be used as a means to tell the PEP the reasons why a request has been denied.
Consider the following policy:
policyset Root { apply denyOverrides policy A { apply permitOverrides rule A1 { deny on permit { advice dummy { message = “a7” }} on deny { advice reason { message = “a8" }} } rule A2 { permit on permit { advice dummy { message = “a9” }} on deny { advice reason { message = “a10" }} } on permit { advice dummy { message = “a3” }} on deny { advice reason { message = “a4" }} } policy B { apply denyOverrides rule B1 { permit on permit { advice dummy { message = “a9” }} on deny { advice reason { message = “a10" }} } rule B2 { deny on permit { advice dummy { message = “a7” }} on deny { advice reason { message = “a8" }} } rule B3 { deny on permit { advice dummy { message = “a9” }} on deny { advice reason { message = “a10" }} } on permit { advice dummy { message = “a5” }} on deny { advice reason { message = “a6" }} } on permit { advice dummy { message = “a1” }} on deny { advice reason { message = “a2" }} }
This policy evaluates the same for all decision requests. An evaluation of this policy looks like this:
The resulting decision carries a set of advice that tells us that the decision is Deny because “a14” (Rule B2 denied), “a6” (Policy B denied) and “a2” (Root policy set denied).
Although interesting in its own right, this technique cannot be used reliably with all policies, because the advice you will get may not really be a good “reason” for the decision. It depends a lot on how you structure your policies and which combining algorithms you use.
For example, imagine a policy that uses the on Permit Apply Second combining algorithm.

AThe diagram shows two possible evaluations of a simple policy that combines three rules with on Permit Apply Second.
Notice that advice 1 is returned only in one case, although I would argue that the fact that the first policy is a Deny is part of the reason why the whole policy set evaluates to Deny.
One can think of ways to work around issues like this, but the resulting policies will be more error-prone and more difficult to maintain. A better, more reliable way to analyze your policies is to use a specialized tool like the Axiomatics Policy Auditor or Axiomatics Reverse Query.
I hope this post has shed some more light on the topic of obligations and advice in XACML. Thank you for staying with me all the way!