Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/main/java/org/casbin/jcasbin/main/ManagementEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import org.casbin.jcasbin.effect.Effect;
import org.casbin.jcasbin.model.Assertion;
import org.casbin.jcasbin.rbac.DefaultRoleManager;
import org.casbin.jcasbin.rbac.RoleManager;
import org.casbin.jcasbin.util.Util;
import org.casbin.jcasbin.util.function.CustomFunction;

Expand All @@ -26,6 +28,24 @@
* ManagementEnforcer = InternalEnforcer + Management API.
*/
public class ManagementEnforcer extends InternalEnforcer {

/**
* loadPolicy reloads the policy from file/database.
* Overridden to add cycle detection check after policy loading.
*/
@Override
public void loadPolicy() {
super.loadPolicy();

// Check for cycles in all role managers after policy is loaded
for (Map.Entry<String, RoleManager> entry : rmMap.entrySet()) {
RoleManager rm = entry.getValue();
if (rm instanceof DefaultRoleManager) {
((DefaultRoleManager) rm).checkCycles();
}
}
}

/**
* getAllSubjects gets the list of subjects that show up in the current policy.
*
Expand Down
38 changes: 37 additions & 1 deletion src/main/java/org/casbin/jcasbin/rbac/DefaultRoleManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.casbin.jcasbin.rbac;

import org.casbin.jcasbin.detector.Detector;
import org.casbin.jcasbin.util.SyncedLRUCache;
import org.casbin.jcasbin.util.Util;

Expand All @@ -27,6 +28,7 @@ public class DefaultRoleManager implements RoleManager {

BiPredicate<String, String> matchingFunc;
private SyncedLRUCache<String, Boolean> matchingFuncCache;
private Detector detector;

/**
* DefaultRoleManager is the constructor for creating an instance of the default RoleManager
Expand Down Expand Up @@ -81,12 +83,40 @@ public void addMatchingFunc(String name, BiPredicate<String, String> matchingFun
public void addDomainMatchingFunc(String name, BiPredicate<String, String> domainMatchingFunc) {
}

/**
* setDetector sets the detector for cycle detection in role inheritance.
*
* @param detector the detector instance to use for cycle detection.
*/
public void setDetector(Detector detector) {
this.detector = detector;
}

/**
* checkCycles performs a one-time cycle detection check on the current role graph.
* This should be called after bulk policy loading to detect any cycles.
*
* @throws IllegalArgumentException if a cycle is detected in the role inheritance graph.
*/
public void checkCycles() {
if (this.detector != null) {
String errorMsg = this.detector.check(this);
if (errorMsg != null && !errorMsg.isEmpty()) {
throw new IllegalArgumentException(errorMsg);
}
}
}

private void rebuild() {
Map<String, Role> roles = new HashMap<>(this.allRoles);
this.clear();

roles.values().forEach(user -> {
user.getAllRoles().keySet().forEach(roleName -> addLink(user.getName(), roleName, DEFAULT_DOMAIN));
});

// Perform cycle check at the end
checkCycles();
}

boolean match(String str, String pattern) {
Expand Down Expand Up @@ -157,9 +187,15 @@ public void clear() {
* inherits role: name2. domain is a prefix to the roles.
*/
@Override
public void addLink(String name1, String name2, String... domain) {
public synchronized void addLink(String name1, String name2, String... domain) {
Role user = getRole(name1);
Role role = getRole(name2);

// Check if the link already exists to maintain idempotency
if (user.roles.containsKey(name2)) {
return;
}

user.addRole(role);
}

Expand Down
Loading