PL-7476 Paging does not work when using in-memory security constraints

This commit is contained in:
Andrey Subbotin 2016-07-11 18:44:24 +04:00
parent e7e0a88060
commit 262eae7582
3 changed files with 71 additions and 17 deletions

View File

@ -65,6 +65,12 @@ public interface PersistenceSecurity extends Security {
*/
void applyConstraints(Collection<Entity> entities);
/**
* Filter entities in collection by in-memory constraints
* @param entities -
*/
boolean filterByConstraints(Collection<Entity> entities);
/**
* Reads security token and restores filtered data
* @param resultEntity -

View File

@ -538,36 +538,53 @@ public class DataManagerBean implements DataManager {
return copy;
}
@SuppressWarnings("unchecked")
protected <E extends Entity> List<E> getResultList(LoadContext<E> context, Query query, boolean ensureDistinct) {
List<E> list = executeQuery(query, false);
if (!ensureDistinct || list.size() == 0)
boolean filteredByConstraints = false;
int initialSize = list.size();
if (initialSize == 0) {
return list;
}
if (needFilterByConstraints(context)) {
filteredByConstraints = security.filterByConstraints((Collection<Entity>) list);
}
if (!ensureDistinct) {
return filteredByConstraints ? getResultListIterative(context, query, list, initialSize) : list;
}
int requestedFirst = context.getQuery().getFirstResult();
LinkedHashSet<E> set = new LinkedHashSet<>(list);
if (set.size() == list.size() && requestedFirst == 0) {
// If this is the first chunk and it has no duplicates, just return it
if (set.size() == list.size() && requestedFirst == 0 && !filteredByConstraints) {
// If this is the first chunk and it has no duplicates and security constraints is not applied, just return it
return list;
}
// In case of not first chunk, even if there where no duplicates, start filling the set from zero
// to ensure correct paging
return getResultListIterative(context, query, set, initialSize);
}
@SuppressWarnings("unchecked")
protected <E extends Entity> List<E> getResultListIterative(LoadContext<E> context, Query query,
Collection<E> filteredCollection,
int initialSize) {
int requestedFirst = context.getQuery().getFirstResult();
int requestedMax = context.getQuery().getMaxResults();
if (requestedMax == 0) {
// set contains all items if query without paging
return new ArrayList<>(set);
return new ArrayList<>(filteredCollection);
}
int setSize = list.size() + requestedFirst;
int factor = list.size() / set.size() * 2;
int setSize = initialSize + requestedFirst;
int factor = initialSize / filteredCollection.size() * 2;
set.clear();
filteredCollection.clear();
int firstResult = 0;
int maxResults = (requestedFirst + requestedMax) * factor;
int i = 0;
while (set.size() < setSize) {
while (filteredCollection.size() < setSize) {
if (i++ > 10) {
log.warn("In-memory distinct: endless loop detected for " + context);
break;
@ -575,19 +592,23 @@ public class DataManagerBean implements DataManager {
query.setFirstResult(firstResult);
query.setMaxResults(maxResults);
//noinspection unchecked
list = query.getResultList();
if (list.size() == 0)
List<E> list = query.getResultList();
if (list.size() == 0) {
break;
set.addAll(list);
}
if (needFilterByConstraints(context)) {
security.filterByConstraints((Collection<Entity>) list);
}
filteredCollection.addAll(list);
firstResult = firstResult + maxResults;
}
// Copy by iteration because subList() returns non-serializable class
int max = Math.min(requestedFirst + requestedMax, set.size());
int max = Math.min(requestedFirst + requestedMax, filteredCollection.size());
List<E> result = new ArrayList<>(max - requestedFirst);
int j = 0;
for (E item : set) {
for (E item : filteredCollection) {
if (j >= max)
break;
if (j >= requestedFirst)
@ -764,6 +785,15 @@ public class DataManagerBean implements DataManager {
return false;
}
protected boolean needFilterByConstraints(LoadContext context) {
if (!isAuthorizationRequired() || !userSessionSource.getUserSession().hasConstraints()) {
return false;
}
MetaClass metaClass = metadata.getSession().getClassNN(context.getMetaClass());
return security.hasConstraints(metaClass);
}
protected Set<Class> collectEntityClasses(View view, Set<View> visited) {
if (visited.contains(view)) {
return Collections.emptySet();

View File

@ -116,6 +116,20 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
}
}
@Override
public boolean filterByConstraints(Collection<Entity> entities) {
boolean filtered = false;
for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext(); ) {
Entity entity = iterator.next();
if (entity instanceof HasUuid && !isPermittedInMemory(entity)) {
//we ignore situations when the collection is immutable
iterator.remove();
filtered = true;
}
}
return filtered;
}
@Override
public void applyConstraints(Collection<Entity> entities) {
List<Entity> supportedEntities = entities.stream().filter(e -> e instanceof HasUuid).collect(Collectors.toList());
@ -226,10 +240,7 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
protected boolean internalApplyConstraints(Entity entity, Set<UUID> handled) {
MetaClass metaClass = entity.getMetaClass();
if (!isPermitted(entity, constraint ->
constraint.getCheckType().memory()
&& (constraint.getOperationType() == ConstraintOperationType.READ
|| constraint.getOperationType() == ConstraintOperationType.ALL))) {
if (!isPermittedInMemory(entity)) {
return true;
}
@ -266,4 +277,11 @@ public class PersistenceSecurityImpl extends SecurityImpl implements Persistence
return false;
}
protected boolean isPermittedInMemory(Entity entity) {
return isPermitted(entity, constraint ->
constraint.getCheckType().memory()
&& (constraint.getOperationType() == ConstraintOperationType.READ
|| constraint.getOperationType() == ConstraintOperationType.ALL));
}
}