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.