XACML Policy Engine in Java: Complete Implementation Guide

Introduction to XACML

XACML (eXtensible Access Control Markup Language) is an OASIS standard for attribute-based access control (ABAC). It provides a fine-grained, flexible authorization framework that evaluates access requests based on attributes of the subject, resource, action, and environment.

Key XACML Concepts

Core Components

  • Policy Decision Point (PDP) - Evaluates policies and renders decisions
  • Policy Administration Point (PAP) - Manages policy storage and creation
  • Policy Information Point (PIP) - Retrieves attribute values
  • Policy Enforcement Point (PEP) - Intercepts requests and enforces decisions

Decision Types

  • Permit - Access granted
  • Deny - Access denied
  • NotApplicable - No policy applies
  • Indeterminate - Evaluation error occurred

Dependencies and Setup

Maven Configuration

<properties>
<balana.version>1.1.0</balana.version>
</properties>
<dependencies>
<!-- Balana - WSO2 XACML Implementation -->
<dependency>
<groupId>org.wso2.balana</groupId>
<artifactId>balana</artifactId>
<version>${balana.version}</version>
</dependency>
<!-- Alternative: AuthzForce -->
<dependency>
<groupId>org.ow2.authzforce</groupId>
<artifactId>authzforce-ce-core-pdp-engine</artifactId>
<version>16.0.0</version>
</dependency>
<!-- XML Processing -->
<dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
<version>2.3.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
</dependencies>

Core XACML Engine Implementation

Basic PDP Engine

package com.xacml.engine;
import org.wso2.balana.*;
import org.wso2.balana.finder.*;
import org.wso2.balana.finder.impl.*;
import org.wso2.balana.PDP;
import org.wso2.balana.PDPConfig;
import org.wso2.balana.ctx.*;
import org.wso2.balana.ctx.xacml3.RequestCtx;
import org.wso2.balana.ctx.xacml3.ResponseCtx;
import org.wso2.balana.xacml3.*;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.util.*;
public class BasicPDPEngine {
private PDP pdp;
private PolicyFinder policyFinder;
private PolicyFinderModule policyFinderModule;
public BasicPDPEngine() {
initializePDP();
}
private void initializePDP() {
try {
// Create policy finder
policyFinder = new PolicyFinder();
policyFinderModule = new FileBasedPolicyFinderModule();
Set<PolicyFinderModule> finderModules = new HashSet<>();
finderModules.add(policyFinderModule);
policyFinder.setModules(finderModules);
// Create attribute finder
AttributeFinder attributeFinder = new AttributeFinder();
Set<AttributeFinderModule> attributeFinderModules = new HashSet<>();
attributeFinderModules.add(new CurrentEnvModule());
attributeFinder.setModules(attributeFinderModules);
// Create resource finder
ResourceFinder resourceFinder = new ResourceFinder();
Set<ResourceFinderModule> resourceFinderModules = new HashSet<>();
resourceFinder.setModules(resourceFinderModules);
// Create PDP configuration
PDPConfig pdpConfig = new PDPConfig(attributeFinder, policyFinder, resourceFinder);
// Create PDP
pdp = new PDP(pdpConfig);
} catch (Exception e) {
throw new RuntimeException("Failed to initialize PDP engine", e);
}
}
/**
* Evaluate XACML request
*/
public ResponseCtx evaluateRequest(String xacmlRequest) throws Exception {
RequestCtx request = RequestCtx.getInstance(new ByteArrayInputStream(xacmlRequest.getBytes()));
return pdp.evaluate(request);
}
/**
* Evaluate access request with simplified parameters
*/
public Decision evaluateAccess(String subjectId, String subjectRole, 
String resourceId, String action, 
Map<String, String> environment) throws Exception {
String xacmlRequest = createXACMLRequest(subjectId, subjectRole, resourceId, action, environment);
ResponseCtx response = evaluateRequest(xacmlRequest);
return extractDecision(response);
}
private String createXACMLRequest(String subjectId, String subjectRole, 
String resourceId, String action, 
Map<String, String> environment) {
StringBuilder request = new StringBuilder();
request.append("<Request xmlns=\"urn:oasis:names:tc:xacml:3.0:core:schema:wd-17\" ")
.append("CombinedDecision=\"false\" ReturnPolicyIdList=\"false\">\n")
.append("  <Attributes Category=\"urn:oasis:names:tc:xacml:1.0:subject-category:access-subject\">\n")
.append("    <Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:subject:subject-id\" IncludeInResult=\"false\">\n")
.append("      <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">").append(subjectId).append("</AttributeValue>\n")
.append("    </Attribute>\n")
.append("    <Attribute AttributeId=\"urn:oasis:names:tc:xacml:2.0:subject:role\" IncludeInResult=\"false\">\n")
.append("      <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">").append(subjectRole).append("</AttributeValue>\n")
.append("    </Attribute>\n")
.append("  </Attributes>\n")
.append("  <Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:resource\">\n")
.append("    <Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\" IncludeInResult=\"false\">\n")
.append("      <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">").append(resourceId).append("</AttributeValue>\n")
.append("    </Attribute>\n")
.append("  </Attributes>\n")
.append("  <Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:action\">\n")
.append("    <Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\" IncludeInResult=\"false\">\n")
.append("      <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">").append(action).append("</AttributeValue>\n")
.append("    </Attribute>\n")
.append("  </Attributes>\n");
if (environment != null && !environment.isEmpty()) {
request.append("  <Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:environment\">\n");
for (Map.Entry<String, String> entry : environment.entrySet()) {
request.append("    <Attribute AttributeId=\"").append(entry.getKey()).append("\" IncludeInResult=\"false\">\n")
.append("      <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">").append(entry.getValue()).append("</AttributeValue>\n")
.append("    </Attribute>\n");
}
request.append("  </Attributes>\n");
}
request.append("</Request>");
return request.toString();
}
private Decision extractDecision(ResponseCtx response) {
if (response.getResults().length == 0) {
return Decision.INDETERMINATE;
}
Result result = response.getResults()[0];
return result.getDecision();
}
/**
* Load policy from string
*/
public void loadPolicy(String policyXml) throws Exception {
Policy policy = Policy.getInstance(new StringReader(policyXml));
((FileBasedPolicyFinderModule) policyFinderModule).loadPolicy(policy);
}
/**
* Load policy from file
*/
public void loadPolicyFromFile(String filePath) throws Exception {
((FileBasedPolicyFinderModule) policyFinderModule).loadPolicy(filePath);
}
}

Policy Management

Policy Administration Point (PAP)

package com.xacml.pap;
import org.wso2.balana.Policy;
import org.wso2.balana.PolicySet;
import org.wso2.balana.combine.PolicyCombiningAlgorithm;
import org.wso2.balana.combine.xacml3.DenyOverridesPolicyAlg;
import java.io.StringReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class PolicyAdministrationPoint {
private Map<String, Policy> policies = new ConcurrentHashMap<>();
private Map<String, PolicySet> policySets = new ConcurrentHashMap<>();
private PolicyCombiningAlgorithm combiningAlgorithm = new DenyOverridesPolicyAlg();
/**
* Add a policy to the repository
*/
public void addPolicy(String policyId, String policyXml) throws Exception {
Policy policy = Policy.getInstance(new StringReader(policyXml));
policies.put(policyId, policy);
}
/**
* Remove a policy from the repository
*/
public void removePolicy(String policyId) {
policies.remove(policyId);
}
/**
* Create a policy set from multiple policies
*/
public void createPolicySet(String policySetId, List<String> policyIds, 
String combiningAlgorithmId) throws Exception {
List<Policy> policyList = new ArrayList<>();
for (String policyId : policyIds) {
Policy policy = policies.get(policyId);
if (policy != null) {
policyList.add(policy);
}
}
PolicySet policySet = new PolicySet(policySetId, combiningAlgorithm, 
null, policyList, null, null, null);
policySets.put(policySetId, policySet);
}
/**
* Get policy by ID
*/
public Policy getPolicy(String policyId) {
return policies.get(policyId);
}
/**
* Get all policies
*/
public Collection<Policy> getAllPolicies() {
return policies.values();
}
/**
* Validate policy syntax
*/
public boolean validatePolicy(String policyXml) {
try {
Policy.getInstance(new StringReader(policyXml));
return true;
} catch (Exception e) {
return false;
}
}
/**
* Export policies as XML
*/
public String exportPolicies() {
StringBuilder sb = new StringBuilder();
sb.append("<PolicyRepository>\n");
for (Policy policy : policies.values()) {
sb.append(policy.encode()).append("\n");
}
sb.append("</PolicyRepository>");
return sb.toString();
}
}

Policy Information Point (PIP)

package com.xacml.pip;
import org.wso2.balana.attr.*;
import org.wso2.balana.ctx.EvaluationCtx;
import org.wso2.balana.finder.AttributeFinderModule;
import java.net.URI;
import java.util.*;
public class CustomAttributeFinder extends AttributeFinderModule {
private Map<String, Map<String, String>> userAttributes = new HashMap<>();
private Map<String, Map<String, String>> resourceAttributes = new HashMap<>();
public CustomAttributeFinder() {
initializeSampleData();
}
private void initializeSampleData() {
// Sample user attributes
Map<String, String> user1Attrs = new HashMap<>();
user1Attrs.put("department", "HR");
user1Attrs.put("clearance", "high");
user1Attrs.put("location", "US");
userAttributes.put("user1", user1Attrs);
Map<String, String> user2Attrs = new HashMap<>();
user2Attrs.put("department", "Engineering");
user2Attrs.put("clearance", "medium");
user2Attrs.put("location", "EU");
userAttributes.put("user2", user2Attrs);
// Sample resource attributes
Map<String, String> resource1Attrs = new HashMap<>();
resource1Attrs.put("sensitivity", "confidential");
resource1Attrs.put("owner", "HR");
resourceAttributes.put("document1", resource1Attrs);
}
@Override
public Set<String> getSupportedCategories() {
Set<String> categories = new HashSet<>();
categories.add("urn:oasis:names:tc:xacml:1.0:subject-category:access-subject");
categories.add("urn:oasis:names:tc:xacml:3.0:attribute-category:resource");
categories.add("urn:oasis:names:tc:xacml:3.0:attribute-category:environment");
return categories;
}
@Override
public Set<String> getSupportedIds() {
Set<String> ids = new HashSet<>();
ids.add("urn:oasis:names:tc:xacml:1.0:subject:subject-id");
ids.add("urn:oasis:names:tc:xacml:2.0:subject:role");
ids.add("urn:oasis:names:tc:xacml:1.0:subject:department");
ids.add("urn:oasis:names:tc:xacml:1.0:subject:clearance");
ids.add("urn:oasis:names:tc:xacml:1.0:resource:resource-id");
ids.add("urn:oasis:names:tc:xacml:1.0:resource:sensitivity");
ids.add("urn:oasis:names:tc:xacml:1.0:resource:owner");
ids.add("urn:oasis:names:tc:xacml:1.0:environment:current-time");
ids.add("urn:oasis:names:tc:xacml:1.0:environment:current-date");
return ids;
}
@Override
public AttributeValue findAttribute(URI category, URI attributeType, 
URI attributeId, String issuer, 
EvaluationCtx context) {
String categoryStr = category.toString();
String attributeIdStr = attributeId.toString();
try {
if (categoryStr.equals("urn:oasis:names:tc:xacml:1.0:subject-category:access-subject")) {
return findSubjectAttribute(attributeIdStr, context);
} else if (categoryStr.equals("urn:oasis:names:tc:xacml:3.0:attribute-category:resource")) {
return findResourceAttribute(attributeIdStr, context);
} else if (categoryStr.equals("urn:oasis:names:tc:xacml:3.0:attribute-category:environment")) {
return findEnvironmentAttribute(attributeIdStr, context);
}
} catch (Exception e) {
// Log error and return null
}
return null;
}
private AttributeValue findSubjectAttribute(String attributeId, EvaluationCtx context) {
// Extract subject ID from context
String subjectId = extractSubjectId(context);
if (subjectId == null) return null;
Map<String, String> attrs = userAttributes.get(subjectId);
if (attrs == null) return null;
String value = attrs.get(getSimpleAttributeName(attributeId));
if (value != null) {
return StringAttribute.getInstance(value);
}
return null;
}
private AttributeValue findResourceAttribute(String attributeId, EvaluationCtx context) {
// Extract resource ID from context
String resourceId = extractResourceId(context);
if (resourceId == null) return null;
Map<String, String> attrs = resourceAttributes.get(resourceId);
if (attrs == null) return null;
String value = attrs.get(getSimpleAttributeName(attributeId));
if (value != null) {
return StringAttribute.getInstance(value);
}
return null;
}
private AttributeValue findEnvironmentAttribute(String attributeId, EvaluationCtx context) {
switch (attributeId) {
case "urn:oasis:names:tc:xacml:1.0:environment:current-time":
return StringAttribute.getInstance(new Date().toString());
case "urn:oasis:names:tc:xacml:1.0:environment:current-date":
return StringAttribute.getInstance(java.time.LocalDate.now().toString());
default:
return null;
}
}
private String extractSubjectId(EvaluationCtx context) {
// Implementation to extract subject ID from evaluation context
// This is a simplified version
return "user1"; // Placeholder
}
private String extractResourceId(EvaluationCtx context) {
// Implementation to extract resource ID from evaluation context
// This is a simplified version
return "document1"; // Placeholder
}
private String getSimpleAttributeName(String fullAttributeId) {
// Convert full attribute ID to simple name
if (fullAttributeId.contains(":")) {
return fullAttributeId.substring(fullAttributeId.lastIndexOf(":") + 1);
}
return fullAttributeId;
}
// Methods to manage attribute data
public void addUserAttribute(String userId, String attributeName, String attributeValue) {
userAttributes.computeIfAbsent(userId, k -> new HashMap<>())
.put(attributeName, attributeValue);
}
public void addResourceAttribute(String resourceId, String attributeName, String attributeValue) {
resourceAttributes.computeIfAbsent(resourceId, k -> new HashMap<>())
.put(attributeName, attributeValue);
}
}

Sample Policies

Role-Based Access Policy

public class SamplePolicies {
public static final String ROLE_BASED_POLICY = 
"<Policy xmlns=\"urn:oasis:names:tc:xacml:3.0:core:schema:wd-17\" " +
"PolicyId=\"role-based-policy\" RuleCombiningAlgId=\"urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides\">\n" +
"  <Target>\n" +
"    <AnyOf>\n" +
"      <AllOf>\n" +
"        <Match MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n" +
"          <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">document</AttributeValue>\n" +
"          <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=\"true\"/>\n" +
"        </Match>\n" +
"      </AllOf>\n" +
"    </AnyOf>\n" +
"  </Target>\n" +
"  <Rule Effect=\"Permit\" RuleId=\"admin-rule\">\n" +
"    <Target>\n" +
"      <AnyOf>\n" +
"        <AllOf>\n" +
"          <Match MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n" +
"            <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">admin</AttributeValue>\n" +
"            <AttributeDesignator AttributeId=\"urn:oasis:names:tc:xacml:2.0:subject:role\" " +
"Category=\"urn:oasis:names:tc:xacml:1.0:subject-category:access-subject\" DataType=\"http://www.w3.org/2001/XMLSchema#string\" MustBePresent=\"true\"/>\n" +
"          </Match>\n" +
"        </AllOf>\n" +
"      </AnyOf>\n" +
"    </Target>\n" +
"  </Rule>\n" +
"  <Rule Effect=\"Permit\" RuleId=\"user-rule\">\n" +
"    <Target>\n" +
"      <AnyOf>\n" +
"        <AllOf>\n" +
"          <Match MatchId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n" +
"            <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">user</AttributeValue>\n" +
"            <AttributeDesignator AttributeId=\"urn:oasis:names:tc:xacml:2.0:subject:role\" " +
"Category=\"urn:oasis:names:tc:xacml:1.0:subject-category:access-subject\" DataType=\"http://www.w3.org/2001/XMLSchema#string\" MustBePresent=\"true\"/>\n" +
"          </Match>\n" +
"        </AllOf>\n" +
"      </AnyOf>\n" +
"    </Target>\n" +
"    <Condition>\n" +
"      <Apply FunctionId=\"urn:oasis:names:tc:xacml:1.0:function:string-equal\">\n" +
"        <Apply FunctionId=\"urn:oasis:names:tc:xacml:1.0:function:string-one-and-only\">\n" +
"          <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=\"true\"/>\n" +
"        </Apply>\n" +
"        <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">read</AttributeValue>\n" +
"      </Apply>\n" +
"    </Condition>\n" +
"  </Rule>\n" +
"  <Rule Effect=\"Deny\" RuleId=\"deny-rule\"/>\n" +
"</Policy>";
public static final String TIME_BASED_POLICY = 
"<Policy xmlns=\"urn:oasis:names:tc:xacml:3.0:core:schema:wd-17\" " +
"PolicyId=\"time-based-policy\" RuleCombiningAlgId=\"urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides\">\n" +
"  <Rule Effect=\"Permit\" RuleId=\"business-hours-rule\">\n" +
"    <Condition>\n" +
"      <Apply FunctionId=\"urn:oasis:names:tc:xacml:1.0:function:and\">\n" +
"        <Apply FunctionId=\"urn:oasis:names:tc:xacml:2.0:function:time-greater-than-or-equal\">\n" +
"          <Apply FunctionId=\"urn:oasis:names:tc:xacml:1.0:function:time-one-and-only\">\n" +
"            <AttributeDesignator AttributeId=\"urn:oasis:names:tc:xacml:1.0:environment:current-time\" " +
"Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:environment\" DataType=\"http://www.w3.org/2001/XMLSchema#time\" MustBePresent=\"true\"/>\n" +
"          </Apply>\n" +
"          <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#time\">09:00:00</AttributeValue>\n" +
"        </Apply>\n" +
"        <Apply FunctionId=\"urn:oasis:names:tc:xacml:2.0:function:time-less-than-or-equal\">\n" +
"          <Apply FunctionId=\"urn:oasis:names:tc:xacml:1.0:function:time-one-and-only\">\n" +
"            <AttributeDesignator AttributeId=\"urn:oasis:names:tc:xacml:1.0:environment:current-time\" " +
"Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:environment\" DataType=\"http://www.w3.org/2001/XMLSchema#time\" MustBePresent=\"true\"/>\n" +
"          </Apply>\n" +
"          <AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#time\">17:00:00</AttributeValue>\n" +
"        </Apply>\n" +
"      </Apply>\n" +
"    </Condition>\n" +
"  </Rule>\n" +
"  <Rule Effect=\"Deny\" RuleId=\"deny-after-hours\"/>\n" +
"</Policy>";
}

Spring Boot Integration

Configuration Class

package com.xacml.config;
import com.xacml.engine.BasicPDPEngine;
import com.xacml.pap.PolicyAdministrationPoint;
import com.xacml.pip.CustomAttributeFinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XACMLConfig {
@Bean
public BasicPDPEngine pdpEngine() {
return new BasicPDPEngine();
}
@Bean
public PolicyAdministrationPoint policyAdministrationPoint() {
return new PolicyAdministrationPoint();
}
@Bean
public CustomAttributeFinder attributeFinder() {
return new CustomAttributeFinder();
}
}

REST Controller

package com.xacml.controller;
import com.xacml.engine.BasicPDPEngine;
import com.xacml.pap.PolicyAdministrationPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.wso2.balana.ctx.xacml3.ResponseCtx;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/xacml")
public class XACMLController {
@Autowired
private BasicPDPEngine pdpEngine;
@Autowired
private PolicyAdministrationPoint pap;
@PostMapping("/evaluate")
public ResponseEntity<Map<String, Object>> evaluateRequest(@RequestBody AccessRequest request) {
Map<String, Object> response = new HashMap<>();
try {
ResponseCtx result = pdpEngine.evaluateRequest(request.getXacmlRequest());
response.put("decision", result.getResults()[0].getDecision().toString());
response.put("success", true);
// Add additional response details
Map<String, String> details = new HashMap<>();
details.put("policyId", result.getResults()[0].getPolicyIdentifier().toString());
response.put("details", details);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/policy")
public ResponseEntity<Map<String, Object>> addPolicy(@RequestBody PolicyRequest policyRequest) {
Map<String, Object> response = new HashMap<>();
try {
pap.addPolicy(policyRequest.getPolicyId(), policyRequest.getPolicyXml());
response.put("success", true);
response.put("message", "Policy added successfully");
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/policy/{policyId}")
public ResponseEntity<Map<String, Object>> getPolicy(@PathVariable String policyId) {
Map<String, Object> response = new HashMap<>();
try {
org.wso2.balana.Policy policy = pap.getPolicy(policyId);
if (policy != null) {
response.put("success", true);
response.put("policy", policy.encode());
} else {
response.put("success", false);
response.put("error", "Policy not found");
}
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
// Request DTOs
public static class AccessRequest {
private String xacmlRequest;
public String getXacmlRequest() { return xacmlRequest; }
public void setXacmlRequest(String xacmlRequest) { this.xacmlRequest = xacmlRequest; }
}
public static class PolicyRequest {
private String policyId;
private String policyXml;
public String getPolicyId() { return policyId; }
public void setPolicyId(String policyId) { this.policyId = policyId; }
public String getPolicyXml() { return policyXml; }
public void setPolicyXml(String policyXml) { this.policyXml = policyXml; }
}
}

Advanced Features

Policy Validation Service

package com.xacml.validation;
import org.wso2.balana.Policy;
import org.wso2.balana.PolicySet;
import org.wso2.balana.parser.PolicyBuilder;
import org.wso2.balana.parser.PolicySetBuilder;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PolicyValidationService {
/**
* Validate XACML policy syntax
*/
public ValidationResult validatePolicySyntax(String policyXml) {
try {
Policy policy = Policy.getInstance(new ByteArrayInputStream(policyXml.getBytes()));
return new ValidationResult(true, "Policy syntax is valid", policy.getVersion());
} catch (Exception e) {
return new ValidationResult(false, "Invalid policy syntax: " + e.getMessage(), null);
}
}
/**
* Validate policy set syntax
*/
public ValidationResult validatePolicySetSyntax(String policySetXml) {
try {
PolicySet policySet = PolicySet.getInstance(new ByteArrayInputStream(policySetXml.getBytes()));
return new ValidationResult(true, "PolicySet syntax is valid", policySet.getVersion());
} catch (Exception e) {
return new ValidationResult(false, "Invalid PolicySet syntax: " + e.getMessage(), null);
}
}
/**
* Check for policy conflicts
*/
public List<Conflict> detectConflicts(List<Policy> policies) {
List<Conflict> conflicts = new ArrayList<>();
// Simple conflict detection based on target overlaps
for (int i = 0; i < policies.size(); i++) {
for (int j = i + 1; j < policies.size(); j++) {
if (policiesOverlap(policies.get(i), policies.get(j))) {
conflicts.add(new Conflict(
policies.get(i).getId().toString(),
policies.get(j).getId().toString(),
"Target overlap detected"
));
}
}
}
return conflicts;
}
private boolean policiesOverlap(Policy policy1, Policy policy2) {
// Simplified overlap detection
// In practice, this would involve complex target matching logic
return policy1.getTarget().equals(policy2.getTarget());
}
public static class ValidationResult {
private final boolean valid;
private final String message;
private final String version;
public ValidationResult(boolean valid, String message, String version) {
this.valid = valid;
this.message = message;
this.version = version;
}
public boolean isValid() { return valid; }
public String getMessage() { return message; }
public String getVersion() { return version; }
}
public static class Conflict {
private final String policyId1;
private final String policyId2;
private final String description;
public Conflict(String policyId1, String policyId2, String description) {
this.policyId1 = policyId1;
this.policyId2 = policyId2;
this.description = description;
}
public String getPolicyId1() { return policyId1; }
public String getPolicyId2() { return policyId2; }
public String getDescription() { return description; }
}
}

Testing the Implementation

JUnit Tests

package com.xacml.test;
import com.xacml.engine.BasicPDPEngine;
import com.xacml.pap.PolicyAdministrationPoint;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.wso2.balana.Decision;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class XACMLEngineTest {
private BasicPDPEngine pdpEngine;
private PolicyAdministrationPoint pap;
@BeforeEach
void setUp() throws Exception {
pdpEngine = new BasicPDPEngine();
pap = new PolicyAdministrationPoint();
// Load sample policies
pap.addPolicy("role-policy", SamplePolicies.ROLE_BASED_POLICY);
pdpEngine.loadPolicy(SamplePolicies.ROLE_BASED_POLICY);
}
@Test
void testRoleBasedAccess() throws Exception {
// Test admin access
Decision decision1 = pdpEngine.evaluateAccess(
"user1", "admin", "document1", "write", new HashMap<>());
assertEquals(Decision.PERMIT, decision1);
// Test user read access
Decision decision2 = pdpEngine.evaluateAccess(
"user2", "user", "document1", "read", new HashMap<>());
assertEquals(Decision.PERMIT, decision2);
// Test user write access (should be denied)
Decision decision3 = pdpEngine.evaluateAccess(
"user2", "user", "document1", "write", new HashMap<>());
assertEquals(Decision.DENY, decision3);
}
@Test
void testPolicyManagement() throws Exception {
// Test policy addition
pap.addPolicy("test-policy", SamplePolicies.TIME_BASED_POLICY);
assertNotNull(pap.getPolicy("test-policy"));
// Test policy removal
pap.removePolicy("test-policy");
assertNull(pap.getPolicy("test-policy"));
}
}

Performance Optimization

Caching PDP

package com.xacml.engine;
import org.wso2.balana.ctx.xacml3.RequestCtx;
import org.wso2.balana.ctx.xacml3.ResponseCtx;
import java.util.concurrent.*;
public class CachedPDPEngine extends BasicPDPEngine {
private final Cache<RequestCtx, ResponseCtx> decisionCache;
private final long cacheTimeoutMs;
public CachedPDPEngine(long cacheTimeoutMs, int maxCacheSize) {
super();
this.cacheTimeoutMs = cacheTimeoutMs;
this.decisionCache = new Cache<>(maxCacheSize, cacheTimeoutMs);
}
@Override
public ResponseCtx evaluateRequest(String xacmlRequest) throws Exception {
RequestCtx request = RequestCtx.getInstance(
new ByteArrayInputStream(xacmlRequest.getBytes()));
// Check cache first
ResponseCtx cachedResponse = decisionCache.get(request);
if (cachedResponse != null) {
return cachedResponse;
}
// Evaluate and cache result
ResponseCtx response = super.evaluateRequest(xacmlRequest);
decisionCache.put(request, response);
return response;
}
/**
* Simple LRU cache implementation
*/
private static class Cache<K, V> {
private final ConcurrentHashMap<K, V> map;
private final ConcurrentLinkedQueue<K> queue;
private final int maxSize;
private final long timeoutMs;
public Cache(int maxSize, long timeoutMs) {
this.maxSize = maxSize;
this.timeoutMs = timeoutMs;
this.map = new ConcurrentHashMap<>();
this.queue = new ConcurrentLinkedQueue<>();
}
public V get(K key) {
return map.get(key);
}
public void put(K key, V value) {
if (map.size() >= maxSize) {
K oldestKey = queue.poll();
if (oldestKey != null) {
map.remove(oldestKey);
}
}
queue.add(key);
map.put(key, value);
}
}
}

Conclusion

This comprehensive XACML policy engine implementation in Java provides:

  1. Complete PDP engine with Balana integration
  2. Policy administration for managing XACML policies
  3. Attribute-based authorization with custom PIP
  4. RESTful API for integration with applications
  5. Policy validation and conflict detection
  6. Performance optimization with caching

The engine supports complex authorization scenarios including role-based access control, attribute-based rules, time-based restrictions, and custom business logic. It can be integrated into enterprise applications to provide fine-grained, centralized authorization management following the XACML standard.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper