Skip to content

Commit

Permalink
feat: sync rbac_with_pattern model test cases (#374)
Browse files Browse the repository at this point in the history
  • Loading branch information
LMay001 committed Jan 27, 2024
1 parent 899dc83 commit 00bc8e8
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 5 deletions.
11 changes: 11 additions & 0 deletions examples/basic_model_without_spaces.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[request_definition]
r = sub,obj,act

[policy_definition]
p = sub,obj,act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
10 changes: 10 additions & 0 deletions examples/rbac_with_pattern_policy.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ p, alice, /pen/1, GET
p, alice, /pen2/1, GET
p, book_admin, book_group, GET
p, pen_admin, pen_group, GET
p, *, pen3_group, GET

p, /book/admin/:id, pen4_group, GET
g, /book/user/:id, /book/admin/1

p, /book/leader/2, pen4_group, POST
g, /book/user/:id, /book/leader/2

g, alice, book_admin
g, bob, pen_admin
Expand All @@ -16,3 +23,6 @@ g2, /pen/:id, pen_group

g2, /book2/{id}, book_group
g2, /pen2/{id}, pen_group

g2, /pen3/:id, pen3_group
g2, /pen4/:id, pen4_group
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2024 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.casbin.jcasbin.persist.file_adapter;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.persist.Adapter;
import org.casbin.jcasbin.persist.Helper;

public class AdapterMock implements Adapter {
private String filePath;
private String errorValue;

public AdapterMock(String filePath) {
this.filePath = filePath;
}

public void setMockErr(String errorToSet) {
this.errorValue = errorToSet;
}

public Exception getMockErr() {
if (errorValue != null && !errorValue.isEmpty()) {
return new Exception(errorValue);
}
return null;
}

@Override
public void loadPolicy(Model model) {
try {
loadPolicyFile(model, Helper::loadPolicyLine);
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void savePolicy(Model model) {
}

@Override
public void addPolicy(String sec, String ptype, List<String> rule) {

}

@Override
public void removePolicy(String sec, String ptype, List<String> rule) {
}

@Override
public void removeFilteredPolicy(String sec, String ptype, int fieldIndex, String... fieldValues) {
}

private void loadPolicyFile(Model model, Helper.loadPolicyLineHandler<String, Model> handler) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
handler.accept(line, model);
}
}
}
}
8 changes: 4 additions & 4 deletions src/main/java/org/casbin/jcasbin/rbac/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ void removeMatch(Role role) {
}

void removeMatches() {
this.matched.values().forEach(this::removeMatch);
// https://stackoverflow.com/a/223929/10206831
for (Iterator<Role> iterator = this.matchedBy.values().iterator(); iterator.hasNext();) {
Role role = iterator.next();
Iterator<Map.Entry<String, Role>> iterator = this.matchedBy.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Role> entry = iterator.next();
Role role = entry.getValue();
role.matched.remove(this.name);
iterator.remove();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public static boolean keyMatch(String key1, String key2) {
public static boolean keyMatch2(String key1, String key2) {
key2 = key2.replace("/*", "/.*");
key2 = KEY_MATCH2_PATTERN.matcher(key2).replaceAll("[^/]+");
key2 = key2.replaceAll("\\{([^/]+)\\}", "([^/]+)");
if(Objects.equals(key2, "*")) {
key2 = "(.*)";
}
Expand Down
73 changes: 72 additions & 1 deletion src/test/java/org/casbin/jcasbin/main/ModelUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

package org.casbin.jcasbin.main;

import org.casbin.jcasbin.persist.file_adapter.AdapterMock;
import org.casbin.jcasbin.rbac.RoleManager;
import org.casbin.jcasbin.util.BuiltInFunctions;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

import static org.casbin.jcasbin.main.TestUtil.testDomainEnforce;
Expand All @@ -27,6 +28,20 @@
public class ModelUnitTest {
@Test
public void testBasicModel() {
Enforcer e = new Enforcer("examples/basic_model_without_spaces.conf", "examples/basic_policy.csv");

testEnforce(e, "alice", "data1", "read", true);
testEnforce(e, "alice", "data1", "write", false);
testEnforce(e, "alice", "data2", "read", false);
testEnforce(e, "alice", "data2", "write", false);
testEnforce(e, "bob", "data1", "read", false);
testEnforce(e, "bob", "data1", "write", false);
testEnforce(e, "bob", "data2", "read", false);
testEnforce(e, "bob", "data2", "write", true);
}

@Test
public void testBasicModelWithoutSpaces() {
Enforcer e = new Enforcer("examples/basic_model.conf", "examples/basic_policy.csv");

testEnforce(e, "alice", "data1", "read", true);
Expand Down Expand Up @@ -197,6 +212,25 @@ public void testRBACModelWithDomainsAtRuntime() {
testDomainEnforce(e, "bob", "domain2", "data2", "write", true);
}

@Test
public void testRBACModelWithDomainsAtRuntimeMockAdapter(){
AdapterMock adapter = new AdapterMock("examples/rbac_with_domains_policy.csv");
Enforcer e = new Enforcer("examples/rbac_with_domains_model.conf", adapter);

e.addPolicy("admin", "domain3", "data1", "read");
e.addGroupingPolicy("alice", "admin", "domain3");

testDomainEnforce(e, "alice", "domain3", "data1", "read", true);

testDomainEnforce(e, "alice", "domain1", "data1", "read", true);
e.removeFilteredPolicy(1, "domain1", "data1");
testDomainEnforce(e, "alice", "domain1", "data1", "read", false);

testDomainEnforce(e, "bob", "domain2", "data2", "read", true);
e.removePolicy("admin", "domain2", "data2", "read");
testDomainEnforce(e, "bob", "domain2", "data2", "read", false);
}

@Test
public void testRBACModelWithDeny() {
Enforcer e = new Enforcer("examples/rbac_with_deny_model.conf", "examples/rbac_with_deny_policy.csv");
Expand Down Expand Up @@ -253,6 +287,43 @@ public void testRBACModelWithCustomData() {
testEnforce(e, "bob", "data2", "write", true);
}

@Test
public void testRBACModelWithPattern(){
Enforcer e = new Enforcer("examples/rbac_with_pattern_model.conf", "examples/rbac_with_pattern_policy.csv");

// Here's a little confusing: the matching function here is not the custom function used in matcher.
// It is the matching function used by "g" (and "g2", "g3" if any..)
// You can see in policy that: "g2, /book/:id, book_group", so in "g2()" function in the matcher, instead
// of checking whether "/book/:id" equals the obj: "/book/1", it checks whether the pattern matches.
// You can see it as normal RBAC: "/book/:id" == "/book/1" becomes KeyMatch2("/book/:id", "/book/1")
e.addNamedMatchingFunc("g2", "KeyMatch2", BuiltInFunctions::keyMatch2);
e.addNamedMatchingFunc("g", "KeyMatch2", BuiltInFunctions::keyMatch2);
testEnforce(e, "any_user", "/pen3/1", "GET", true);
testEnforce(e, "/book/user/1", "/pen4/1", "GET", true);

testEnforce(e, "/book/user/1", "/pen4/1", "POST", true);

testEnforce(e, "alice", "/book/1", "GET", true);
testEnforce(e, "alice", "/book/2", "GET", true);
testEnforce(e, "alice", "/pen/1", "GET", true);
testEnforce(e, "alice", "/pen/2", "GET", false);
testEnforce(e, "bob", "/book/1", "GET", false);
testEnforce(e, "bob", "/pen/1", "GET", true);
testEnforce(e, "bob", "/pen/2", "GET", true);

// AddMatchingFunc() is actually setting a function because only one function is allowed,
// so when we set "KeyMatch3", we are actually replacing "KeyMatch2" with "KeyMatch3".
e.addNamedMatchingFunc("g2", "KeyMatch2", BuiltInFunctions::keyMatch3);
testEnforce(e, "alice", "/book2/1", "GET", true);
testEnforce(e, "alice", "/book2/2", "GET", true);
testEnforce(e, "alice", "/pen2/1", "GET", true);
testEnforce(e, "alice", "/pen2/2", "GET", false);
testEnforce(e, "bob", "/book2/1", "GET", false);
testEnforce(e, "bob", "/book2/2", "GET", false);
testEnforce(e, "bob", "/pen2/1", "GET", true);
testEnforce(e, "bob", "/pen2/2", "GET", true);
}

class CustomRoleManager implements RoleManager {
@Override
public void clear() {
Expand Down

0 comments on commit 00bc8e8

Please sign in to comment.