google-site-verification: google61fe8ba583a51912.html
Implementing Attribute-Based Access Control (ABAC) in Java

Overview

Attribute-Based Access Control (ABAC) is a flexible authorization model that uses attributes (user, resource, action, environment) to make access control decisions. This implementation provides a comprehensive ABAC system in Java.

Core Concepts

ABAC Components

  • Subject: User or system requesting access
  • Resource: Object being accessed
  • Action: Operation being performed
  • Environment: Contextual information (time, location, etc.)
  • Policy: Rules defining access conditions

Implementation

1. Dependencies

<dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <dependency> <groupId>org.mvel</groupId> <artifactId>mvel2</artifactId> <version>2.4.14.Final</version> </dependency> </dependencies>

2. Core Domain Models

import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.HashSet; // Subject (User) with attributes public class Subject { private String id; private String username; private String role; private String department; private String clearanceLevel; private Map<String, Object> attributes; public Subject(String id, String username) { this.id = id; this.username = username; this.attributes = new HashMap<>(); } // Getters and setters public String getId() { return id; } public String getUsername() { return username; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public String getClearanceLevel() { return clearanceLevel; } public void setClearanceLevel(String clearanceLevel) { this.clearanceLevel = clearanceLevel; } public Map<String, Object> getAttributes() { return attributes; } public void setAttribute(String key, Object value) { attributes.put(key, value); } public Object getAttribute(String key) { return attributes.get(key); } } // Resource with attributes public class Resource { private String id; private String name; private String type; private String owner; private String sensitivity; private Map<String, Object> attributes; public Resource(String id, String name, String type) { this.id = id; this.name = name; this.type = type; this.attributes = new HashMap<>(); } // Getters and setters public String getId() { return id; } public String getName() { return name; } public String getType() { return type; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } public String getSensitivity() { return sensitivity; } public void setSensitivity(String sensitivity) { this.sensitivity = sensitivity; } public Map<String, Object> getAttributes() { return attributes; } public void setAttribute(String key, Object value) { attributes.put(key, value); } public Object getAttribute(String key) { return attributes.get(key); } } // Action being performed public class Action { private String name; private Map<String, Object> attributes; public Action(String name) { this.name = name; this.attributes = new HashMap<>(); } // Getters and setters public String getName() { return name; } public Map<String, Object> getAttributes() { return attributes; } public void setAttribute(String key, Object value) { attributes.put(key, value); } } // Environment context public class Environment { private long timestamp; private String ipAddress; private String location; private String deviceType; private Map<String, Object> attributes; public Environment() { this.timestamp = System.currentTimeMillis(); this.attributes = new HashMap<>(); } // Getters and setters public long getTimestamp() { return timestamp; } public String getIpAddress() { return ipAddress; } public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getDeviceType() { return deviceType; } public void setDeviceType(String deviceType) { this.deviceType = deviceType; } public Map<String, Object> getAttributes() { return attributes; } public void setAttribute(String key, Object value) { attributes.put(key, value); } } // Access Request public class AccessRequest { private Subject subject; private Resource resource; private Action action; private Environment environment; public AccessRequest(Subject subject, Resource resource, Action action, Environment environment) { this.subject = subject; this.resource = resource; this.action = action; this.environment = environment; } // Getters public Subject getSubject() { return subject; } public Resource getResource() { return resource; } public Action getAction() { return action; } public Environment getEnvironment() { return environment; } }

3. Policy Definition

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.ArrayList; public class Policy { public enum Effect { PERMIT, DENY } private String id; private String name; private String description; private Effect effect; private List<String> targets; private List<Rule> rules; private int priority; @JsonCreator public Policy(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("description") String description, @JsonProperty("effect") Effect effect, @JsonProperty("targets") List<String> targets, @JsonProperty("rules") List<Rule> rules, @JsonProperty("priority") int priority) { this.id = id; this.name = name; this.description = description; this.effect = effect; this.targets = targets != null ? targets : new ArrayList<>(); this.rules = rules != null ? rules : new ArrayList<>(); this.priority = priority; } // Getters public String getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } public Effect getEffect() { return effect; } public List<String> getTargets() { return targets; } public List<Rule> getRules() { return rules; } public int getPriority() { return priority; } public boolean matchesTarget(AccessRequest request) { if (targets.isEmpty()) return true; for (String target : targets) { if (evaluateTargetExpression(target, request)) { return true; } } return false; } private boolean evaluateTargetExpression(String expression, AccessRequest request) { // Simple target matching - can be enhanced with expression evaluator return expression.contains(request.getAction().getName()) || expression.contains(request.getResource().getType()); } } public class Rule { private String id; private String condition; @JsonCreator public Rule(@JsonProperty("id") String id, @JsonProperty("condition") String condition) { this.id = id; this.condition = condition; } // Getters public String getId() { return id; } public String getCondition() { return condition; } }

4. Policy Decision Point (PDP)

import org.mvel2.MVEL; import java.io.Serializable; import java.util.*; public class PolicyDecisionPoint { private PolicyAdministrationPoint pap; private Map<String, Serializable> compiledExpressions; public PolicyDecisionPoint(PolicyAdministrationPoint pap) { this.pap = pap; this.compiledExpressions = new HashMap<>(); } public AuthorizationDecision decide(AccessRequest request) { List<Policy> applicablePolicies = pap.findApplicablePolicies(request); // Sort by priority (higher priority first) applicablePolicies.sort((p1, p2) -> Integer.compare(p2.getPriority(), p1.getPriority())); for (Policy policy : applicablePolicies) { if (evaluatePolicy(policy, request)) { return new AuthorizationDecision(policy.getEffect() == Policy.Effect.PERMIT, policy.getId(), "Policy " + policy.getName() + " applied"); } } // Default deny return new AuthorizationDecision(false, null, "No applicable policy found"); } private boolean evaluatePolicy(Policy policy, AccessRequest request) { if (!policy.matchesTarget(request)) { return false; } for (Rule rule : policy.getRules()) { if (!evaluateRule(rule, request)) { return false; } } return true; } private boolean evaluateRule(Rule rule, AccessRequest request) { try { // Compile expression for better performance String expressionKey = rule.getId() + "_" + rule.getCondition().hashCode(); Serializable compiledExpr = compiledExpressions.computeIfAbsent(expressionKey, k -> MVEL.compileExpression(rule.getCondition())); // Create evaluation context Map<String, Object> context = createEvaluationContext(request); // Evaluate condition Object result = MVEL.executeExpression(compiledExpr, context); return Boolean.TRUE.equals(result); } catch (Exception e) { System.err.println("Error evaluating rule " + rule.getId() + ": " + e.getMessage()); return false; } } private Map<String, Object> createEvaluationContext(AccessRequest request) { Map<String, Object> context = new HashMap<>(); // Add subject attributes context.put("subject", request.getSubject()); context.put("user", request.getSubject()); // alias context.put("userRole", request.getSubject().getRole()); context.put("userDepartment", request.getSubject().getDepartment()); context.put("userClearance", request.getSubject().getClearanceLevel()); // Add resource attributes context.put("resource", request.getResource()); context.put("resourceType", request.getResource().getType()); context.put("resourceOwner", request.getResource().getOwner()); context.put("resourceSensitivity", request.getResource().getSensitivity()); // Add action context.put("action", request.getAction()); context.put("actionName", request.getAction().getName()); // Add environment context.put("environment", request.getEnvironment()); context.put("currentTime", request.getEnvironment().getTimestamp()); context.put("location", request.getEnvironment().getLocation()); context.put("ipAddress", request.getEnvironment().getIpAddress()); // Add custom attributes context.putAll(request.getSubject().getAttributes()); context.putAll(request.getResource().getAttributes()); context.putAll(request.getAction().getAttributes()); context.putAll(request.getEnvironment().getAttributes()); return context; } } public class AuthorizationDecision { private final boolean permitted; private final String policyId; private final String reason; private final long timestamp; public AuthorizationDecision(boolean permitted, String policyId, String reason) { this.permitted = permitted; this.policyId = policyId; this.reason = reason; this.timestamp = System.currentTimeMillis(); } // Getters public boolean isPermitted() { return permitted; } public String getPolicyId() { return policyId; } public String getReason() { return reason; } public long getTimestamp() { return timestamp; } @Override public String toString() { return String.format("AuthorizationDecision{permitted=%s, policyId='%s', reason='%s'}", permitted, policyId, reason); } }

5. Policy Administration Point (PAP)

import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; public class PolicyAdministrationPoint { private Map<String, Policy> policies; private ObjectMapper objectMapper; public PolicyAdministrationPoint() { this.policies = new HashMap<>(); this.objectMapper = new ObjectMapper(); } public void loadPoliciesFromFile(String filePath) throws IOException { Policy[] policyArray = objectMapper.readValue(new File(filePath), Policy[].class); for (Policy policy : policyArray) { addPolicy(policy); } System.out.println("Loaded " + policyArray.length + " policies from " + filePath); } public void addPolicy(Policy policy) { policies.put(policy.getId(), policy); } public void removePolicy(String policyId) { policies.remove(policyId); } public Policy getPolicy(String policyId) { return policies.get(policyId); } public List<Policy> getAllPolicies() { return new ArrayList<>(policies.values()); } public List<Policy> findApplicablePolicies(AccessRequest request) { return policies.values().stream() .filter(policy -> policy.matchesTarget(request)) .collect(Collectors.toList()); } }

6. Policy Enforcement Point (PEP)

public class PolicyEnforcementPoint { private PolicyDecisionPoint pdp; public PolicyEnforcementPoint(PolicyDecisionPoint pdp) { this.pdp = pdp; } public boolean checkAccess(Subject subject, Resource resource, Action action, Environment environment) { AccessRequest request = new AccessRequest(subject, resource, action, environment); AuthorizationDecision decision = pdp.decide(request); // Log access decision logAccessDecision(request, decision); return decision.isPermitted(); } public AuthorizationDecision checkAccessWithDetails(Subject subject, Resource resource, Action action, Environment environment) { AccessRequest request = new AccessRequest(subject, resource, action, environment); AuthorizationDecision decision = pdp.decide(request); logAccessDecision(request, decision); return decision; } private void logAccessDecision(AccessRequest request, AuthorizationDecision decision) { System.out.println("Access Decision: " + "User=" + request.getSubject().getUsername() + ", Resource=" + request.getResource().getName() + ", Action=" + request.getAction().getName() + ", Decision=" + (decision.isPermitted() ? "PERMIT" : "DENY") + ", Reason=" + decision.getReason()); } }

7. Sample Policy Configuration

Create policies.json:

[ { "id": "policy-001", "name": "HR Department Document Access", "description": "Allow HR department to access HR documents", "effect": "PERMIT", "targets": ["document", "read"], "rules": [ { "id": "rule-001", "condition": "userDepartment == 'HR' && resourceType == 'document' && actionName == 'read'" } ], "priority": 10 }, { "id": "policy-002", "name": "Manager Edit Access", "description": "Allow managers to edit documents in their department", "effect": "PERMIT", "targets": ["document", "edit"], "rules": [ { "id": "rule-002", "condition": "userRole == 'MANAGER' && userDepartment == resourceOwner && actionName == 'edit'" } ], "priority": 20 }, { "id": "policy-003", "name": "Confidential Document Access", "description": "Only allow access to confidential documents with proper clearance", "effect": "PERMIT", "targets": ["document"], "rules": [ { "id": "rule-003", "condition": "resourceSensitivity == 'CONFIDENTIAL' && userClearance == 'HIGH'" }, { "id": "rule-004", "condition": "resourceSensitivity != 'CONFIDENTIAL'" } ], "priority": 30 }, { "id": "policy-004", "name": "Business Hours Restriction", "description": "Only allow access during business hours", "effect": "PERMIT", "targets": [], "rules": [ { "id": "rule-005", "condition": "isBusinessHours(currentTime)" } ], "priority": 5 }, { "id": "policy-005", "name": "Deny Delete for Non-Admins", "description": "Only admins can delete resources", "effect": "DENY", "targets": ["delete"], "rules": [ { "id": "rule-006", "condition": "userRole != 'ADMIN' && actionName == 'delete'" } ], "priority": 100 } ]

8. Utility Functions

import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; public class ABACUtils { public static boolean isBusinessHours(long timestamp) { ZonedDateTime dateTime = Instant.ofEpochMilli(timestamp) .atZone(ZoneId.systemDefault()); int hour = dateTime.getHour(); int dayOfWeek = dateTime.getDayOfWeek().getValue(); // Business hours: Monday-Friday, 9 AM - 5 PM return dayOfWeek >= 1 && dayOfWeek <= 5 && hour >= 9 && hour < 17; } public static Environment createDefaultEnvironment() { Environment env = new Environment(); env.setIpAddress("192.168.1.100"); env.setLocation("Office"); env.setDeviceType("Desktop"); env.setAttribute("isBusinessHours", isBusinessHours(System.currentTimeMillis())); return env; } }

9. Complete Example Usage

public class ABACExample { public static void main(String[] args) { try { // Initialize ABAC components PolicyAdministrationPoint pap = new PolicyAdministrationPoint(); PolicyDecisionPoint pdp = new PolicyDecisionPoint(pap); PolicyEnforcementPoint pep = new PolicyEnforcementPoint(pdp); // Load policies pap.loadPoliciesFromFile("policies.json"); // Create test subjects Subject hrUser = new Subject("user-001", "john.doe"); hrUser.setRole("EMPLOYEE"); hrUser.setDepartment("HR"); hrUser.setClearanceLevel("LOW"); Subject manager = new Subject("user-002", "jane.smith"); manager.setRole("MANAGER"); manager.setDepartment("Finance"); manager.setClearanceLevel("MEDIUM"); Subject admin = new Subject("user-003", "admin.user"); admin.setRole("ADMIN"); admin.setDepartment("IT"); admin.setClearanceLevel("HIGH"); // Create test resources Resource hrDocument = new Resource("doc-001", "HR Policy", "document"); hrDocument.setOwner("HR"); hrDocument.setSensitivity("PUBLIC"); Resource confidentialDoc = new Resource("doc-002", "Financial Report", "document"); confidentialDoc.setOwner("Finance"); confidentialDoc.setSensitivity("CONFIDENTIAL"); Resource budgetDoc = new Resource("doc-003", "Budget Plan", "document"); budgetDoc.setOwner("Finance"); budgetDoc.setSensitivity("INTERNAL"); // Test scenarios testScenario(pep, hrUser, hrDocument, new Action("read"), "HR reading HR document"); testScenario(pep, hrUser, confidentialDoc, new Action("read"), "HR reading confidential document"); testScenario(pep, manager, budgetDoc, new Action("edit"), "Manager editing budget"); testScenario(pep, manager, confidentialDoc, new Action("read"), "Manager reading confidential doc"); testScenario(pep, admin, confidentialDoc, new Action("delete"), "Admin deleting document"); testScenario(pep, manager, confidentialDoc, new Action("delete"), "Manager deleting document"); } catch (Exception e) { e.printStackTrace(); } } private static void testScenario(PolicyEnforcementPoint pep, Subject subject, Resource resource, Action action, String description) { Environment env = ABACUtils.createDefaultEnvironment(); System.out.println("\n=== Scenario: " + description + " ==="); AuthorizationDecision decision = pep.checkAccessWithDetails(subject, resource, action, env); System.out.println("User: " + subject.getUsername() + " (" + subject.getRole() + "/" + subject.getDepartment() + ")"); System.out.println("Resource: " + resource.getName() + " (" + resource.getSensitivity() + ")"); System.out.println("Action: " + action.getName()); System.out.println("Decision: " + (decision.isPermitted() ? "PERMIT" : "DENY")); System.out.println("Reason: " + decision.getReason()); } }

Advanced Features

1. Policy Combining Algorithms

public enum PolicyCombiningAlgorithm { DENY_OVERRIDES, PERMIT_OVERRIDES, FIRST_APPLICABLE, ONLY_ONE_APPLICABLE } public class AdvancedPDP { private PolicyCombiningAlgorithm algorithm; public AuthorizationDecision decideWithAlgorithm(AccessRequest request, List<Policy> policies, PolicyCombiningAlgorithm algorithm) { switch (algorithm) { case DENY_OVERRIDES: return denyOverrides(request, policies); case PERMIT_OVERRIDES: return permitOverrides(request, policies); case FIRST_APPLICABLE: return firstApplicable(request, policies); default: return firstApplicable(request, policies); } } private AuthorizationDecision denyOverrides(AccessRequest request, List<Policy> policies) { boolean atLeastOnePermit = false; for (Policy policy : policies) { if (evaluatePolicy(policy, request)) { if (policy.getEffect() == Policy.Effect.DENY) { return new AuthorizationDecision(false, policy.getId(), "Deny policy applied"); } atLeastOnePermit = true; } } return new AuthorizationDecision(atLeastOnePermit, null, atLeastOnePermit ? "Permit granted" : "No policy applied"); } // Implement other algorithms similarly... }

Security Considerations

  1. Policy Validation: Validate policies before loading
  2. Expression Security: Sanitize expression inputs to prevent code injection
  3. Audit Logging: Log all access decisions for compliance
  4. Performance: Cache compiled expressions and policy evaluations
  5. Policy Management: Secure policy administration interface

This ABAC implementation provides a flexible, attribute-based authorization system that can handle complex access control scenarios while maintaining performance and security.

Leave a Reply

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


Macro Nepal Helper