-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Elide 5.0 - Simplify Security Checks #753
Comments
Implementing #753The requirements of #753 are best satisfied by having @FunctionalInterface
public interface Check {
default String checkIdentifier() {
return this.getClass().getName();
}
} and have 3 sub-types as the aforementioned "three kinds of checks": // Checks which return true/false based on a User object (equivalent of UserCheck)
public interface UserCheck extends Check {
boolean ok(User user);
} // Checks which return true/false based on the entity being read/written plus an optional Change Spec)
public interface OperationCheck<T> extends Check {
default boolean ok(T object, User user) {
return ok(object, user, null);
}
boolean ok(T object, User user, ChangeSpec changeSpec);
} // Checks which return a predicate which can be pushed to the datastore or evaluated in memory.
public interface FilterExpressionCheck<T> extends OperationCheck<T> {
FilterExpression getFilterExpression(Class<?> entityClass, RequestScope requestScope);
}
public abstract class AbstractFilterExpressionCheck<T> implements FilterExpressionCheck<T> {
@Override
public final boolean ok(T object, RequestScope requestScope, ChangeSpec changeSpec) {
Class entityClass = coreScope(requestScope).getDictionary().lookupEntityClass(object.getClass());
FilterExpression filterExpression = getFilterExpression(entityClass, requestScope);
return filterExpression.accept(new FilterExpressionCheckEvaluationVisitor(object, this, requestScope));
}
public boolean applyPredicateToObject(T object, FilterPredicate filterPredicate, RequestScope requestScope) {
try {
String fieldPath = filterPredicate.getFieldPath();
com.yahoo.elide.core.RequestScope scope = coreScope(requestScope);
Predicate fn = filterPredicate.getOperator().contextualize(fieldPath, filterPredicate.getValues(), scope);
return fn.test(object);
} catch (Exception e) {
log.error("Failed to apply predicate {}", filterPredicate, e);
return false;
}
}
protected static Path getFieldPath(Class<?> type, RequestScope requestScope, String method, String defaultPath) {
EntityDictionary dictionary = coreScope(requestScope).getDictionary();
try {
FilterExpressionPath fep = getFilterExpressionPath(type, method, dictionary);
return new Path(type, dictionary, fep == null ? defaultPath : fep.value());
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalStateException(e);
}
}
private static FilterExpressionPath getFilterExpressionPath(
Class<?> type,
String method,
EntityDictionary dictionary) throws NoSuchMethodException {
FilterExpressionPath path = dictionary.lookupEntityClass(type)
.getMethod(method)
.getAnnotation(FilterExpressionPath.class);
return path;
}
protected static com.yahoo.elide.core.RequestScope coreScope(RequestScope requestScope) {
return (com.yahoo.elide.core.RequestScope) requestScope;
}
} It is also suggested to add a reference to With the proposal above, the security framework becomes simpler because instead of intermingle functionalities between different interface types, we now separate them by grouping different checks to sub-types, each contracting a specific check(user, operation, filter expression). Implementations can definitely be made injectable and "newly created objects" can implement any one of the 3 sub-interfaces that are not inline. |
Looks good, but the FilterExpressionCheck looks identical to the code today. Was that intentional or did you plan on making some edits there? A few caveats:
|
Make sense. I see your concern. Let me spend some time figuring out a more rigorous design on Friday. |
@aklish In order to make the operation check final, looks like we want a concrete check that cannot have variant. How about making public class FilterExpressionCheck<T> implements OperationCheck<T> {
@Getter
private final FilterExpression filterExpression;
public FilterExpressionCheck(FilterExpression filterExpression) {
this.filterExpression = Objects.requireNonNull(filterExpression, "filterExpression");
}
@Override
public final boolean ok(T object, RequestScope requestScope, ChangeSpec changeSpec) {
Class entityClass = coreScope(requestScope).getDictionary().lookupEntityClass(object.getClass());
FilterExpression filterExpression = getFilterExpression(entityClass, requestScope);
return filterExpression.accept(new FilterExpressionCheckEvaluationVisitor(object, this, requestScope));
}
public boolean applyPredicateToObject(T object, FilterPredicate filterPredicate, RequestScope requestScope) {
try {
String fieldPath = filterPredicate.getFieldPath();
com.yahoo.elide.core.RequestScope scope = coreScope(requestScope);
Predicate fn = filterPredicate.getOperator().contextualize(fieldPath, filterPredicate.getValues(), scope);
return fn.test(object);
} catch (Exception e) {
log.error("Failed to apply predicate {}", filterPredicate, e);
return false;
}
}
protected static Path getFieldPath(Class<?> type, RequestScope requestScope, String method, String defaultPath) {
EntityDictionary dictionary = coreScope(requestScope).getDictionary();
try {
FilterExpressionPath fep = getFilterExpressionPath(type, method, dictionary);
return new Path(type, dictionary, fep == null ? defaultPath : fep.value());
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalStateException(e);
}
}
private static FilterExpressionPath getFilterExpressionPath(
Class<?> type,
String method,
EntityDictionary dictionary) throws NoSuchMethodException {
FilterExpressionPath path = dictionary.lookupEntityClass(type)
.getMethod(method)
.getAnnotation(FilterExpressionPath.class);
return path;
}
protected static com.yahoo.elide.core.RequestScope coreScope(RequestScope requestScope) {
return (com.yahoo.elide.core.RequestScope) requestScope;
}
} The upside of this is that
|
This issue should encompass the following improvements:
UserCheck
)RequestScope
. Instead they should take aUser
.The text was updated successfully, but these errors were encountered: