diff --git a/src/main/java/com/klaroworks/runtime/role/RoleMatrixRuntime.java b/src/main/java/com/klaroworks/runtime/role/RoleMatrixRuntime.java new file mode 100644 index 0000000..a6944c0 --- /dev/null +++ b/src/main/java/com/klaroworks/runtime/role/RoleMatrixRuntime.java @@ -0,0 +1,132 @@ +package com.klaroworks.runtime.role; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Runtime engine for evaluating role-based access and permissions. + */ +public final class RoleMatrixRuntime { + + private final Map> roleHierarchy; + private final Map> rolePermissions; + private final RoleValidator validator; + private final ThreadLocal currentContext; + + public RoleMatrixRuntime() { + this(new HashMap<>(), new HashMap<>(), new RoleValidator()); + } + + public RoleMatrixRuntime(Map> roleHierarchy, Map> rolePermissions, RoleValidator validator) { + this.roleHierarchy = new ConcurrentHashMap<>(roleHierarchy); + this.rolePermissions = new ConcurrentHashMap<>(rolePermissions); + this.validator = validator != null ? validator : new RoleValidator(); + this.currentContext = ThreadLocal.withInitial(() -> null); + } + + public boolean hasRole(RoleContext context, String role) { + if (context == null || role == null) { + return false; + } + Set userRoles = getUserRoles(context); + return userRoles.contains(role) || hasRoleInHierarchy(role, userRoles); + } + + public boolean hasPermission(RoleContext context, String permission) { + if (context == null || permission == null) { + return false; + } + Set userRoles = getUserRoles(context); + for (String role : userRoles) { + Set perms = rolePermissions.get(role); + if (perms != null && perms.contains(permission)) { + return true; + } + } + return false; + } + + public boolean evaluateRole(RoleContext context, String roleExpression) { + if (context == null || roleExpression == null) { + return false; + } + String[] parts = roleExpression.split("\\|"); + for (String part : parts) { + String trimmed = part.trim(); + if (trimmed.startsWith("!") && hasRole(context, trimmed.substring(1))) { + return false; + } else if (hasRole(context, trimmed)) { + return true; + } + } + return false; + } + + public void setContext(RoleContext context) { + currentContext.set(context); + } + + public RoleContext getContext() { + return currentContext.get(); + } + + public void clearContext() { + currentContext.remove(); + } + + public void defineRole(String role, Set parentRoles) { + roleHierarchy.put(role, Set.copyOf(parentRoles)); + } + + public void assignPermissions(String role, Set permissions) { + rolePermissions.put(role, Set.copyOf(permissions)); + } + + private Set getUserRoles(RoleContext context) { + Object rolesObj = context.getAttribute("roles"); + if (rolesObj instanceof Collection) { + Set roles = new HashSet<>(); + for (Object r : (Collection) rolesObj) { + if (r != null) { + roles.add(r.toString()); + } + } + return roles; + } + return Collections.emptySet(); + } + + private boolean hasRoleInHierarchy(String targetRole, Set userRoles) { + for (String userRole : userRoles) { + if (hasInHierarchy(targetRole, userRole, new HashSet<>())) { + return true; + } + } + return false; + } + + private boolean hasInHierarchy(String target, String current, Set visited) { + if (visited.contains(current)) { + return false; + } + visited.add(current); + Set parents = roleHierarchy.get(current); + if (parents != null) { + if (parents.contains(target)) { + return true; + } + for (String parent : parents) { + if (hasInHierarchy(target, parent, visited)) { + return true; + } + } + } + return false; + } + + public static final class Nested { + public static boolean evaluate(RoleMatrixRuntime runtime, RoleContext context, String expression) { + return runtime.evaluateRole(context, expression); + } + } +}