Monday, January 27, 2014

node.js + XACML

Following is one possible approach to carryout XACML authorization in node.js.

1.) Wrap WSO2 Balana PDP implementation to expose a simple interface to process incoming XACML requests.
package edu.purdue.cs.endtoendsoa;

public class AccessController {

 public String evaluate(String policy, String request) {
  PDP pdp = this.getPDPInstance(policy);
  return this.evaluate(request, pdp);
 }
 
 private String evaluate(String request, PDP pdp) {
  String res = pdp.evaluate(request);
  ByteArrayInputStream in = new ByteArrayInputStream(res.getBytes());
  OMElement elem = OMXMLBuilderFactory.createOMBuilder(in).getDocumentElement();
  
  OMElement tmp = elem.getFirstChildWithName(new QName("urn:oasis:names:tc:xacml:3.0:core:schema:wd-17", "Result"));
  tmp = tmp.getFirstChildWithName(new QName("urn:oasis:names:tc:xacml:3.0:core:schema:wd-17", "Decision"));
  return tmp.getText();
 }
 
 private PDP getPDPInstance(String policyLocation) {
    PolicyFinder finder= new PolicyFinder();
    Set policyLocations = new HashSet();
    policyLocations.add(policyLocation);
    FileBasedPolicyFinderModule testPolicyFinderModule = new FileBasedPolicyFinderModule(policyLocations);
    Set policyModules = new HashSet();
    policyModules.add(testPolicyFinderModule);
    finder.setModules(policyModules);

    Balana balana = Balana.getInstance();
    PDPConfig pdpConfig = balana.getPdpConfig();
    pdpConfig = new PDPConfig(pdpConfig.getAttributeFinder(), finder,
                              pdpConfig.getResourceFinder(), true);
    return new PDP(pdpConfig);
 }
}
The objective of this wrapper class is only to make things easy when integrating with node.js. Source code is available here: https://code.google.com/p/end-to-end-soa/source/browse/policy. When building this project with maven it collects all the dependencies into 'target/lib' directory.

2.) Import the above implementation into Javascript and try it out.
var java = require('java');
var xml2js = require('xml2js');
var fs = require('fs');


var jars_dir = process.cwd() + '/lib/';
java.classpath.push(jars_dir + "apache-mime4j-core-0.7.2.jar");
java.classpath.push(jars_dir + "balana-distribution-1.0.0-wso2v7.jar");
java.classpath.push(jars_dir + "geronimo-activation_1.1_spec-1.1.jar");
java.classpath.push(jars_dir + "jaxen-1.1.3.jar");
java.classpath.push(jars_dir + "wstx-asl-3.2.9.jar");
java.classpath.push(jars_dir + "axiom-api-1.2.13.jar");
java.classpath.push(jars_dir + "commons-io-1.3.2.jar");
java.classpath.push(jars_dir + "geronimo-javamail_1.4_spec-1.7.1.jar");
java.classpath.push(jars_dir + "junit-3.8.1.jar");
java.classpath.push(jars_dir + "axiom-impl-1.2.13.jar");
java.classpath.push(jars_dir + "commons-logging-1.1.1.jar");
java.classpath.push(jars_dir + "geronimo-stax-api_1.0_spec-1.0.1.jar");
java.classpath.push(jars_dir + "org.wso2.balana-1.0.0-wso2v7.jar");
java.classpath.push(jars_dir + "policy-1.0-SNAPSHOT.jar");

var req_text = fs.readFileSync('./req_1.json','utf8');

var req = JSON.parse(req_text);

//Update request template with values
for(var i in req.Request.Attributes) {
 var attr = req.Request.Attributes[i];
 var attr_id = attr.Attribute[0].$.AttributeId;
 if(attr_id == 'urn:oasis:names:tc:xacml:1.0:resource:resource-id') {
  attr.Attribute[0].AttributeValue[0]._='localhost:6102';
 } else if(attr_id == 'http://endtoendsoa.cs.purdue.edu/policy/service_uri') {
  attr.Attribute[0].AttributeValue[0]._='localhost:6101';
 } else if(attr_id == 'urn:oasis:names:tc:xacml:1.0:action:action-id') {
  attr.Attribute[0].AttributeValue[0]._='READ';
 } else if(attr_id == 'http://test.org/trust_level') {
  attr.Attribute[0].AttributeValue[0]._=10;
 }
}

//Convert populated request template to XML
var builder = new xml2js.Builder();
var req_xml = builder.buildObject(req);


var AccessController = java.import('edu.purdue.cs.endtoendsoa.AccessController');
var ac = new AccessController();
ac.evaluate(process.cwd() + '/policy_1.xml', req_xml, function(err, result) {
 if(err) {
  console.log(err);
 } else {
  console.log('Response from access controller : ' + result);
 }
});
Note that the a template was used in the above code to create a XACML request. This template was generated using a regular XML XACML request. The original XACML request was simply converted into JSON. This was loaded as a JSON object, which is very convenient to traverse in Javascript. The policy file used in the above example is available here. Request template is available here.