25.1 Authorization Models and Concepts
Authorization controls what authenticated users can do in a system. Understanding different authorization models is essential for building secure applications.
Authorization Fundamentals
// Authorization Concepts
public class AuthorizationFundamentals {
public static void printAuthorizationConcepts() {
System.out.println("=== AUTHORIZATION FUNDAMENTALS ===");
System.out.println("\n--- AUTHENTICATION vs AUTHORIZATION ---");
System.out.println("Authentication: Who are you?");
System.out.println(" - Verify user identity");
System.out.println(" - Username/password, tokens, certificates");
System.out.println(" - Happens first");
System.out.println("\nAuthorization: What can you do?");
System.out.println(" - Check permissions and access rights");
System.out.println(" - Based on roles, attributes, policies");
System.out.println(" - Happens after authentication");
System.out.println("\n--- KEY CONCEPTS ---");
System.out.println("Principal: Authenticated user or system");
System.out.println("Resource: Protected asset (file, API, data)");
System.out.println("Action: Operation (read, write, delete)");
System.out.println("Permission: Right to perform action on resource");
System.out.println("Role: Collection of permissions");
System.out.println("Policy: Rule defining access decisions");
System.out.println("\n--- AUTHORIZATION MODELS ---");
System.out.println("1. Discretionary Access Control (DAC)");
System.out.println(" - Resource owner controls access");
System.out.println(" - Example: File system permissions");
System.out.println("\n2. Mandatory Access Control (MAC)");
System.out.println(" - System-enforced security labels");
System.out.println(" - Example: Military classifications");
System.out.println("\n3. Role-Based Access Control (RBAC)");
System.out.println(" - Access based on user roles");
System.out.println(" - Most common in enterprise apps");
System.out.println("\n4. Attribute-Based Access Control (ABAC)");
System.out.println(" - Access based on attributes (user, resource, environment)");
System.out.println(" - Flexible and fine-grained");
System.out.println("\n5. Policy-Based Access Control (PBAC)");
System.out.println(" - Centralized policy engine");
System.out.println(" - Example: Open Policy Agent (OPA)");
}
}
Role-Based Access Control (RBAC)
// RBAC Implementation
public class RBACSystem {
// User with roles
public record User(String id, String name, Set<String> roles) {
public boolean hasRole(String role) {
return roles.contains(role);
}
public boolean hasAnyRole(String... roles) {
return Arrays.stream(roles).anyMatch(this.roles::contains);
}
public boolean hasAllRoles(String... roles) {
return Arrays.stream(roles).allMatch(this.roles::contains);
}
}
// Permission definition
public record Permission(String resource, String action) {
@Override
public String toString() {
return resource + ":" + action;
}
public static Permission of(String resource, String action) {
return new Permission(resource, action);
}
}
// Role with permissions
public static class Role {
private final String name;
private final Set<Permission> permissions;
private final Set<String> inheritsFrom;
public Role(String name, Set<Permission> permissions) {
this(name, permissions, Set.of());
}
public Role(String name, Set<Permission> permissions,
Set<String> inheritsFrom) {
this.name = name;
this.permissions = new HashSet<>(permissions);
this.inheritsFrom = new HashSet<>(inheritsFrom);
}
public String getName() { return name; }
public Set<Permission> getPermissions() { return permissions; }
public Set<String> getInheritsFrom() { return inheritsFrom; }
}
// RBAC Manager
public static class RBACManager {
private final Map<String, Role> roles = new ConcurrentHashMap<>();
public void defineRole(Role role) {
roles.put(role.getName(), role);
}
public boolean checkPermission(User user, Permission permission) {
Set<Permission> allPermissions = getAllPermissions(user);
return allPermissions.contains(permission);
}
public boolean checkPermission(User user, String resource, String action) {
return checkPermission(user, Permission.of(resource, action));
}
private Set<Permission> getAllPermissions(User user) {
Set<Permission> allPermissions = new HashSet<>();
for (String roleName : user.roles()) {
Role role = roles.get(roleName);
if (role != null) {
allPermissions.addAll(role.getPermissions());
allPermissions.addAll(getInheritedPermissions(role));
}
}
return allPermissions;
}
private Set<Permission> getInheritedPermissions(Role role) {
Set<Permission> inherited = new HashSet<>();
for (String parentRoleName : role.getInheritsFrom()) {
Role parentRole = roles.get(parentRoleName);
if (parentRole != null) {
inherited.addAll(parentRole.getPermissions());
inherited.addAll(getInheritedPermissions(parentRole));
}
}
return inherited;
}
public Set<Permission> getUserPermissions(User user) {
return getAllPermissions(user);
}
}
// Example: Define roles and check permissions
public static void demonstrateRBAC() {
RBACManager rbac = new RBACManager();
// Define roles
Role viewer = new Role("viewer", Set.of(
Permission.of("document", "read"),
Permission.of("document", "list")
));
Role editor = new Role("editor", Set.of(
Permission.of("document", "write"),
Permission.of("document", "update")
), Set.of("viewer")); // inherits from viewer
Role admin = new Role("admin", Set.of(
Permission.of("document", "delete"),
Permission.of("user", "manage"),
Permission.of("system", "configure")
), Set.of("editor")); // inherits from editor
rbac.defineRole(viewer);
rbac.defineRole(editor);
rbac.defineRole(admin);
// Create users
User alice = new User("1", "alice", Set.of("viewer"));
User bob = new User("2", "bob", Set.of("editor"));
User charlie = new User("3", "charlie", Set.of("admin"));
// Check permissions
System.out.println("=== RBAC DEMO ===");
System.out.println("\nAlice (viewer):");
System.out.println(" Can read documents: " +
rbac.checkPermission(alice, "document", "read"));
System.out.println(" Can write documents: " +
rbac.checkPermission(alice, "document", "write"));
System.out.println("\nBob (editor):");
System.out.println(" Can read documents: " +
rbac.checkPermission(bob, "document", "read"));
System.out.println(" Can write documents: " +
rbac.checkPermission(bob, "document", "write"));
System.out.println(" Can delete documents: " +
rbac.checkPermission(bob, "document", "delete"));
System.out.println("\nCharlie (admin):");
System.out.println(" Can read documents: " +
rbac.checkPermission(charlie, "document", "read"));
System.out.println(" Can write documents: " +
rbac.checkPermission(charlie, "document", "write"));
System.out.println(" Can delete documents: " +
rbac.checkPermission(charlie, "document", "delete"));
System.out.println(" Can manage users: " +
rbac.checkPermission(charlie, "user", "manage"));
// Show all permissions
System.out.println("\nCharlie's all permissions:");
rbac.getUserPermissions(charlie).forEach(p ->
System.out.println(" - " + p));
}
}
Attribute-Based Access Control (ABAC)
// ABAC Implementation
public class ABACSystem {
// Attributes
public interface Attributes {
Object get(String key);
default String getString(String key) {
Object value = get(key);
return value != null ? value.toString() : null;
}
default Integer getInt(String key) {
Object value = get(key);
return value instanceof Number ? ((Number) value).intValue() : null;
}
}
public static class AttributeMap implements Attributes {
private final Map<String, Object> attributes;
public AttributeMap(Map<String, Object> attributes) {
this.attributes = new HashMap<>(attributes);
}
@Override
public Object get(String key) {
return attributes.get(key);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final Map<String, Object> attributes = new HashMap<>();
public Builder with(String key, Object value) {
attributes.put(key, value);
return this;
}
public AttributeMap build() {
return new AttributeMap(attributes);
}
}
}
// Access request
public record AccessRequest(
Attributes subject,
Attributes resource,
String action,
Attributes environment
) {}
// Policy evaluation result
public enum Decision {
PERMIT, DENY, NOT_APPLICABLE
}
public record EvaluationResult(Decision decision, String reason) {
public boolean isPermitted() {
return decision == Decision.PERMIT;
}
}
// Policy interface
public interface Policy {
EvaluationResult evaluate(AccessRequest request);
}
// Example policies
public static class DepartmentAccessPolicy implements Policy {
@Override
public EvaluationResult evaluate(AccessRequest request) {
String userDept = request.subject().getString("department");
String resourceDept = request.resource().getString("department");
if (userDept == null || resourceDept == null) {
return new EvaluationResult(Decision.NOT_APPLICABLE,
"Missing department attribute");
}
if (userDept.equals(resourceDept)) {
return new EvaluationResult(Decision.PERMIT,
"Same department access");
}
return new EvaluationResult(Decision.DENY,
"Different department");
}
}
public static class BusinessHoursPolicy implements Policy {
@Override
public EvaluationResult evaluate(AccessRequest request) {
Integer hour = request.environment().getInt("hour");
if (hour == null) {
return new EvaluationResult(Decision.NOT_APPLICABLE,
"Missing time attribute");
}
if (hour >= 9 && hour < 17) {
return new EvaluationResult(Decision.PERMIT,
"During business hours");
}
return new EvaluationResult(Decision.DENY,
"Outside business hours");
}
}
public static class SeniorityPolicy implements Policy {
@Override
public EvaluationResult evaluate(AccessRequest request) {
if (!"sensitive".equals(request.resource().getString("classification"))) {
return new EvaluationResult(Decision.NOT_APPLICABLE,
"Not sensitive resource");
}
Integer seniority = request.subject().getInt("seniority");
if (seniority == null) {
return new EvaluationResult(Decision.DENY,
"Missing seniority attribute");
}
if (seniority >= 5) {
return new EvaluationResult(Decision.PERMIT,
"Sufficient seniority for sensitive data");
}
return new EvaluationResult(Decision.DENY,
"Insufficient seniority");
}
}
// Policy Decision Point (PDP)
public static class PolicyDecisionPoint {
private final List<Policy> policies = new ArrayList<>();
public void addPolicy(Policy policy) {
policies.add(policy);
}
public EvaluationResult evaluate(AccessRequest request) {
List<EvaluationResult> results = new ArrayList<>();
for (Policy policy : policies) {
EvaluationResult result = policy.evaluate(request);
results.add(result);
// If any policy denies, deny immediately
if (result.decision() == Decision.DENY) {
return result;
}
}
// Check if any policy permitted
boolean anyPermit = results.stream()
.anyMatch(r -> r.decision() == Decision.PERMIT);
if (anyPermit) {
return new EvaluationResult(Decision.PERMIT,
"All applicable policies permit");
}
return new EvaluationResult(Decision.DENY,
"No policy permits access");
}
}
// Example: ABAC in action
public static void demonstrateABAC() {
System.out.println("=== ABAC DEMO ===");
// Setup PDP with policies
PolicyDecisionPoint pdp = new PolicyDecisionPoint();
pdp.addPolicy(new DepartmentAccessPolicy());
pdp.addPolicy(new BusinessHoursPolicy());
pdp.addPolicy(new SeniorityPolicy());
// Test case 1: Same department, business hours, regular document
Attributes user1 = AttributeMap.builder()
.with("name", "Alice")
.with("department", "engineering")
.with("seniority", 3)
.build();
Attributes resource1 = AttributeMap.builder()
.with("name", "design.pdf")
.with("department", "engineering")
.with("classification", "public")
.build();
Attributes env1 = AttributeMap.builder()
.with("hour", 14)
.build();
AccessRequest request1 = new AccessRequest(user1, resource1, "read", env1);
EvaluationResult result1 = pdp.evaluate(request1);
System.out.println("\nTest 1 - Same dept, business hours, public doc:");
System.out.println(" Decision: " + result1.decision());
System.out.println(" Reason: " + result1.reason());
// Test case 2: Different department
Attributes resource2 = AttributeMap.builder()
.with("name", "finance.xlsx")
.with("department", "finance")
.with("classification", "public")
.build();
AccessRequest request2 = new AccessRequest(user1, resource2, "read", env1);
EvaluationResult result2 = pdp.evaluate(request2);
System.out.println("\nTest 2 - Different dept:");
System.out.println(" Decision: " + result2.decision());
System.out.println(" Reason: " + result2.reason());
// Test case 3: Sensitive document, insufficient seniority
Attributes resource3 = AttributeMap.builder()
.with("name", "confidential.pdf")
.with("department", "engineering")
.with("classification", "sensitive")
.build();
AccessRequest request3 = new AccessRequest(user1, resource3, "read", env1);
EvaluationResult result3 = pdp.evaluate(request3);
System.out.println("\nTest 3 - Sensitive doc, seniority 3:");
System.out.println(" Decision: " + result3.decision());
System.out.println(" Reason: " + result3.reason());
// Test case 4: Senior user with sensitive document
Attributes user2 = AttributeMap.builder()
.with("name", "Bob")
.with("department", "engineering")
.with("seniority", 8)
.build();
AccessRequest request4 = new AccessRequest(user2, resource3, "read", env1);
EvaluationResult result4 = pdp.evaluate(request4);
System.out.println("\nTest 4 - Sensitive doc, seniority 8:");
System.out.println(" Decision: " + result4.decision());
System.out.println(" Reason: " + result4.reason());
}
}
Permission-Based Authorization
// Fine-grained permission system
public class PermissionBasedAuth {
// Permission with wildcards
public static class Permission {
private final String resource;
private final String action;
public Permission(String resource, String action) {
this.resource = resource;
this.action = action;
}
public boolean implies(Permission other) {
return matchesPattern(this.resource, other.resource) &&
matchesPattern(this.action, other.action);
}
private boolean matchesPattern(String pattern, String value) {
if ("*".equals(pattern)) return true;
if (pattern.equals(value)) return true;
// Support prefix wildcards: "document:*"
if (pattern.endsWith("*")) {
String prefix = pattern.substring(0, pattern.length() - 1);
return value.startsWith(prefix);
}
return false;
}
@Override
public String toString() {
return resource + ":" + action;
}
public static Permission parse(String permission) {
String[] parts = permission.split(":", 2);
if (parts.length != 2) {
throw new IllegalArgumentException("Invalid permission: " + permission);
}
return new Permission(parts[0], parts[1]);
}
}
// Subject with permissions
public static class Subject {
private final String id;
private final Set<Permission> permissions;
public Subject(String id, Set<Permission> permissions) {
this.id = id;
this.permissions = new HashSet<>(permissions);
}
public boolean hasPermission(Permission required) {
return permissions.stream()
.anyMatch(p -> p.implies(required));
}
public boolean hasPermission(String resource, String action) {
return hasPermission(new Permission(resource, action));
}
public String getId() { return id; }
}
// Authorization service
public static class AuthorizationService {
public void checkPermission(Subject subject, Permission required) {
if (!subject.hasPermission(required)) {
throw new SecurityException(
"Access denied: " + required + " for subject " + subject.getId());
}
}
public void checkPermission(Subject subject, String resource, String action) {
checkPermission(subject, new Permission(resource, action));
}
public boolean isAuthorized(Subject subject, Permission required) {
return subject.hasPermission(required);
}
}
// Example: Wildcard permissions
public static void demonstrateWildcardPermissions() {
System.out.println("=== WILDCARD PERMISSIONS ===");
// User with wildcard read permission
Subject reader = new Subject("reader", Set.of(
new Permission("document", "read"),
new Permission("image", "read")
));
// User with all document permissions
Subject docAdmin = new Subject("docAdmin", Set.of(
new Permission("document", "*")
));
// Super admin with all permissions
Subject superAdmin = new Subject("superAdmin", Set.of(
new Permission("*", "*")
));
AuthorizationService authz = new AuthorizationService();
// Test permissions
System.out.println("\nReader:");
System.out.println(" Can read documents: " +
authz.isAuthorized(reader, Permission.parse("document:read")));
System.out.println(" Can write documents: " +
authz.isAuthorized(reader, Permission.parse("document:write")));
System.out.println("\nDocument Admin:");
System.out.println(" Can read documents: " +
authz.isAuthorized(docAdmin, Permission.parse("document:read")));
System.out.println(" Can write documents: " +
authz.isAuthorized(docAdmin, Permission.parse("document:write")));
System.out.println(" Can delete documents: " +
authz.isAuthorized(docAdmin, Permission.parse("document:delete")));
System.out.println(" Can read images: " +
authz.isAuthorized(docAdmin, Permission.parse("image:read")));
System.out.println("\nSuper Admin:");
System.out.println(" Can do anything: " +
authz.isAuthorized(superAdmin, Permission.parse("anything:everything")));
}
}
Best Practices
- Principle of least privilege: Grant minimum permissions needed.
- Role hierarchies: Use inheritance to simplify management.
- Fine-grained permissions: Balance granularity with complexity.
- Audit logging: Log all authorization decisions.
- Fail securely: Deny by default, permit explicitly.
- Separate concerns: Keep authorization logic separate from business logic.
- Performance: Cache authorization decisions when appropriate.
- Testing: Test all permission combinations thoroughly.
- Documentation: Document roles, permissions, and policies clearly.
- Regular reviews: Audit and update permissions periodically.