Skip to content

Commit

Permalink
feat: sync ModelTest case from Go to Java (#373)
Browse files Browse the repository at this point in the history
* test

* fix: sync ModelTest case from Go to Java

* fix: solve build failed error
  • Loading branch information
LMay001 committed Jan 18, 2024
1 parent cc09162 commit 899dc83
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/main/java/org/casbin/jcasbin/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class Config {
/**
* Config represents the configuration parser.
*/
private Config() {
public Config() {
data = new HashMap<>();
}

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/casbin/jcasbin/model/Assertion.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.casbin.jcasbin.model;

import org.casbin.jcasbin.log.Logger;
import org.casbin.jcasbin.rbac.RoleManager;
import org.casbin.jcasbin.util.Util;

Expand All @@ -34,12 +35,19 @@ public class Assertion {
public Map<String, Integer> policyIndex;
public RoleManager rm;
public int priorityIndex;
private Logger logger;

public Assertion() {
policy = new ArrayList<>();
policyIndex = new HashMap<>();
}

public Assertion(Logger logger) {
policy = new ArrayList<>();
policyIndex = new HashMap<>();
setLogger(logger);
}

protected void buildRoleLinks(RoleManager rm) {
this.rm = rm;
int count = 0;
Expand Down Expand Up @@ -96,4 +104,12 @@ public void buildIncrementalRoleLinks(RoleManager rm, Model.PolicyOperations op,
public void initPriorityIndex() {
priorityIndex = -1;
}

public Logger getLogger() {
return logger;
}

public void setLogger(Logger logger) {
this.logger = logger;
}
}
128 changes: 124 additions & 4 deletions src/main/java/org/casbin/jcasbin/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package org.casbin.jcasbin.model;

import org.casbin.jcasbin.config.Config;
import org.casbin.jcasbin.log.*;
import org.casbin.jcasbin.util.Util;

import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
Expand All @@ -30,7 +32,7 @@
* Model represents the whole access control model.
*/
public class Model extends Policy {
private static final Map<String, String> sectionNameMap;
public static final Map<String, String> sectionNameMap;

static {
sectionNameMap = new HashMap<>();
Expand All @@ -41,6 +43,8 @@ public class Model extends Policy {
sectionNameMap.put("m", "matchers");
}

public static final String[] requiredSections = {"r", "p", "e", "m"};

// used by CoreEnforcer to detect changes to Model
protected int modCount;
private int domainIndex = -1;
Expand Down Expand Up @@ -133,6 +137,49 @@ private void loadSections(Config cfg) {
loadSection(this, cfg, "g");
}

/**
* SetLogger sets the model's logger.
*/
public void setLogger(Logger logger) {
for (Map<String, Assertion> astMap : model.values()) {
for (Assertion ast : astMap.values()) {
ast.setLogger(logger);
}
}
model.put("logger", Collections.singletonMap("logger", new Assertion(logger)));
}

/**
* NewModel creates an empty model.
*/
public static Model newModel() {
Model model = new Model();
model.setLogger(new DefaultLogger());
return model;
}

/**
* NewModelFromString creates a model from a string which contains model text.
*
* @param path the path of the model file.
*/
public static Model newModelFromFile(String path) {
Model model = new Model();
model.loadModel(path);
return model;
}

/**
* NewModelFromString creates a model from a string which contains model text.
*
* @param text the path of the file.
*/
public static Model newModelFromString(String text) {
Model model = new Model();
model.loadModelFromText(text);
return model;
}

/**
* loadModel loads the model from model CONF file.
*
Expand All @@ -155,6 +202,34 @@ public void loadModelFromText(String text) {
loadSections(cfg);
}

/**
* loadModelFromConfig loads the model from the configuration.
*
* @param cfg the model text.
*/
public void loadModelFromConfig(Config cfg) {
for (String s : sectionNameMap.keySet()) {
loadSection(this, cfg, s);
}
List<String> ms = new ArrayList<>();
for (String rs : requiredSections) {
if (!hasSection(rs)) {
ms.add(sectionNameMap.get(rs));
}
}
if (!ms.isEmpty()) {
throw new RuntimeException("missing required sections: " + String.join(",", ms));
}
}

/**
* hasSection checks if the section exists in the model.
*/
public boolean hasSection(String sec) {
Map<String, Assertion> section = model.get(sec);
return section != null;
}

/**
* saveSectionToText saves the section to the text.
*
Expand All @@ -169,7 +244,7 @@ private String saveSectionToText(String sec) {
}

for (Map.Entry<String, Assertion> entry : section.entrySet()) {
res.append(String.format("%s = %s\n", entry.getKey(), entry.getValue().value.replace("_", ".")));
res.append(String.format("%s = %s\n", entry.getKey(), entry.getValue().value.replace("_", ".")));
}

return res.toString();
Expand Down Expand Up @@ -242,7 +317,7 @@ public void sortPoliciesBySubjectHieraichy() {
if (model.get("e") == null || (!"subjectPriority(p_eft) || deny".equals(model.get("e").get("e").value))) {
return;
}

for (Map.Entry<String, Assertion> entry : model.get("p").entrySet()) {
Map<String, Integer> subjectHierarchyMap = getSubjectHierarchyMap(model.get("g").get("g").policy);
Assertion assertion = entry.getValue();
Expand All @@ -268,7 +343,7 @@ public Map<String, Integer> getSubjectHierarchyMap(List<List<String>> policies)
Map<String, Integer> subjectHierarchyMap = new HashMap<>();
Map<String, String> policyMap = new HashMap<>();
String domain = defaultDomain;

for(List<String> policy:policies) {
if(policy.size()!=2) {
domain = policy.get(2);
Expand Down Expand Up @@ -312,4 +387,49 @@ public enum PolicyOperations {
POLICY_ADD,
POLICY_REMOVE
}

public String toText() {
Map<String, String> tokenPatterns = new HashMap<>();

Pattern pPattern = Pattern.compile("^p_");
Pattern rPattern = Pattern.compile("^r_");

for (String ptype : new String[]{"r", "p"}) {
for (String token : model.get(ptype).get(ptype).tokens) {
String newToken = rPattern.matcher(pPattern.matcher(token).replaceAll("p.")).replaceAll("r.");
tokenPatterns.put(token, newToken);
}
}

if (model.get("e").get("e").value.contains("p_eft")) {
tokenPatterns.put("p_eft", "p.eft");
}

StringBuilder s = new StringBuilder();
writeString(s, "r", tokenPatterns);
writeString(s, "p", tokenPatterns);

if (model.containsKey("g")) {
s.append("[role_definition]\n");
for (String ptype : model.get("g").keySet()) {
s.append(String.format("%s = %s\n", ptype, model.get("g").get(ptype).value));
}
}

writeString(s, "e", tokenPatterns);
writeString(s, "m", tokenPatterns);

return s.toString();
}

private void writeString(StringBuilder s, String sec, Map<String, String> tokenPatterns) {
s.append(String.format("[%s]\n", sectionNameMap.get(sec)));
for (String ptype : model.get(sec).keySet()) {
String value = model.get(sec).get(ptype).value;
for (Map.Entry<String, String> entry : tokenPatterns.entrySet()) {
value = value.replace(entry.getKey(), entry.getValue());
}
s.append(String.format("%s = %s\n", sec, value));
}
}
}
155 changes: 155 additions & 0 deletions src/test/java/org/casbin/jcasbin/main/ModelTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// 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.main;

import org.casbin.jcasbin.config.Config;
import org.casbin.jcasbin.model.Model;
import org.junit.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static org.casbin.jcasbin.model.Model.requiredSections;
import static org.casbin.jcasbin.model.Model.sectionNameMap;
import static org.junit.Assert.*;

public class ModelTest {

private String basicExample = "examples/basic_model.conf";

private static final MockConfig basicConfig = new MockConfig();

static {
basicConfig.setData("request_definition::r", "sub, obj, act");
basicConfig.setData("policy_definition::p", "sub, obj, act");
basicConfig.setData("policy_effect::e", "some(where (p.eft == allow))");
basicConfig.setData("matchers::m", "r.sub == p.sub && r.obj == p.obj && r.act == p.act");
}

public static class MockConfig extends Config {
private final Map<String, String> testData = new HashMap<>();

public MockConfig() {
super();
}

private void setData(String key, String value) {
testData.put(key, value);
}

@Override
public String getString(String key) {
if (testData.get(key) == null)
return "";
return testData.get(key);
}
}

@Test
public void testNewModel() {
Model m = Model.newModel();
assertNotNull(m);
}

@Test
public void testNewModelFromFile() {
Model m = Model.newModelFromFile(basicExample);
assertNotNull(m);
}

@Test
public void testNewModelFromString() throws IOException {
String modelString = new String(Files.readAllBytes(Paths.get(basicExample)));
Model m = Model.newModelFromString(modelString);
assertNotNull(m);
}

@Test
public void testLoadModelFromConfig() {
Model m = new Model();
try {
m.loadModelFromConfig(basicConfig);
} catch (Exception e) {
fail("basic config should not return an error");
}

m = new Model();
try {
m.loadModelFromConfig(new MockConfig());
fail("empty config should return error");
} catch (RuntimeException e) {
// check for missing sections in message
for (String rs : requiredSections) {
assertTrue("section name: " + sectionNameMap.get(rs) + " should be in message",
e.getMessage().contains(sectionNameMap.get(rs)));
}
}
}

@Test
public void testHasSection() {
Model m = new Model();
m.loadModelFromConfig(basicConfig);
for (String sec : requiredSections) {
assertTrue(sec + " section was expected in model", m.hasSection(sec));
}

m = new Model();
try {
m.loadModelFromConfig(new MockConfig());
}catch (RuntimeException e){
}finally {
for (String sec : requiredSections) {
assertFalse(sec + " section was not expected in model", m.hasSection(sec));
}
}
}

@Test
public void testModelAddDef() {
Model m = new Model();
assertEquals(true, m.addDef("r", "r", "sub, obj, act"));
assertEquals(false, m.addDef("r", "r", ""));
}

@Test
public void testModelToText() {
testModelToText("r.sub == p.sub && r.obj == p.obj && r_func(r.act, p.act) && testr_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && r_func(r_act, p_act) && testr_func(r_act, p_act)");
testModelToText("r.sub == p.sub && r.obj == p.obj && p_func(r.act, p.act) && testp_func(r.act, p.act)", "r_sub == p_sub && r_obj == p_obj && p_func(r_act, p_act) && testp_func(r_act, p_act)");
}

private void testModelToText(String mData, String mExpected) {
Model m = new Model();

String[] ptypes = {"r", "p", "e", "m"};
String[] values = {"sub, obj, act", "sub, obj, act", "some(where (p.eft == allow))", mData};
String[] expectedValues = {"sub, obj, act", "sub, obj, act", "some(where (p_eft == allow))", mExpected};

for (int i = 0; i < ptypes.length; i++) {
m.addDef(ptypes[i], ptypes[i], values[i]);
}

Model newM = new Model();
System.out.println(m.toText());
newM.loadModelFromText(m.toText());

for (int i = 0; i < ptypes.length; i++) {
assertEquals(expectedValues[i], newM.model.get(ptypes[i]).get(ptypes[i]).value);
}
}
}

0 comments on commit 899dc83

Please sign in to comment.