1

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.

The Chinese Wall Policy

ALFA Code

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
        }
    }
}

XACML policy

<?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>

Policy Enforcement Point (PEP) Code

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"
    }
}

Challenge

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.

rshah
  • 675
  • 2
  • 12
  • 32

2 Answers2

2

You need to fix at least 2 issues in your code:

  1. 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.

  2. 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.

David Brossard
  • 13,584
  • 6
  • 55
  • 88
cdan
  • 3,470
  • 13
  • 27
  • I am using `AttributeBag> principleConflictsValue = Bags.newAttributeBag(StandardDatatypes.STRING, conflicts);` where `conflicts` is a `Collection` from JSON where the JSON is `["1", "2", "3"]` and this still results in a DENY. Am I using the attribute bag correctly? Or is there a problem with the input from the JSON? – rshah Jul 06 '19 at 12:38
  • For the first issue, you have to use `Bags.newAttributeBag(...)` for all multi-valued attributes, i.e. for **both principleConflictsValue and objectConflictsValue** (maybe you missed the second one). **What about the second issue, have you fixed it?** I don't see it fixed in your question. Please edit your question accordingly or point us to full up-to-date code (e.g. on github) so that we can follow your changes/fixes, else it's very difficult to check and/or reproduce. – cdan Jul 08 '19 at 19:23
  • Alright. Which decision are you getting now with the fixed code? **Deny or NotApplicable?** You still say Deny in the OP, but NotApplicable in your answer to David Brossard. Just to make sure. And be careful with the **'first-applicable'** algorithm; in your policy you combine only one Rule with Deny effect, therefore you'll get either Deny (if Rule evaluates to Deny) or NotApplicable (by default) as final result, that's it (or Indeterminate in case of error but that's the edge case). More info in appendix C.8 of XACML spec. – cdan Jul 09 '19 at 22:14
  • Sorry for the confusion, I am currently getting NotApplicable. – rshah Jul 10 '19 at 06:06
  • In this case, I'm sorry to say that... **it works as expected!**. Indeed, with such request and policy, the Policy's Target is a *Match* (`calcert` resource type), but: the Rule's Target is a *Match* (`read` action) and Condition is False (`subject-conflicts` and `resource-conflicts` have no string in common), so the Rule is NotApplicable (cf. XACML §7.11), and so the `first-applicable` algorithm results in `NotApplicable` (c.f XACML §C.8). If you expect something else, you should change the combining algorithm (e.g. `permit-unless-deny` if you want Permit instead). – cdan Jul 11 '19 at 21:04
1

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:

Sample JSON Request

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"
               }
            ]
         }
      ]
   }
}

Sample XACML JSON Response

{
   "Response":[
      {
         "Decision":"NotApplicable"
      }
   ]
}

Finding the right SDK for requests / responses

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"}));

Full code

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());
        }
    }
}
David Brossard
  • 13,584
  • 6
  • 55
  • 88
  • Thanks for the detailed answer! However, with the code that I have (so no major changes need to be made), how can I add the list of conflicts as attributes using the bag? If you see my comment on Cyrils answer, I retrieve the value when I add the list to the bag as follows: `Bag(elementType='http://www.w3.org/2001/XMLSchema#string', elements=[1, 2, 10], causeForEmpty=null)`. But I am still getting a `DENY/NOT_APPLICABLE` – rshah Jul 07 '19 at 12:17
  • Are you getting deny or not applicable? – David Brossard Jul 07 '19 at 13:59
  • Not applicable. – rshah Jul 08 '19 at 05:58