Skip to content

Commit

Permalink
feat: support AutoSave in built-in FileAdapter (#391)
Browse files Browse the repository at this point in the history
* feat: implement `FileAdapter` addPolicy(), removePolicy()

* fix: unit test compatible with autoSave

* fix: sync

* fix: set fileAdapter autoSave default false

* fix: sync
  • Loading branch information
imp2002 authored Mar 18, 2024
1 parent c56af29 commit c7d1c21
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/main/java/org/casbin/jcasbin/main/CoreEnforcer.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.casbin.jcasbin.model.FunctionMap;
import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.persist.*;
import org.casbin.jcasbin.persist.file_adapter.FileAdapter;
import org.casbin.jcasbin.rbac.DomainManager;
import org.casbin.jcasbin.rbac.RoleManager;
import org.casbin.jcasbin.util.BuiltInFunctions;
Expand Down Expand Up @@ -70,7 +71,7 @@ void initialize() {
watcher = null;

enabled = true;
autoSave = true;
autoSave = adapter instanceof FileAdapter ? false : true;
autoBuildRoleLinks = true;
dispatcher = null;
aviatorEval = AviatorEvaluator.newInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -99,10 +100,10 @@ public void savePolicy(Model model) {

private List<String> getModelPolicy(Model model, String ptype) {
List<String> policy = new ArrayList<>();
model.model.get(ptype).forEach((k, v) -> {
Optional.ofNullable(model.model.get(ptype)).ifPresent(entry -> entry.forEach((k, v) -> {
List<String> p = v.policy.parallelStream().map(x -> k + ", " + Util.arrayToString(x)).collect(Collectors.toList());
policy.addAll(p);
});
}));
return policy;
}

Expand Down Expand Up @@ -130,15 +131,35 @@ private void savePolicyFile(String text) {
*/
@Override
public void addPolicy(String sec, String ptype, List<String> rule) {
throw new UnsupportedOperationException("not implemented");
String ruleText = System.lineSeparator() + ptype + ", " + String.join(", ", rule);
if (byteArrayInputStream != null && readOnly) {
throw new CasbinAdapterException("Policy file can not write, because use inputStream is readOnly");
}
try (FileOutputStream fos = new FileOutputStream(filePath, true)) {
IOUtils.write(ruleText, fos, Charset.forName("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
throw new CasbinAdapterException("Policy add error");
}
}

/**
* removePolicy removes a policy rule from the storage.
*/
@Override
public void removePolicy(String sec, String ptype, List<String> rule) {
throw new UnsupportedOperationException("not implemented");
String ruleText = ptype + ", " + String.join(", ", rule);
if (byteArrayInputStream != null && readOnly) {
throw new CasbinAdapterException("Policy file can not write, because use inputStream is readOnly");
}
try {
List<String> lines = IOUtils.readLines(new FileInputStream(filePath), Charset.forName("UTF-8"));
lines.remove(ruleText);
savePolicyFile(String.join("\n", lines));
} catch (IOException e) {
e.printStackTrace();
throw new CasbinAdapterException("Policy remove error");
}
}

/**
Expand Down
82 changes: 70 additions & 12 deletions src/test/java/org/casbin/jcasbin/main/EnforcerUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

package org.casbin.jcasbin.main;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.persist.Adapter;
import org.casbin.jcasbin.persist.file_adapter.FileAdapter;
Expand All @@ -29,9 +27,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

Expand Down Expand Up @@ -365,22 +361,23 @@ public void testEnableAutoSave() {
testEnforce(e, "bob", "data2", "write", true);

e.enableAutoSave(true);
// Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer,
// but also affects the policy in the storage.
e.removePolicy("alice", "data1", "read");

// However, the file adapter doesn't implement the AutoSave feature, so enabling it has no effect at all here.

testEnforce(e, "bob", "data2", "write", true);
e.removePolicy("bob", "data2", "write");
// Affects the policy in the memory
testEnforce(e, "bob", "data2", "write", false);
// Affects the policy in the adapter
// Reload the policy from the storage to see the effect.
e.loadPolicy();
testEnforce(e, "alice", "data1", "read", true); // Will not be false here.
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);
testEnforce(e, "bob", "data2", "write", false);
// prevent previous operations from affecting the CSV file, add the remove policy
e.addPolicy("bob", "data2", "write");
}

@Test
Expand All @@ -398,6 +395,66 @@ public void testInitWithAdapter() {
testEnforce(e, "bob", "data2", "write", true);
}

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

// test addPolicy() autoSave
e.enableAutoSave(false);
testEnforce(e, "erica", "data3", "read", false);
e.addPolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", true);
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", false);

e.enableAutoSave(true);
testEnforce(e, "erica", "data3", "read", false);
e.addPolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", true);
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", true);

// test removePolicy() autoSave
e.enableAutoSave(false);
e.removePolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", false);
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", true);
e.enableAutoSave(true);
e.removePolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", false);
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", false);

// test savePolicy()
e.enableAutoSave(false);
e.addPolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", true);
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", false);
e.addPolicy("erica", "data3", "read");
testEnforce(e, "erica", "data3", "read", true);
e.savePolicy();
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", true);
e.removePolicy("erica", "data3", "read");
e.savePolicy();
e.loadPolicy();
testEnforce(e, "erica", "data3", "read", false);

// test group policy auto save
e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
e.enableAutoSave(true);
testEnforce(e, "alice", "data2", "read", true);
e.removeGroupingPolicy("alice", "data2_admin");
testEnforce(e, "alice", "data2", "read", false);
e.loadPolicy();
testEnforce(e, "alice", "data2", "read", false);
e.addGroupingPolicy("alice", "data2_admin");
e.loadPolicy();
testEnforce(e, "alice", "data2", "read", true);
}

@Test
public void testRoleLinks() {
Enforcer e = new Enforcer("examples/rbac_model.conf");
Expand Down Expand Up @@ -569,6 +626,7 @@ public void testPriorityExplicit() {
testEnforce(e, "data2_allow_group", "data2", "write", true);

// add a higher priority policy
e.enableAutoSave(false);
e.addPolicy("bob", "data2", "write", "deny", "1");

testEnforce(e, "alice", "data1", "write", true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,22 +394,23 @@ public void testEnableAutoSave() {
testEnforce(e, "bob", "data2", "write", true);

e.enableAutoSave(true);
// Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer,
// but also affects the policy in the storage.
e.removePolicy("alice", "data1", "read");

// However, the file adapter doesn't implement the AutoSave feature, so enabling it has no effect at all here.

testEnforce(e, "bob", "data2", "write", true);
e.removePolicy("bob", "data2", "write");
// Affects the policy in the memory
testEnforce(e, "bob", "data2", "write", false);
// Affects the policy in the adapter
// Reload the policy from the storage to see the effect.
e.loadPolicy();
testEnforce(e, "alice", "data1", "read", true); // Will not be false here.
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);
testEnforce(e, "bob", "data2", "write", false);
// prevent previous operations from affecting the CSV file, add the remove policy
e.addPolicy("bob", "data2", "write");
}

@Test
Expand Down

0 comments on commit c7d1c21

Please sign in to comment.