Reputation: 691
Below is an XACML policy, for Chinese Walls, which uses stringAtLeastOneMemberOf
to compare two attributes two see if they contain the same value of a list of values.
I.e. if the subject requesting access to an object has a label [1, 4, 5]
and the object has a label [2, 3, 5]
then access will be denied as both contain 5
.
attribute subjectConflicts {
id = "urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts"
type = string
category = subjectCat
}
attribute resourceConflicts {
id = "urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts"
type = string
category = resourceCat
}
namespace models {
import Attributes.*
/*
*
* This policy implements the Chinese Wall model.
*
*/
policy ChineseWall {
target clause resourceType=="calcert"
apply firstApplicable
/*
* Check subject is not in conflict with object OEMs and calibrators
*
* This rule will deny access is user.label contains at least 1 value that is also present
* in object.label
*/
rule noconflict {
target clause actionId=="read" or actionId=="write"
condition stringAtLeastOneMemberOf(subjectConflicts, resourceConflicts)
deny
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--This file was generated by the ALFA Plugin for Eclipse from Axiomatics AB (http://www.axiomatics.com).-->
<!--Any modification to this file will be lost upon recompilation of the source ALFA file-->
<xacml3:Policy xmlns:xacml3="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicyId="http://axiomatics.com/alfa/identifier/models.ChineseWall" RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" Version="1.0">
<xacml3:Description>This policy implements the Chinese Wall model.</xacml3:Description>
<xacml3:PolicyDefaults>
<xacml3:XPathVersion>http://www.w3.org/TR/1999/REC-xpath-19991116</xacml3:XPathVersion>
</xacml3:PolicyDefaults>
<xacml3:Target>
<xacml3:AnyOf>
<xacml3:AllOf>
<xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<xacml3:AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">calcert</xacml3:AttributeValue>
<xacml3:AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-type" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
</xacml3:Match>
</xacml3:AllOf>
</xacml3:AnyOf>
</xacml3:Target>
<xacml3:Rule Effect="Deny" RuleId="models.ChineseWall.noconflict">
<xacml3:Description>Check subject is not in conflict with object OEMs and calibrators
This rule will deny access is user.label contains at least 1 value that is also present
in object.label</xacml3:Description>
<xacml3:Target>
<xacml3:AnyOf>
<xacml3:AllOf>
<xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<xacml3:AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</xacml3:AttributeValue>
<xacml3:AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
</xacml3:Match>
</xacml3:AllOf>
<xacml3:AllOf>
<xacml3:Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<xacml3:AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">write</xacml3:AttributeValue>
<xacml3:AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
</xacml3:Match>
</xacml3:AllOf>
</xacml3:AnyOf>
</xacml3:Target>
<xacml3:Condition>
<xacml3:Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of">
<xacml3:AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
<xacml3:AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false" />
</xacml3:Apply>
</xacml3:Condition>
</xacml3:Rule>
</xacml3:Policy>
I am using the Authzforce Core PDP for Java to emulate the PDP and I evaluate the request as follows:
private DecisionRequest parseJSONAndBuildXACML(JSONObject obj) {
DecisionRequestBuilder builder = server.pdpEngine.getEngine().newRequestBuilder(-1, -1);
/** Add Principle **/
// Principle ID
AttributeFqn principleID = AttributeFqns.newInstance(XACML_1_0_ACCESS_SUBJECT.value(), Optional.empty(), XacmlAttributeId.XACML_1_0_SUBJECT_ID.value());
AttributeBag<?> principleIDValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue("Principle" + obj.getJSONObject("principle").getString("id")));
builder.putNamedAttributeIfAbsent(principleID, principleIDValue);
// Principle Label
AttributeFqn principleLabel = AttributeFqns.newInstance(XACML_1_0_ACCESS_SUBJECT.value(), Optional.<String>empty(), XACML_1_0_SUBJECT_LABEL);
AttributeBag<?> principleLabelValue = Bags.singletonAttributeBag(StandardDatatypes.INTEGER, new IntegerValue(new MediumInteger(obj.getJSONObject("principle").getInt("label"))));
builder.putNamedAttributeIfAbsent(principleLabel, principleLabelValue);
// Principle Conflict Set
AttributeFqn principleConflicts = AttributeFqns.newInstance(XACML_1_0_ACCESS_SUBJECT.value(), Optional.empty(), XACML_1_0_SUBJECT_CONFLICTS);
Collection<StringValue> pconflicts = getStringListFromJsonArray(obj.getJSONObject("principle").getJSONArray("conflicts"));
AttributeBag<?> principleConflictsValue = Bags.newAttributeBag(StandardDatatypes.STRING, pconflicts);
//AttributeBag<?> principleConflictsValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getJSONObject("principle").getString("conflicts")));
builder.putNamedAttributeIfAbsent(principleConflicts, principleConflictsValue);
// Object ID
AttributeFqn objectID = AttributeFqns.newInstance(XACML_3_0_RESOURCE.value(), Optional.empty(), XACML_1_0_RESOURCE_ID.value());
AttributeBag<?> objectIDValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getJSONObject("object").getString("id")));
builder.putNamedAttributeIfAbsent(objectID, objectIDValue);
// Object Type
AttributeFqn objectType = AttributeFqns.newInstance(XACML_3_0_RESOURCE.value(), Optional.empty(), XACML_1_0_RESOURCE_TYPE);
AttributeBag<?> objectTypeValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getJSONObject("object").getString("type")));
builder.putNamedAttributeIfAbsent(objectType, objectTypeValue);
// Object Label
AttributeFqn objectLabel = AttributeFqns.newInstance(XACML_3_0_RESOURCE.value(), Optional.<String>empty(), XACML_1_0_RESOURCE_LABEL);
AttributeBag<?> objectLabelValue = Bags.singletonAttributeBag(StandardDatatypes.INTEGER, new IntegerValue(new MediumInteger(obj.getJSONObject("object").getInt("label"))));
builder.putNamedAttributeIfAbsent(objectLabel, objectLabelValue);
// Object Conflict Set
AttributeFqn objectConflicts = AttributeFqns.newInstance(XACML_3_0_RESOURCE.value(), Optional.empty(), XACML_1_0_RESOURCE_CONFLICTS);
Collection<StringValue> oconflicts = getStringListFromJsonArray(obj.getJSONObject("object").getJSONArray("conflicts"));
AttributeBag<?> objectConflictsValue = Bags.newAttributeBag(StandardDatatypes.STRING, oconflicts);
//AttributeBag<?> objectConflictsValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getJSONObject("object").getString("conflicts")));
builder.putNamedAttributeIfAbsent(objectConflicts, objectConflictsValue);
// Action
AttributeFqn action = AttributeFqns.newInstance(XACML_3_0_ACTION.value(), Optional.empty(), XacmlAttributeId.XACML_1_0_ACTION_ID.value());
AttributeBag<?> actionValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getString("action")));
builder.putNamedAttributeIfAbsent(action, actionValue);
return builder.build(false);
}
The JSONObject
in this case is just how I send the request from my client to the PDP engine. Other policies work, the problem here is I am sending a String i.e. "[2, 4, 5]"
to the bag, but it always results in a NotApplicable
. Is there are list type that I should be using here instead to conform with the policy?
Here is the JSON I am sending:
{
"principle": {
"conflicts": [
"1",
"2",
"10"
],
"id": "Principle 1",
"label": 3
},
"action": "read",
"object": {
"conflicts": [
"4",
"5",
"9"
],
"id": "Object 1",
"label": 2,
"type": "calcert"
}
}
To be more precise, the JSON output in the java code to Authzforce for the conflict set will be a string i.e. "[2,3,5]"
whereas I think this needs to be another format (since it always results in NotApplicable), but this is my question.
Upvotes: 1
Views: 985
Reputation: 13836
First of all you have to understand that all attributes in XACML are bags of values by default. So, urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts
is a bag that can contain 0 (zero), 1, or more values. But it's a bag nonetheless. This is true of any attribute in ALFA / XACML. This means that when you write:
target clause resourceType=="calcert"
You are actually saying if there is at least one value of resourceType
equal to calcert
...
In your case, you are using a function called stringAtLeastOneMemberOf
. This function takes in two bags of type string and returns true if the first bag contains at least one value present in the second bag. It is similar to stringIsIn
except that the latter takes in an atomic string and a bag of strings. You would have to write stringIsIn(stringOneAndOnly(a), b)
to make it work. The attribute a would also have to be a bag containing a single value.
In your case, you have 2 attributes (subjectConflicts
and resourceConflicts
) that can be multi-valued. This means that if you "printed" them, you would see something along the lines of ["a", "b", "c", "a"]. This is a bag of values. Bags can contain duplicates by the way. And the order does not matter (neither the order of attributes or the order of attribute values).
Now, what you want to do is send a XACML JSON request that represents this. The resulting JSON should look like:
In this example, both attributes are multi-valued:
{
"Request":{
"Resource":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-type",
"Value":"calcert"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts",
"Value":[
"4",
"5",
"6"
]
}
]
}
],
"Action":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:action:action-id",
"Value":"read"
}
]
}
],
"AccessSubject":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts",
"Value":[
"1",
"2",
"3"
]
}
]
}
]
}
}
The request could also have been written as
{
"Request":{
"Resource":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-type",
"Value":"calcert"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts",
"Value":"4"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts",
"Value":"5"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts",
"Value":"6"
}
]
}
],
"Action":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:action:action-id",
"Value":"read"
}
]
}
],
"AccessSubject":[
{
"Attribute":[
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts",
"Value":"1"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts",
"Value":"2"
},
{
"AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts",
"Value":"3"
}
]
}
]
}
}
{
"Response":[
{
"Decision":"NotApplicable"
}
]
}
In your code, you use AuthZForce's PEP SDK. Cyril, in his other answer pointed out the main mistake:
AttributeBag<?> objectConflictsValue = Bags.singletonAttributeBag(StandardDatatypes.STRING, new StringValue(obj.getJSONObject("object").getString("conflicts")));
The issue is that you are adding the entire string representation of the conflicts ["1",...] inside a single value inside the XACML bag rather than adding each part individually.
In my example, I used my own Java JSON PEP SDK. The code looks like the following:
Either add each value one-by-one:
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "4"));
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "5"));
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "6"));
or add as an array of values:
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", new String[]{"4","5","6"}));
package io.xacml.pep.json.so;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.xacml.json.model.Attribute;
import io.xacml.json.model.Category;
import io.xacml.json.model.Request;
import io.xacml.json.model.Response;
import io.xacml.json.model.Result;
import io.xacml.pep.json.client.AuthZClient;
import io.xacml.pep.json.client.ClientConfiguration;
import io.xacml.pep.json.client.DefaultClientConfiguration;
import io.xacml.pep.json.client.jaxrs.JaxRsAuthZClient;
import lombok.extern.slf4j.Slf4j;
/**
* This class contains sample code using JAX-RS to invoke a Policy Decision Point.
* It supports both the JSON Profile of XACML 1.0 (where the response could be either an Object or
* an Array) and the JSON Profile of XACML 1.1 (where the response is always an array - to simplify
* things)
*
* @author djob
*/
@Slf4j
public class Example {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
ClientConfiguration clientConfiguration = DefaultClientConfiguration.builder()
.pdpUrl("http://djob-hp:8080")
.username("ads-user")
.password("secret")
.build();
// Add user attributes
Category subject = new Category();
subject.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts", "1"));
subject.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts", "2"));
subject.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:subject:subject-conflicts", "3"));
// Add action attributes - if any
Category action = new Category();
action.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:action:action-id", "read"));
// Add user attributes
Category resource = new Category();
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-type", "calcert"));
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "4"));
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "5"));
resource.addAttribute(new Attribute("urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts", "6"));
Request authZRequest = new Request();
authZRequest.addAccessSubjectCategory(subject);
authZRequest.addActionCategory(action);
authZRequest.addResourceCategory(resource);
AuthZClient authZClient = new JaxRsAuthZClient(clientConfiguration, mapper);
Response authZResponse = authZClient.makeAuthorizationRequest(authZRequest);
for (Result r : authZResponse.getResults()) {
log.debug(r.getDecision().name());
}
}
}
Upvotes: 1
Reputation: 3586
You need to fix at least 2 issues in your code:
Since you want a bag from a list of Strings, possibly more than one, you have to use the more generic Bags.newAttributeBag(...)
- second argument must be your actual List of Strings - instead of Bags.singletonAttributeBag()
. Only then will it be treated by the PDP as multiple AttributeValues of type String.
You are using the same subject attribute XACML_1_0_SUBJECT_CONFLICTS
twice, and second time is for objectConflicts attribute, so I guess this is wrong and needs to be fixed since this must be the resource attribute urn:oasis:names:tc:xacml:1.0:resource:resource-conflicts
instead.
Upvotes: 2