Policy XACML - Custom Application Tutorial

This tutorial shows how to build a XACML application for a Policy Type. Please be sure to clone the policy repositories before going through the tutorial. See Policy Platform Development Tools for details.

Design a Policy Type

Follow TOSCA Policy Primer for more information. For the tutorial, we will use this example Policy Type in which an ONAP PEP client would like to enforce an action authorize for a user to execute a permission on an entity. See here for latest Tutorial Policy Type.

Example Tutorial Policy Type
 1tosca_definitions_version: tosca_simple_yaml_1_1_0
 2policy_types:
 3    onap.policies.Authorization:
 4        derived_from: tosca.policies.Root
 5        version: 1.0.0
 6        description: Example tutorial policy type for doing user authorization
 7        properties:
 8            user:
 9                type: string
10                required: true
11                description: The unique user name
12            permissions:
13                type: list
14                required: true
15                description: A list of resource permissions
16                entry_schema:
17                    type: onap.datatypes.Tutorial
18data_types:
19    onap.datatypes.Tutorial:
20        derived_from: tosca.datatypes.Root
21        version: 1.0.0
22        properties:
23            entity:
24                type: string
25                required: true
26                description: The resource
27            permission:
28                type: string
29                required: true
30                description: The permission level
31                constraints:
32                    - valid_values: [read, write, delete]

We would expect then to be able to create the following policies to allow the demo user to Read/Write an entity called foo, while the audit user can only read the entity called foo. Neither user has Delete permission. See here for latest Tutorial Policies.

Example Policies Derived From Tutorial Policy Type
 1tosca_definitions_version: tosca_simple_yaml_1_1_0
 2topology_template:
 3    policies:
 4        -
 5            onap.policy.tutorial.demo:
 6                type: onap.policies.Authorization
 7                type_version: 1.0.0
 8                version: 1.0.0
 9                metadata:
10                    policy-id: onap.policy.tutorial.demo
11                    policy-version: 1
12                properties:
13                    user: demo
14                    permissions:
15                        -
16                            entity: foo
17                            permission: read
18                        -
19                            entity: foo
20                            permission: write
21        -
22            onap.policy.tutorial.audit:
23                type: onap.policies.Authorization
24                version: 1.0.0
25                type_version: 1.0.0
26                metadata:
27                    policy-id: onap.policy.tutorial.bar
28                    policy-version: 1
29                properties:
30                    user: audit
31                    permissions:
32                        -
33                            entity: foo
34                            permission: read

Design Decision Request and expected Decision Response

For the PEP (Policy Enforcement Point) client applications that call the Decision API, you need to design how the Decision API Request resource fields will be sent via the PEP.

Example Decision Request
 1{
 2  "ONAPName": "TutorialPEP",
 3  "ONAPComponent": "TutorialPEPComponent",
 4  "ONAPInstance": "TutorialPEPInstance",
 5  "requestId": "unique-request-id-tutorial",
 6  "action": "authorize",
 7  "resource": {
 8    "user": "demo",
 9    "entity": "foo",
10    "permission" : "write"
11  }
12}

For simplicity, this tutorial expects only a Permit or Deny in the Decision Response. However, one could customize the Decision Response object and send back whatever information is desired.

Example Decision Response
1{
2    "status":"Permit"
3}

Create A Maven Project

Use whatever tool or environment to create your application project. This tutorial assumes you use Maven to build it.

Add Dependencies Into Application pom.xml

Here we import the XACML PDP Application common dependency which has the interfaces we need to implement. In addition, we are importing a testing dependency that has common code for producing a JUnit test.

pom.xml dependencies
  <dependency>
    <groupId>org.onap.policy.xacml-pdp.applications</groupId>
    <artifactId>common</artifactId>
    <version>2.3.3</version>
  </dependency>
  <dependency>
    <groupId>org.onap.policy.xacml-pdp</groupId>
    <artifactId>xacml-test</artifactId>
    <version>2.3.3</version>
    <scope>test</scope>
  </dependency>

Create META-INF to expose Java Service

The ONAP XACML PDP Engine will not be able to find the tutorial application unless it has a property file located in src/main/resources/META-INF/services that contains a property file declaring the class that implements the service.

The name of the file must match org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider and the contents of the file is one line org.onap.policy.tutorial.tutorial.TutorialApplication.

META-INF/services/org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider
  org.onap.policy.tutorial.tutorial.TutorialApplication

Create A Java Class That Extends StdXacmlApplicationServiceProvider

You could implement XacmlApplicationServiceProvider if you wish, but for simplicity if you just extend StdXacmlApplicationServiceProvider you will get a lot of implementation done for your application up front. All that needs to be implemented is providing a custom translator.

Custom Tutorial Application Service Provider
package org.onap.policy.tutorial.tutorial;

import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
import org.onap.policy.pdp.xacml.application.common.std.StdXacmlApplicationServiceProvider;

public class TutorialApplication extends StdXacmlApplicationServiceProvider {

      @Override
      protected ToscaPolicyTranslator getTranslator(String type) {
              // TODO Auto-generated method stub
              return null;
      }

}

Override Methods for Tutorial

Override these methods to differentiate Tutorial from other applications so that the XACML PDP Engine can determine how to route policy types and policies to the application.

Custom Tutorial Application Service Provider
package org.onap.policy.tutorial.tutorial;

import java.util.Arrays;
import java.util.List;

import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
import org.onap.policy.pdp.xacml.application.common.std.StdXacmlApplicationServiceProvider;

public class TutorialApplication extends StdXacmlApplicationServiceProvider {

  private final ToscaPolicyTypeIdentifier supportedPolicyType = new ToscaPolicyTypeIdentifier();

  @Override
  public String applicationName() {
      return "tutorial";
  }

  @Override
  public List<String> actionDecisionsSupported() {
      return Arrays.asList("authorize");
  }

  @Override
  public synchronized List<ToscaPolicyTypeIdentifier> supportedPolicyTypes() {
      return Arrays.asList(supportedPolicyType);
  }

  @Override
  public boolean canSupportPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
      return supportedPolicyType.equals(policyTypeId);
  }

  @Override
      protected ToscaPolicyTranslator getTranslator(String type) {
      // TODO Auto-generated method stub
      return null;
  }

}

Create A Translation Class that extends the ToscaPolicyTranslator Class

Please be sure to review the existing translators in the policy/xacml-pdp repo to see if they could be re-used for your policy type. For the tutorial, we will create our own translator.

The custom translator is not only responsible for translating Policies derived from the Tutorial Policy Type, but also for translating Decision API Requests/Responses to/from the appropriate XACML requests/response objects the XACML engine understands.

Custom Tutorial Translator Class
package org.onap.policy.tutorial.tutorial;

import org.onap.policy.models.decisions.concepts.DecisionRequest;
import org.onap.policy.models.decisions.concepts.DecisionResponse;
import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;

import com.att.research.xacml.api.Request;
import com.att.research.xacml.api.Response;

import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;

public class TutorialTranslator implements ToscaPolicyTranslator {

  public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
      // TODO Auto-generated method stub
      return null;
  }

  public Request convertRequest(DecisionRequest request) {
      // TODO Auto-generated method stub
      return null;
  }

  public DecisionResponse convertResponse(Response xacmlResponse) {
      // TODO Auto-generated method stub
      return null;
  }

}

Implement the TutorialTranslator Methods

This is the part where knowledge of the XACML OASIS 3.0 specification is required. Please refer to that specification on the many ways to design a XACML Policy.

For the tutorial, we will build code that translates the TOSCA Policy into one XACML Policy that matches on the user and action. It will then have one or more rules for each entity and permission combination. The default combining algorithm for the XACML Rules are to “Deny Unless Permit”.

See the tutorial example for details on how the translator is implemented. Note that in the Tutorial Translator, it also shows how a developer could extend the translator to return or act upon obligations, advice and attributes.

Note

There are many ways to build the policy based on the attributes. How to do so is a matter of experience and fine tuning using the many options for combining algorithms, target and/or condition matching and the rich set of functions available.

Use the TutorialTranslator in the TutorialApplication

Be sure to go back to the TutorialApplication and create an instance of the translator to return to the StdXacmlApplicationServiceProvider. The StdXacmlApplicationServiceProvider uses the translator to convert a policy when a new policy is deployed to the ONAP XACML PDP Engine. See the Tutorial Application Example.

Final TutorialApplication Class
 1  package org.onap.policy.tutorial.tutorial;
 2
 3  import java.util.Arrays;
 4  import java.util.List;
 5  import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 6  import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
 7  import org.onap.policy.pdp.xacml.application.common.std.StdXacmlApplicationServiceProvider;
 8
 9  public class TutorialApplication extends StdXacmlApplicationServiceProvider {
10
11      private final ToscaPolicyTypeIdentifier supportedPolicyType =
12              new ToscaPolicyTypeIdentifier("onap.policies.Authorization", "1.0.0");
13      private final TutorialTranslator translator = new TutorialTranslator();
14
15      @Override
16      public String applicationName() {
17          return "tutorial";
18      }
19
20      @Override
21      public List<String> actionDecisionsSupported() {
22          return Arrays.asList("authorize");
23      }
24
25      @Override
26      public synchronized List<ToscaPolicyTypeIdentifier> supportedPolicyTypes() {
27          return Arrays.asList(supportedPolicyType);
28      }
29
30      @Override
31      public boolean canSupportPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
32          return supportedPolicyType.equals(policyTypeId);
33      }
34
35      @Override
36      protected ToscaPolicyTranslator getTranslator(String type) {
37          return translator;
38      }
39
40  }

Create a XACML Request from ONAP Decision Request

The easiest way to do this is to use the annotations feature from XACML PDP library to create an example XACML request. Then create an instance and simply populate it from an incoming ONAP Decision Request.

Final TutorialApplication Class
   import com.att.research.xacml.std.annotations.XACMLAction;
   import com.att.research.xacml.std.annotations.XACMLRequest;
   import com.att.research.xacml.std.annotations.XACMLResource;
   import com.att.research.xacml.std.annotations.XACMLSubject;
   import java.util.Map;
   import java.util.Map.Entry;
   import lombok.Getter;
   import lombok.Setter;
   import lombok.ToString;
   import org.onap.policy.models.decisions.concepts.DecisionRequest;

   @Getter
   @Setter
   @ToString
   @XACMLRequest(ReturnPolicyIdList = true)
   public class TutorialRequest {
       @XACMLSubject(includeInResults = true)
       private String onapName;

       @XACMLSubject(attributeId = "urn:org:onap:onap-component", includeInResults = true)
       private String onapComponent;

       @XACMLSubject(attributeId = "urn:org:onap:onap-instance", includeInResults = true)
       private String onapInstance;

       @XACMLAction()
       private String action;

       @XACMLResource(attributeId = "urn:org:onap:tutorial-user", includeInResults = true)
       private String user;

       @XACMLResource(attributeId = "urn:org:onap:tutorial-entity", includeInResults = true)
       private String entity;

       @XACMLResource(attributeId = "urn:org:onap:tutorial-permission", includeInResults = true)
       private String permission;

       /**
        * createRequest.
        *
        * @param decisionRequest Incoming
        * @return TutorialRequest object
        */
       public static TutorialRequest createRequest(DecisionRequest decisionRequest) {
           //
           // Create our object
           //
           TutorialRequest request = new TutorialRequest();
           //
           // Add the subject attributes
           //
           request.onapName = decisionRequest.getOnapName();
           request.onapComponent = decisionRequest.getOnapComponent();
           request.onapInstance = decisionRequest.getOnapInstance();
           //
           // Add the action attribute
           //
           request.action = decisionRequest.getAction();
           //
           // Add the resource attributes
           //
           Map<String, Object> resources = decisionRequest.getResource();
           for (Entry<String, Object> entrySet : resources.entrySet()) {
               if ("user".equals(entrySet.getKey())) {
                   request.user = entrySet.getValue().toString();
               }
               if ("entity".equals(entrySet.getKey())) {
                   request.entity = entrySet.getValue().toString();
               }
               if ("permission".equals(entrySet.getKey())) {
                   request.permission = entrySet.getValue().toString();
               }
           }

           return request;
       }
   }

See the Tutorial Request

Create a JUnit and use the TestUtils.java class in xacml-test dependency

Be sure to create a JUnit that will test your translator and application code. You can utilize a TestUtils.java class from the policy/xamcl-pdp repo’s xacml-test submodule to use some utility methods for building the JUnit test.

Build the code and run the JUnit test. Its easiest to run it via a terminal command line using maven commands.

Running Maven Commands
1> mvn clean install

Download Tutorial Application Example

If you clone the XACML-PDP repo, the tutorial is included for local testing without building your own.

Tutorial code located in xacml-pdp repo

There are instructions on the repo to run the Policy Framework components locally and test the tutorial out using Docker.

Docker Instructions

In addition, there is a POSTMAN collection available for setting up and running tests against a running instance of ONAP Policy Components (api, pap, dmaap-simulator, tutorial-xacml-pdp).

POSTMAN collection for testing