diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java index 07a165a352ba9a07a345018e7d287cd961f4a00b..2697b77c316defc085a95be664e5d77f597f2824 100644 --- a/src/main/java/org/caosdb/server/query/Query.java +++ b/src/main/java/org/caosdb/server/query/Query.java @@ -33,9 +33,9 @@ import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -694,8 +694,6 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac this.cached = true; } - this.resultSet = filterEntitiesWithoutRetrievePermission(this.resultSet); - // Fill resulting entities into container if (this.container != null && this.type == Type.FIND) { for (final IdVersionPair p : this.resultSet) { @@ -749,7 +747,10 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac protected void executeNoCache(final Access access) { try { - this.resultSet = getResultSet(executeStrategy(this.versioned), this.versioned); + final String tabname = executeStrategy(this.versioned); + filterEntitiesWithoutRetrievePermission(tabname); + + this.resultSet = getResultSet(tabname, this.versioned); } finally { cleanUp(); } @@ -793,66 +794,61 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac * @throws SQLException * @throws TransactionException */ - public void filterEntitiesWithoutRetrievePermission(final String resultSet) - throws SQLException, TransactionException { + public void filterEntitiesWithoutRetrievePermission(final String resultSet) { if (!filterEntitiesWithoutRetrievePermisions) { return; } cachable = false; - try (final Statement stmt = this.getConnection().createStatement()) { - final ResultSet rs = stmt.executeQuery("SELECT id from `" + resultSet + "`"); - final List<Integer> toBeDeleted = new LinkedList<Integer>(); - while (rs.next()) { - final long t1 = System.currentTimeMillis(); - final Integer id = rs.getInt("id"); - if (id > 99 - && !execute(new RetrieveSparseEntity(id, null), this.getAccess()) - .getEntity() - .getEntityACL() - .isPermitted(this.getUser(), EntityPermission.RETRIEVE_ENTITY)) { - toBeDeleted.add(id); - } - final long t2 = System.currentTimeMillis(); - this.addBenchmark("filterEntitiesWithoutRetrievePermission", t2 - t1); - } - rs.close(); - for (final Integer id : toBeDeleted) { - stmt.execute("DELETE FROM `" + resultSet + "` WHERE id = " + id); + + /* + * The following creates a table with the columns (entity ID, acl) from + * a given table with entity IDs. Here, acl is the string representation + * of the acl. + * + * TODO:In future, one might want to retrieve only a distinct set of acl + * with (acl_id, acl) and a table with (entity_id, acl_id) to reduce the + * amount of data being transfered. + */ + final Statement stmt = this.getConnection().createStatement() + final String query = + ("SELECT entity_n_acl.id, entity_acl.acl from " + + "(select entities.id, entities.acl from entities " + + "inner join `" + resultSet + " as rs on entities.id=rs.id`) " + + "as entity_n_acl " + + "left join entity_acl on entity_n_acl.acl=entity_acl.id;"); + final ResultSet entities_with_acl = stmt.executeQuery(query); + + final HashMap<String, Boolean> acl_cache = new HashMap<String, Boolean>(); + final List<Integer> toBeDeleted = new LinkedList<Integer>(); + + // @todo here, we must operate on sql site. only retrieve different permissions + while (entities_with_acl.next()) { + final long t1 = System.currentTimeMillis(); + final Integer id = entities_with_acl.getInt("id"); + if (id <= 99) { + continue; } - } - } - /** - * Filter out all entities which may not be retrieved by this user due to a missing RETRIEVE - * permission. This one is for the filtering of the final result set and not for the filtering of - * any intermediate results. - * - * @param entities - * @throws TransactionException - * @return the filtered list. - */ - private List<IdVersionPair> filterEntitiesWithoutRetrievePermission( - final List<IdVersionPair> entities) throws TransactionException { - if (!filterEntitiesWithoutRetrievePermisions) { - return entities; - } + final String acl_str = bytes2UTF8(entities_with_acl.getBytes("ACL")); + if (!acl_cache.containsKey(acl_str)) { + acl_cache.put( + acl_str, + EntityACL.deserialize(acl_str) + .isPermitted(this.getUser(), EntityPermission.RETRIEVE_ENTITY)); + } - final List<IdVersionPair> result = new ArrayList<>(); - final Iterator<IdVersionPair> iterator = entities.iterator(); - while (iterator.hasNext()) { - final long t1 = System.currentTimeMillis(); - final IdVersionPair next = iterator.next(); - if (next.id > 99 - && execute(new RetrieveSparseEntity(next.id, next.version), getAccess()) - .getEntity() - .getEntityACL() - .isPermitted(getUser(), EntityPermission.RETRIEVE_ENTITY)) { - result.add(next); + if (!acl_cache.get(acl_str)) { + toBeDeleted.add(id); } + final long t2 = System.currentTimeMillis(); - addBenchmark("filterEntitiesWithoutRetrievePermission", t2 - t1); + this.addBenchmark("filterEntitiesWithoutRetrievePermission", t2 - t1); + } + entities_with_acl.close(); + // TODO is there a better way than the following? + for (final Integer id : toBeDeleted) { + stmt.execute("DELETE FROM `" + resultSet + "` WHERE id = " + id); } - return result; } @Override