From 71af67d53d49419a82a9f94ed47981bbfed11600 Mon Sep 17 00:00:00 2001
From: shy <928357057@qq.com>
Date: Tue, 26 Jan 2021 14:30:52 +0800
Subject: [PATCH] Support FilteredAdapter interface.
---
README.md | 7 +-
examples/rbac_with_domains_model.conf | 14 +
examples/rbac_with_domains_policy.csv | 6 +
pom.xml | 4 +-
.../java/org/casbin/adapter/JDBCAdapter.java | 347 ++++-------------
.../org/casbin/adapter/JDBCBaseAdapter.java | 368 ++++++++++++++++++
.../org/casbin/adapter/JDBCAdapterTest.java | 101 +++++
.../casbin/adapter/JDBCAdapterTestSets.java | 12 +-
8 files changed, 584 insertions(+), 275 deletions(-)
create mode 100644 examples/rbac_with_domains_model.conf
create mode 100644 examples/rbac_with_domains_policy.csv
create mode 100644 src/main/java/org/casbin/adapter/JDBCBaseAdapter.java
diff --git a/README.md b/README.md
index 58663ee..329a7c1 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ For Maven:
org.casbin
jdbc-adapter
- 2.0.0
+ 2.0.1
```
@@ -45,9 +45,8 @@ For Maven:
```java
package com.company.test;
-import org.casbin.jcasbin.main.Enforcer;
-import org.casbin.jcasbin.util.Util;
import org.casbin.adapter.JDBCAdapter;
+import org.casbin.jcasbin.main.Enforcer;
import com.mysql.cj.jdbc.MysqlDataSource;
public class Test {
@@ -81,6 +80,8 @@ public class Test {
// Save the policy back to DB.
e.savePolicy();
+ // Close the connection.
+ a.close();
}
}
```
diff --git a/examples/rbac_with_domains_model.conf b/examples/rbac_with_domains_model.conf
new file mode 100644
index 0000000..57c3721
--- /dev/null
+++ b/examples/rbac_with_domains_model.conf
@@ -0,0 +1,14 @@
+[request_definition]
+r = sub, dom, obj, act
+
+[policy_definition]
+p = sub, dom, obj, act
+
+[role_definition]
+g = _, _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
\ No newline at end of file
diff --git a/examples/rbac_with_domains_policy.csv b/examples/rbac_with_domains_policy.csv
new file mode 100644
index 0000000..8558d17
--- /dev/null
+++ b/examples/rbac_with_domains_policy.csv
@@ -0,0 +1,6 @@
+p, admin, domain1, data1, read
+p, admin, domain1, data1, write
+p, admin, domain2, data2, read
+p, admin, domain2, data2, write
+g, alice, admin, domain1
+g, bob, admin, domain2
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2b5c765..c9fdc94 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
org.casbin
jdbc-adapter
- 2.0.0
+ 2.0.1
JDBC Adapter for JCasbin
Load policy from JDBC supported database or save policy to it
@@ -93,7 +93,7 @@
org.casbin
jcasbin
- 1.1.0
+ 1.6.2
mysql
diff --git a/src/main/java/org/casbin/adapter/JDBCAdapter.java b/src/main/java/org/casbin/adapter/JDBCAdapter.java
index eac56e9..f3d0835 100644
--- a/src/main/java/org/casbin/adapter/JDBCAdapter.java
+++ b/src/main/java/org/casbin/adapter/JDBCAdapter.java
@@ -1,4 +1,4 @@
-// Copyright 2018 The casbin Authors. All Rights Reserved.
+// Copyright 2021 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.
@@ -14,35 +14,28 @@
package org.casbin.adapter;
-import org.apache.commons.collections.CollectionUtils;
-import org.casbin.jcasbin.model.Assertion;
+import org.casbin.jcasbin.exception.CasbinAdapterException;
import org.casbin.jcasbin.model.Model;
-import org.casbin.jcasbin.persist.Adapter;
+import org.casbin.jcasbin.persist.FilteredAdapter;
import org.casbin.jcasbin.persist.Helper;
+import org.casbin.jcasbin.persist.file_adapter.FilteredAdapter.Filter;
import javax.sql.DataSource;
-import java.sql.*;
-import java.util.*;
-
-class CasbinRule {
- int id; //Fields reserved for compatibility with other adapters, and the primary key is automatically incremented.
- String ptype;
- String v0;
- String v1;
- String v2;
- String v3;
- String v4;
- String v5;
-}
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
/**
- * JDBCAdapter is the JDBC adapter for jCasbin.
- * It can load policy from JDBC supported database or save policy to it.
+ * JDBCFilteredAdapter is the JDBC adapter for jCasbin.
+ * JDBCFilteredAdapter can load policy from JDBC supported database or save policy to it and it supports filtered policies .
+ *
+ * @author shy
+ * @since 2021/01/26
*/
-public class JDBCAdapter implements Adapter {
- private DataSource dataSource;
- private final int batchSize = 1000;
- private Connection conn;
+public class JDBCAdapter extends JDBCBaseAdapter implements FilteredAdapter {
+
+ private boolean isFiltered = true;
/**
* JDBCAdapter is the constructor for JDBCAdapter.
@@ -53,7 +46,7 @@ public class JDBCAdapter implements Adapter {
* @param password the password of the database.
*/
public JDBCAdapter(String driver, String url, String username, String password) throws Exception {
- this(new JDBCDataSource(driver, url, username, password));
+ super(driver, url, username, password);
}
/**
@@ -62,99 +55,50 @@ public JDBCAdapter(String driver, String url, String username, String password)
* @param dataSource the JDBC DataSource.
*/
public JDBCAdapter(DataSource dataSource) throws Exception {
- this.dataSource = dataSource;
- migrate();
+ super(dataSource);
}
- private void migrate() throws SQLException {
- conn = dataSource.getConnection();
- Statement stmt = conn.createStatement();
- String sql = "CREATE TABLE IF NOT EXISTS casbin_rule(id int NOT NULL PRIMARY KEY auto_increment, ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
- String productName = conn.getMetaData().getDatabaseProductName();
-
- switch (productName) {
- case "Oracle":
- sql = "declare begin execute immediate 'CREATE TABLE CASBIN_RULE(id NUMBER(5, 0) not NULL primary key, ptype VARCHAR(100) not NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))'; " +
- "exception when others then " +
- "if SQLCODE = -955 then " +
- "null; " +
- "else raise; " +
- "end if; " +
- "end;";
- break;
- case "Microsoft SQL Server":
- sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='casbin_rule' and xtype='U') CREATE TABLE casbin_rule(id int NOT NULL primary key identity(1, 1), ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
- break;
- case "PostgreSQL":
- sql = "CREATE SEQUENCE IF NOT EXISTS CASBIN_SEQUENCE START 1;";
- break;
- }
-
- stmt.executeUpdate(sql);
- if (productName.equals("Oracle")) {
- sql = "declare " +
- "V_NUM number;" +
- "BEGIN " +
- "V_NUM := 0; " +
- "select count(0) into V_NUM from user_sequences where sequence_name = 'CASBIN_SEQUENCE';" +
- "if V_NUM > 0 then " +
- "null;" +
- "else " +
- "execute immediate 'CREATE SEQUENCE casbin_sequence increment by 1 start with 1 nomaxvalue nocycle nocache';" +
- "end if;END;";
- stmt.executeUpdate(sql);
- sql = "declare " +
- "V_NUM number;" +
- "BEGIN " +
- "V_NUM := 0;" +
- "select count(0) into V_NUM from user_triggers where trigger_name = 'CASBIN_ID_AUTOINCREMENT';" +
- "if V_NUM > 0 then " +
- "null;" +
- "else " +
- "execute immediate 'create trigger casbin_id_autoincrement before "+
- " insert on CASBIN_RULE for each row "+
- " when (new.id is null) "+
- " begin "+
- " select casbin_sequence.nextval into:new.id from dual;"+
- " end;';" +
- "end if;" +
- "END;";
- stmt.executeUpdate(sql);
- } else if (productName.equals("PostgreSQL")) {
- sql = "CREATE TABLE IF NOT EXISTS casbin_rule(id int NOT NULL PRIMARY KEY default nextval('CASBIN_SEQUENCE'::regclass), ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
- stmt.executeUpdate(sql);
- }
- }
- private void loadPolicyLine(CasbinRule line, Model model) {
- String lineText = line.ptype;
- if (!line.v0.equals("")) {
- lineText += ", " + line.v0;
- }
- if (!line.v1.equals("")) {
- lineText += ", " + line.v1;
- }
- if (!line.v2.equals("")) {
- lineText += ", " + line.v2;
- }
- if (!line.v3.equals("")) {
- lineText += ", " + line.v3;
+ /**
+ * loadFilteredPolicy loads only policy rules that match the filter.
+ *
+ * @param model the model.
+ * @param filter the filter used to specify which type of policy should be loaded.
+ * @throws CasbinAdapterException if the file path or the type of the filter is incorrect.
+ */
+ @Override
+ public void loadFilteredPolicy(Model model, Object filter) throws CasbinAdapterException {
+ if (filter == null) {
+ loadPolicy(model);
+ isFiltered = false;
+ return;
}
- if (!line.v4.equals("")) {
- lineText += ", " + line.v4;
+ if (!(filter instanceof Filter)) {
+ isFiltered = false;
+ throw new CasbinAdapterException("Invalid filter type.");
}
- if (!line.v5.equals("")) {
- lineText += ", " + line.v5;
+ try {
+ loadFilteredPolicyFile(model, (Filter) filter, Helper::loadPolicyLine);
+ isFiltered = true;
+ } catch (Exception e) {
+ e.printStackTrace();
}
-
- Helper.loadPolicyLine(lineText, model);
}
/**
- * loadPolicy loads all policy rules from the storage.
+ * IsFiltered returns true if the loaded policy has been filtered.
+ *
+ * @return true if have any filter roles.
*/
@Override
- public void loadPolicy(Model model) {
+ public boolean isFiltered() {
+ return isFiltered;
+ }
+
+ /**
+ * loadFilteredPolicyFile loads only policy rules that match the filter from file.
+ */
+ private void loadFilteredPolicyFile(Model model, Filter filter, Helper.loadPolicyLineHandler handler) throws CasbinAdapterException {
try (Statement stmt = conn.createStatement()) {
ResultSet rSet = stmt.executeQuery("SELECT * FROM casbin_rule");
ResultSetMetaData rData = rSet.getMetaData();
@@ -177,6 +121,9 @@ public void loadPolicy(Model model) {
line.v5 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
}
}
+ if (filterLine(line, filter)) {
+ continue;
+ }
loadPolicyLine(line, model);
}
rSet.close();
@@ -186,177 +133,41 @@ public void loadPolicy(Model model) {
}
}
- private CasbinRule savePolicyLine(String ptype, List rule) {
- CasbinRule line = new CasbinRule();
-
- line.ptype = ptype;
- if (rule.size() > 0) {
- line.v0 = rule.get(0);
- }
- if (rule.size() > 1) {
- line.v1 = rule.get(1);
- }
- if (rule.size() > 2) {
- line.v2 = rule.get(2);
- }
- if (rule.size() > 3) {
- line.v3 = rule.get(3);
- }
- if (rule.size() > 4) {
- line.v4 = rule.get(4);
- }
- if (rule.size() > 5) {
- line.v5 = rule.get(5);
- }
-
- return line;
- }
-
/**
- * savePolicy saves all policy rules to the storage.
+ * match the line.
*/
- @Override
- public void savePolicy(Model model) {
- String cleanSql = "delete from casbin_rule";
- String addSql = "INSERT INTO casbin_rule (ptype,v0,v1,v2,v3,v4,v5) VALUES(?,?,?,?,?,?,?)";
-
- try {
- conn.setAutoCommit(false);
-
- int count = 0;
-
- try (Statement statement = conn.createStatement(); PreparedStatement ps = conn.prepareStatement(addSql)) {
- statement.execute(cleanSql);
- for (Map.Entry entry : model.model.get("p").entrySet()) {
- String ptype = entry.getKey();
- Assertion ast = entry.getValue();
-
- for (List rule : ast.policy) {
- CasbinRule line = savePolicyLine(ptype, rule);
-
- ps.setString(1, line.ptype);
- ps.setString(2, line.v0);
- ps.setString(3, line.v1);
- ps.setString(4, line.v2);
- ps.setString(5, line.v3);
- ps.setString(6, line.v4);
- ps.setString(7, line.v5);
-
- ps.addBatch();
- if (++count % batchSize == 0) {
- ps.executeBatch();
- }
- }
- }
-
- for (Map.Entry entry : model.model.get("g").entrySet()) {
- String ptype = entry.getKey();
- Assertion ast = entry.getValue();
-
- for (List rule : ast.policy) {
- CasbinRule line = savePolicyLine(ptype, rule);
-
- ps.setString(1, line.ptype);
- ps.setString(2, line.v0);
- ps.setString(3, line.v1);
- ps.setString(4, line.v2);
- ps.setString(5, line.v3);
- ps.setString(6, line.v4);
- ps.setString(7, line.v5);
-
- ps.addBatch();
- if (++count % batchSize == 0) {
- ps.executeBatch();
- }
- }
- }
-
- ps.executeBatch();
-
- conn.commit();
- } catch (SQLException e) {
- conn.rollback();
-
- e.printStackTrace();
- throw new Error(e);
- } finally {
- conn.setAutoCommit(true);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- throw new Error(e);
+ private boolean filterLine(CasbinRule line, Filter filter) {
+ if (filter == null) {
+ return false;
+ }
+ String[] filterSlice = null;
+ switch (line.ptype.trim()) {
+ case "p":
+ filterSlice = filter.p;
+ break;
+ case "g":
+ filterSlice = filter.g;
+ break;
}
- }
-
- /**
- * addPolicy adds a policy rule to the storage.
- */
- @Override
- public void addPolicy(String sec, String ptype, List rule) {
- if (CollectionUtils.isEmpty(rule)) return;
-
- String sql = "INSERT INTO casbin_rule (ptype,v0,v1,v2,v3,v4,v5) VALUES(?,?,?,?,?,?,?)";
-
- try(PreparedStatement ps = conn.prepareStatement(sql)) {
- CasbinRule line = savePolicyLine(ptype, rule);
-
- ps.setString(1, line.ptype);
- ps.setString(2, line.v0);
- ps.setString(3, line.v1);
- ps.setString(4, line.v2);
- ps.setString(5, line.v3);
- ps.setString(6, line.v4);
- ps.setString(7, line.v5);
- ps.addBatch();
- ps.executeBatch();
- } catch (SQLException e) {
- e.printStackTrace();
- throw new Error(e);
+ if (filterSlice == null) {
+ filterSlice = new String[]{};
}
+ return filterWords(line.toStringArray(), filterSlice);
}
/**
- * removePolicy removes a policy rule from the storage.
+ * match the words in the specific line.
*/
- @Override
- public void removePolicy(String sec, String ptype, List rule) {
- if (CollectionUtils.isEmpty(rule)) return;
- removeFilteredPolicy(sec, ptype, 0, rule.toArray(new String[0]));
- }
-
- /**
- * removeFilteredPolicy removes policy rules that match the filter from the storage.
- */
- @Override
- public void removeFilteredPolicy(String sec, String ptype, int fieldIndex, String... fieldValues) {
- List values = Optional.of(Arrays.asList(fieldValues)).orElse(new ArrayList<>());
- if (CollectionUtils.isEmpty(values)) return;
- String sql = "DELETE FROM casbin_rule WHERE ptype = ?";
- int columnIndex = fieldIndex;
- for (int i = 0; i < values.size(); i++) {
- sql = String.format("%s%s%s%s", sql, " AND v", columnIndex, " = ?");
- columnIndex++;
- }
-
- try (PreparedStatement ps = conn.prepareStatement(sql)) {
- ps.setString(1, ptype);
- for (int j = 0; j < values.size(); j++) {
- ps.setString(j + 2, values.get(j));
+ private boolean filterWords(String[] line, String[] filter) {
+ boolean skipLine = false;
+ int i = 0;
+ for (String s : filter) {
+ i++;
+ if (s.length() > 0 && !s.trim().equals(line[i].trim())) {
+ skipLine = true;
+ break;
}
-
- ps.addBatch();
-
- ps.executeBatch();
- } catch (SQLException e) {
- e.printStackTrace();
- throw new Error(e);
}
- }
-
- /**
- * Close the Connection.
- */
- public void close() throws SQLException {
- conn.close();
+ return skipLine;
}
}
diff --git a/src/main/java/org/casbin/adapter/JDBCBaseAdapter.java b/src/main/java/org/casbin/adapter/JDBCBaseAdapter.java
new file mode 100644
index 0000000..79c1a34
--- /dev/null
+++ b/src/main/java/org/casbin/adapter/JDBCBaseAdapter.java
@@ -0,0 +1,368 @@
+// Copyright 2018 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.adapter;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.casbin.jcasbin.main.CoreEnforcer;
+import org.casbin.jcasbin.model.Assertion;
+import org.casbin.jcasbin.model.Model;
+import org.casbin.jcasbin.persist.Adapter;
+import org.casbin.jcasbin.persist.Helper;
+
+import javax.sql.DataSource;
+import java.sql.*;
+import java.util.*;
+
+class CasbinRule {
+ int id; //Fields reserved for compatibility with other adapters, and the primary key is automatically incremented.
+ String ptype;
+ String v0;
+ String v1;
+ String v2;
+ String v3;
+ String v4;
+ String v5;
+
+ public String[] toStringArray() {
+ return new String[]{ptype, v0, v1, v2, v3, v4, v5};
+ }
+}
+
+/**
+ * JDBCAdapter is the JDBC adapter for jCasbin.
+ * It can load policy from JDBC supported database or save policy to it.
+ */
+abstract class JDBCBaseAdapter implements Adapter {
+ private DataSource dataSource;
+ private final int batchSize = 1000;
+ protected Connection conn;
+
+ /**
+ * JDBCAdapter is the constructor for JDBCAdapter.
+ *
+ * @param driver the JDBC driver, like "com.mysql.cj.jdbc.Driver".
+ * @param url the JDBC URL, like "jdbc:mysql://localhost:3306/casbin".
+ * @param username the username of the database.
+ * @param password the password of the database.
+ */
+ protected JDBCBaseAdapter(String driver, String url, String username, String password) throws Exception {
+ this(new JDBCDataSource(driver, url, username, password));
+ }
+
+ /**
+ * The constructor for JDBCAdapter, will not try to create database.
+ *
+ * @param dataSource the JDBC DataSource.
+ */
+ protected JDBCBaseAdapter(DataSource dataSource) throws Exception {
+ this.dataSource = dataSource;
+ migrate();
+ }
+
+
+ protected void migrate() throws SQLException {
+ conn = dataSource.getConnection();
+ Statement stmt = conn.createStatement();
+ String sql = "CREATE TABLE IF NOT EXISTS casbin_rule(id int NOT NULL PRIMARY KEY auto_increment, ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
+ String productName = conn.getMetaData().getDatabaseProductName();
+
+ switch (productName) {
+ case "Oracle":
+ sql = "declare begin execute immediate 'CREATE TABLE CASBIN_RULE(id NUMBER(5, 0) not NULL primary key, ptype VARCHAR(100) not NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))'; " +
+ "exception when others then " +
+ "if SQLCODE = -955 then " +
+ "null; " +
+ "else raise; " +
+ "end if; " +
+ "end;";
+ break;
+ case "Microsoft SQL Server":
+ sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='casbin_rule' and xtype='U') CREATE TABLE casbin_rule(id int NOT NULL primary key identity(1, 1), ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
+ break;
+ case "PostgreSQL":
+ sql = "CREATE SEQUENCE IF NOT EXISTS CASBIN_SEQUENCE START 1;";
+ break;
+ }
+
+ stmt.executeUpdate(sql);
+ if (productName.equals("Oracle")) {
+ sql = "declare " +
+ "V_NUM number;" +
+ "BEGIN " +
+ "V_NUM := 0; " +
+ "select count(0) into V_NUM from user_sequences where sequence_name = 'CASBIN_SEQUENCE';" +
+ "if V_NUM > 0 then " +
+ "null;" +
+ "else " +
+ "execute immediate 'CREATE SEQUENCE casbin_sequence increment by 1 start with 1 nomaxvalue nocycle nocache';" +
+ "end if;END;";
+ stmt.executeUpdate(sql);
+ sql = "declare " +
+ "V_NUM number;" +
+ "BEGIN " +
+ "V_NUM := 0;" +
+ "select count(0) into V_NUM from user_triggers where trigger_name = 'CASBIN_ID_AUTOINCREMENT';" +
+ "if V_NUM > 0 then " +
+ "null;" +
+ "else " +
+ "execute immediate 'create trigger casbin_id_autoincrement before "+
+ " insert on CASBIN_RULE for each row "+
+ " when (new.id is null) "+
+ " begin "+
+ " select casbin_sequence.nextval into:new.id from dual;"+
+ " end;';" +
+ "end if;" +
+ "END;";
+ stmt.executeUpdate(sql);
+ } else if (productName.equals("PostgreSQL")) {
+ sql = "CREATE TABLE IF NOT EXISTS casbin_rule(id int NOT NULL PRIMARY KEY default nextval('CASBIN_SEQUENCE'::regclass), ptype VARCHAR(100) NOT NULL, v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100), v3 VARCHAR(100), v4 VARCHAR(100), v5 VARCHAR(100))";
+ stmt.executeUpdate(sql);
+ }
+ }
+
+ protected void loadPolicyLine(CasbinRule line, Model model) {
+ String lineText = line.ptype;
+ if (!line.v0.equals("")) {
+ lineText += ", " + line.v0;
+ }
+ if (!line.v1.equals("")) {
+ lineText += ", " + line.v1;
+ }
+ if (!line.v2.equals("")) {
+ lineText += ", " + line.v2;
+ }
+ if (!line.v3.equals("")) {
+ lineText += ", " + line.v3;
+ }
+ if (!line.v4.equals("")) {
+ lineText += ", " + line.v4;
+ }
+ if (!line.v5.equals("")) {
+ lineText += ", " + line.v5;
+ }
+
+ Helper.loadPolicyLine(lineText, model);
+ }
+
+ /**
+ * loadPolicy loads all policy rules from the storage.
+ */
+ @Override
+ public void loadPolicy(Model model) {
+ try (Statement stmt = conn.createStatement()) {
+ ResultSet rSet = stmt.executeQuery("SELECT * FROM casbin_rule");
+ ResultSetMetaData rData = rSet.getMetaData();
+ while (rSet.next()) {
+ CasbinRule line = new CasbinRule();
+ for (int i = 1; i <= rData.getColumnCount(); i++) {
+ if (i == 2) {
+ line.ptype = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 3) {
+ line.v0 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 4) {
+ line.v1 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 5) {
+ line.v2 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 6) {
+ line.v3 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 7) {
+ line.v4 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ } else if (i == 8) {
+ line.v5 = rSet.getObject(i) == null ? "" : (String) rSet.getObject(i);
+ }
+ }
+ loadPolicyLine(line, model);
+ }
+ rSet.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new Error(e);
+ }
+ }
+
+ private CasbinRule savePolicyLine(String ptype, List rule) {
+ CasbinRule line = new CasbinRule();
+
+ line.ptype = ptype;
+ if (rule.size() > 0) {
+ line.v0 = rule.get(0);
+ }
+ if (rule.size() > 1) {
+ line.v1 = rule.get(1);
+ }
+ if (rule.size() > 2) {
+ line.v2 = rule.get(2);
+ }
+ if (rule.size() > 3) {
+ line.v3 = rule.get(3);
+ }
+ if (rule.size() > 4) {
+ line.v4 = rule.get(4);
+ }
+ if (rule.size() > 5) {
+ line.v5 = rule.get(5);
+ }
+
+ return line;
+ }
+
+ /**
+ * savePolicy saves all policy rules to the storage.
+ */
+ @Override
+ public void savePolicy(Model model) {
+ String cleanSql = "delete from casbin_rule";
+ String addSql = "INSERT INTO casbin_rule (ptype,v0,v1,v2,v3,v4,v5) VALUES(?,?,?,?,?,?,?)";
+
+ try {
+ conn.setAutoCommit(false);
+
+ int count = 0;
+
+ try (Statement statement = conn.createStatement(); PreparedStatement ps = conn.prepareStatement(addSql)) {
+ statement.execute(cleanSql);
+ for (Map.Entry entry : model.model.get("p").entrySet()) {
+ String ptype = entry.getKey();
+ Assertion ast = entry.getValue();
+
+ for (List rule : ast.policy) {
+ CasbinRule line = savePolicyLine(ptype, rule);
+
+ ps.setString(1, line.ptype);
+ ps.setString(2, line.v0);
+ ps.setString(3, line.v1);
+ ps.setString(4, line.v2);
+ ps.setString(5, line.v3);
+ ps.setString(6, line.v4);
+ ps.setString(7, line.v5);
+
+ ps.addBatch();
+ if (++count % batchSize == 0) {
+ ps.executeBatch();
+ }
+ }
+ }
+
+ for (Map.Entry entry : model.model.get("g").entrySet()) {
+ String ptype = entry.getKey();
+ Assertion ast = entry.getValue();
+
+ for (List rule : ast.policy) {
+ CasbinRule line = savePolicyLine(ptype, rule);
+
+ ps.setString(1, line.ptype);
+ ps.setString(2, line.v0);
+ ps.setString(3, line.v1);
+ ps.setString(4, line.v2);
+ ps.setString(5, line.v3);
+ ps.setString(6, line.v4);
+ ps.setString(7, line.v5);
+
+ ps.addBatch();
+ if (++count % batchSize == 0) {
+ ps.executeBatch();
+ }
+ }
+ }
+
+ ps.executeBatch();
+
+ conn.commit();
+ } catch (SQLException e) {
+ conn.rollback();
+
+ e.printStackTrace();
+ throw new Error(e);
+ } finally {
+ conn.setAutoCommit(true);
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * addPolicy adds a policy rule to the storage.
+ */
+ @Override
+ public void addPolicy(String sec, String ptype, List rule) {
+ if (CollectionUtils.isEmpty(rule)) return;
+
+ String sql = "INSERT INTO casbin_rule (ptype,v0,v1,v2,v3,v4,v5) VALUES(?,?,?,?,?,?,?)";
+
+ try(PreparedStatement ps = conn.prepareStatement(sql)) {
+ CasbinRule line = savePolicyLine(ptype, rule);
+
+ ps.setString(1, line.ptype);
+ ps.setString(2, line.v0);
+ ps.setString(3, line.v1);
+ ps.setString(4, line.v2);
+ ps.setString(5, line.v3);
+ ps.setString(6, line.v4);
+ ps.setString(7, line.v5);
+ ps.addBatch();
+ ps.executeBatch();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * removePolicy removes a policy rule from the storage.
+ */
+ @Override
+ public void removePolicy(String sec, String ptype, List rule) {
+ if (CollectionUtils.isEmpty(rule)) return;
+ removeFilteredPolicy(sec, ptype, 0, rule.toArray(new String[0]));
+ }
+
+ /**
+ * removeFilteredPolicy removes policy rules that match the filter from the storage.
+ */
+ @Override
+ public void removeFilteredPolicy(String sec, String ptype, int fieldIndex, String... fieldValues) {
+ List values = Optional.of(Arrays.asList(fieldValues)).orElse(new ArrayList<>());
+ if (CollectionUtils.isEmpty(values)) return;
+ String sql = "DELETE FROM casbin_rule WHERE ptype = ?";
+ int columnIndex = fieldIndex;
+ for (int i = 0; i < values.size(); i++) {
+ sql = String.format("%s%s%s%s", sql, " AND v", columnIndex, " = ?");
+ columnIndex++;
+ }
+
+ try (PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setString(1, ptype);
+ for (int j = 0; j < values.size(); j++) {
+ ps.setString(j + 2, values.get(j));
+ }
+
+ ps.addBatch();
+
+ ps.executeBatch();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * Close the Connection.
+ */
+ public void close() throws SQLException {
+ conn.close();
+ }
+}
diff --git a/src/test/java/org/casbin/adapter/JDBCAdapterTest.java b/src/test/java/org/casbin/adapter/JDBCAdapterTest.java
index 031fcd2..d69ad6f 100644
--- a/src/test/java/org/casbin/adapter/JDBCAdapterTest.java
+++ b/src/test/java/org/casbin/adapter/JDBCAdapterTest.java
@@ -14,12 +14,18 @@
package org.casbin.adapter;
+import org.casbin.jcasbin.exception.CasbinAdapterException;
+import org.casbin.jcasbin.main.Enforcer;
+import org.casbin.jcasbin.persist.file_adapter.FilteredAdapter;
+import org.junit.Assert;
import org.junit.Test;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
+import static java.util.Arrays.asList;
+import static org.casbin.adapter.JDBCAdapterTestSets.*;
import static org.junit.Assert.fail;
public class JDBCAdapterTest {
@@ -101,4 +107,99 @@ public void testSQLServerAdapter() {
}
});
}
+
+ @Test
+ public void testLoadFilteredPolicyEmptyFilter() throws Exception {
+ JDBCAdapter adapter = new MySQLAdapterCreator().create();
+
+ // Because the DB is empty at first,
+ // so we need to load the policy from the file adapter (.CSV) first.
+ Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
+
+ // This is a trick to save the current policy to the DB.
+ // We can't call e.savePolicy() because the adapter in the enforcer is still the file adapter.
+ // The current policy means the policy in the jCasbin enforcer (aka in memory).
+ adapter.savePolicy(e.getModel());
+
+ // Clear the current policy.
+ e.clearPolicy();
+ testGetPolicy(e, asList());
+
+ // Load the policy from DB.
+ adapter.loadFilteredPolicy(e.getModel(), null);
+ testGetPolicy(e, asList(
+ asList("alice", "data1", "read"),
+ asList("bob", "data2", "write"),
+ asList("data2_admin", "data2", "read"),
+ asList("data2_admin", "data2", "write")));
+
+ // Note: you don't need to look at the above code
+ // if you already have a working DB with policy inside.
+ e = new Enforcer("examples/rbac_model.conf", adapter);
+ testGetPolicy(e, asList(
+ asList("alice", "data1", "read"),
+ asList("bob", "data2", "write"),
+ asList("data2_admin", "data2", "read"),
+ asList("data2_admin", "data2", "write")));
+
+ adapter.close();
+ }
+
+ @Test
+ public void testLoadFilteredPolicyInvalidFilter() throws Exception {
+ JDBCAdapter adapter = new MySQLAdapterCreator().create();
+ // Because the DB is empty at first,
+ // so we need to load the policy from the file adapter (.CSV) first.
+ Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
+
+ // This is a trick to save the current policy to the DB.
+ // We can't call e.savePolicy() because the adapter in the enforcer is still the file adapter.
+ // The current policy means the policy in the jCasbin enforcer (aka in memory).
+ adapter.savePolicy(e.getModel());
+
+ // Clear the current policy.
+ e.clearPolicy();
+ testGetPolicy(e, asList());
+
+ // Load the policy from DB.
+ Assert.assertThrows(CasbinAdapterException.class, () -> adapter.loadFilteredPolicy(e.getModel(), new Object()));
+
+ adapter.close();
+ }
+
+ @Test
+ public void testLoadFilteredPolicy() throws Exception {
+ JDBCAdapter adapter = new MySQLAdapterCreator().create();
+
+ FilteredAdapter.Filter f = new FilteredAdapter.Filter();
+ f.g = new String[]{
+ "", "", "domain1"
+ };
+ f.p = new String[]{
+ "", "domain1"
+ };
+
+ // Because the DB is empty at first,
+ // so we need to load the policy from the file adapter (.CSV) first.
+ Enforcer e = new Enforcer("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv");
+ // This is a trick to save the current policy to the DB.
+ // We can't call e.savePolicy() because the adapter in the enforcer is still the file adapter.
+ // The current policy means the policy in the jCasbin enforcer (aka in memory).
+ adapter.savePolicy(e.getModel());
+
+ testHasPolicy(e, asList("admin", "domain1", "data1", "read"), true);
+ testHasPolicy(e, asList("admin", "domain2", "data2", "read"), true);
+
+ // Clear the current policy.
+ e.clearPolicy();
+ testGetPolicy(e, asList());
+
+ // Load the policy from DB.
+ adapter.loadFilteredPolicy(e.getModel(), f);
+
+ testHasPolicy(e, asList("admin", "domain1", "data1", "read"), true);
+ testHasPolicy(e, asList("admin", "domain2", "data2", "read"), false);
+
+ adapter.close();
+ }
}
diff --git a/src/test/java/org/casbin/adapter/JDBCAdapterTestSets.java b/src/test/java/org/casbin/adapter/JDBCAdapterTestSets.java
index c1440dc..688baed 100644
--- a/src/test/java/org/casbin/adapter/JDBCAdapterTestSets.java
+++ b/src/test/java/org/casbin/adapter/JDBCAdapterTestSets.java
@@ -15,7 +15,6 @@
package org.casbin.adapter;
import org.casbin.jcasbin.main.Enforcer;
-import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.util.Util;
import java.util.List;
@@ -30,7 +29,7 @@ private static void testEnforce(Enforcer e, String sub, Object obj, String act,
assertEquals(res, e.enforce(sub, obj, act));
}
- private static void testGetPolicy(Enforcer e, List> res) {
+ public static void testGetPolicy(Enforcer e, List> res) {
List> myRes = e.getPolicy();
Util.logPrint("Policy: " + myRes);
@@ -39,6 +38,15 @@ private static void testGetPolicy(Enforcer e, List> res) {
}
}
+ public static void testHasPolicy(Enforcer e, List policy, boolean res) {
+ boolean myRes = e.hasPolicy(policy);
+ Util.logPrint("Has policy " + Util.arrayToString(policy) + ": " + myRes);
+
+ if (res != myRes) {
+ fail("Has policy " + Util.arrayToString(policy) + ": " + myRes + ", supposed to be " + res);
+ }
+ }
+
static void testAdapter(JDBCAdapter a) {
// Because the DB is empty at first,
// so we need to load the policy from the file adapter (.CSV) first.