Skip to content
Closed
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
13 changes: 13 additions & 0 deletions src/main/java/org/casbin/jcasbin/main/ManagementEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@ public List<String> getAllNamedRoles(String ptype) {
return model.getValuesForFieldInPolicy("g", ptype, 1);
}

/**
* getAllUsers gets the list of users that show up in the current policy.
* Users are subjects that are not roles (i.e., subjects that do not appear
* as the second element in any grouping policy).
*
* @return all users in policy, excluding roles.
*/
public List<String> getAllUsers() {
List<String> subjects = getAllSubjects();
List<String> roles = getAllRoles();
return Util.setSubtract(subjects, roles);
}

/**
* getPolicy gets all the authorization rules in the policy.
*
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/casbin/jcasbin/main/SyncedEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,17 @@ public List<String> getAllNamedRoles(String ptype) {
return runSynchronized(() -> super.getAllNamedRoles(ptype), getReadWriteLock().readLock());
}

/**
* getAllUsers gets the list of users that show up in the current policy.
* Users are subjects that are not roles.
*
* @return all users in policy rules, excluding roles.
*/
@Override
public List<String> getAllUsers() {
return runSynchronized(super::getAllUsers, getReadWriteLock().readLock());
}

/**
* getPolicy gets all the authorization rules in the policy.
*
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/org/casbin/jcasbin/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -470,4 +470,58 @@ private void writeString(StringBuilder s, String sec, Map<String, String> tokenP
s.append(String.format("%s = %s\n", sec, value));
}
}

/**
* getValuesForFieldInPolicyAllTypes gets all values for a field for all rules
* across all policy types in a section. Duplicated values are removed.
*
* @param sec the section, "p" or "g".
* @param fieldIndex the policy rule's index.
* @return all field values across all ptypes.
*/
public List<String> getValuesForFieldInPolicyAllTypes(String sec, int fieldIndex) {
List<String> values = new ArrayList<>();

Map<String, Assertion> section = model.get(sec);
if (section == null) {
return values;
}

for (Assertion assertion : section.values()) {
for (List<String> rule : assertion.policy) {
if (fieldIndex < rule.size()) {
values.add(rule.get(fieldIndex));
}
}
}

return Util.arrayRemoveDuplicates(values);
}

/**
* getFieldIndex returns the index of a field in a policy type.
* For example, given ptype="p" and field="sub", returns the index
* where "p_sub" appears in the tokens array.
*
* @param ptype the policy type, e.g., "p", "p2"
* @param field the field name, e.g., "sub", "obj", "act"
* @return the index of the field in the policy rule, or -1 if not found
*/
public int getFieldIndex(String ptype, String field) {
String pattern = ptype + "_" + field;
Map<String, Assertion> pSection = model.get("p");
if (pSection == null) {
return -1;
}
Assertion ast = pSection.get(ptype);
if (ast == null || ast.tokens == null) {
return -1;
}
for (int i = 0; i < ast.tokens.length; i++) {
if (pattern.equals(ast.tokens[i])) {
return i;
}
}
return -1;
}
}
25 changes: 25 additions & 0 deletions src/main/java/org/casbin/jcasbin/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -388,4 +388,29 @@ public static boolean isJsonString(String str) {
return false;
}
}

/**
* setSubtract returns elements in list A that are not in list B.
* Preserves order from list A.
*
* @param a the first list (base)
* @param b the second list (subtrahend)
* @return a - b (elements in A but not in B)
*/
public static List<String> setSubtract(List<String> a, List<String> b) {
if (a == null) {
a = new ArrayList<>();
}
if (b == null) {
b = new ArrayList<>();
}
Set<String> bSet = new HashSet<>(b);
List<String> result = new ArrayList<>();
for (String item : a) {
if (!bSet.contains(item)) {
result.add(item);
}
}
return result;
}
}
71 changes: 71 additions & 0 deletions src/test/java/org/casbin/jcasbin/main/ManagementAPIUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.AviatorEvaluatorInstance;
import org.casbin.jcasbin.util.Util;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -298,4 +299,74 @@ public void should_true_when_setAviatorEvaluator_given_customInstance() {
assertEquals(enforcer.getAviatorEval(), instance);
}

@Test
public void testGetUsersAPI() {
// 1. Basic RBAC: alice and bob are users, data2_admin is a role
Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
testGetAllSubjectsUtil(e, asList("alice", "bob", "data2_admin"));
testGetAllRolesUtil(e, asList("data2_admin"));
testGetAllUsersUtil(e, asList("alice", "bob"));

// 2. Add user "admin" that appears in both policy (as subject) and grouping (as role target)
// getAllUsers computes: subjects (from p) - roles (from g, column 1)
// admin is added as a policy subject and also assigned to role "root"
e.addPolicy("admin", "data1", "read");
e.addGroupingPolicy("admin", "root");
testGetAllSubjectsUtil(e, asList("alice", "bob", "data2_admin", "admin"));
testGetAllRolesUtil(e, asList("data2_admin", "root"));
testGetAllUsersUtil(e, asList("alice", "bob", "admin")); // admin is user since it's in p, not in g column 1
e.removePolicy("admin", "data1", "read");
e.removeGroupingPolicy("admin", "root");

// 3. Add regular user "eve" who is only in policy (not a role in any grouping)
e.addPolicy("eve", "data3", "read");
testGetAllSubjectsUtil(e, asList("alice", "bob", "data2_admin", "eve"));
testGetAllRolesUtil(e, asList("data2_admin"));
testGetAllUsersUtil(e, asList("alice", "bob", "eve")); // eve is user since she's not in g column 1
e.removePolicy("eve", "data3", "read");

// 4. Clear all policies - verify empty results
e.clearPolicy();
testGetAllSubjectsUtil(e, asList());
testGetAllRolesUtil(e, asList());
testGetAllUsersUtil(e, asList());

// 5. Add new user and role relationship: user1 is a member of role "member"
// getAllSubjects: ["user1"] - from p column 0
// getAllRoles: ["member"] - from g column 1 (the second element in grouping)
// getAllUsers: ["user1"] = subjects - roles
e.addPolicy("user1", "data1", "read");
e.addGroupingPolicy("user1", "member");
testGetAllSubjectsUtil(e, asList("user1"));
testGetAllRolesUtil(e, asList("member"));
testGetAllUsersUtil(e, asList("user1"));
}

private void testGetAllSubjectsUtil(Enforcer enforcer, List<String> res) {
List<String> myRes = enforcer.getAllSubjects();
Util.logPrint("All subjects: " + myRes);

if (!Util.setEquals(res, myRes)) {
Assert.fail("All subjects: " + myRes + ", supposed to be " + res);
}
}

private void testGetAllRolesUtil(Enforcer enforcer, List<String> res) {
List<String> myRes = enforcer.getAllRoles();
Util.logPrint("All roles: " + myRes);

if (!Util.setEquals(res, myRes)) {
Assert.fail("All roles: " + myRes + ", supposed to be " + res);
}
}

private void testGetAllUsersUtil(Enforcer enforcer, List<String> res) {
List<String> myRes = enforcer.getAllUsers();
Util.logPrint("All users: " + myRes);

if (!Util.setEquals(res, myRes)) {
Assert.fail("All users: " + myRes + ", supposed to be " + res);
}
}

}
Loading