use of com.yahoo.elide.core.exceptions.ForbiddenAccessException in project elide by yahoo.
the class VerifyFieldAccessFilterExpressionVisitor method visitPredicate.
/**
* Enforce ReadPermission on provided query filter.
*
* @return true if allowed, false if rejected
*/
@Override
public Boolean visitPredicate(FilterPredicate filterPredicate) {
RequestScope requestScope = resource.getRequestScope();
Set<PersistentResource> val = Collections.singleton(resource);
PermissionExecutor permissionExecutor = requestScope.getPermissionExecutor();
ExpressionResult result = permissionExecutor.evaluateFilterJoinUserChecks(resource, filterPredicate);
if (result == ExpressionResult.UNEVALUATED) {
result = evaluateUserChecks(filterPredicate, permissionExecutor);
}
if (result == ExpressionResult.PASS) {
return true;
}
if (result == ExpressionResult.FAIL) {
return false;
}
for (PathElement element : filterPredicate.getPath().getPathElements()) {
String fieldName = element.getFieldName();
if ("this".equals(fieldName)) {
continue;
}
try {
val = val.stream().filter(Objects::nonNull).flatMap(x -> getValueChecked(x, fieldName, requestScope).toList(LinkedHashSet::new).blockingGet().stream()).filter(Objects::nonNull).collect(Collectors.toSet());
} catch (ForbiddenAccessException e) {
result = permissionExecutor.handleFilterJoinReject(filterPredicate, element, e);
if (result == ExpressionResult.DEFERRED) {
continue;
}
// pass or fail
return result == ExpressionResult.PASS;
}
}
return true;
}
use of com.yahoo.elide.core.exceptions.ForbiddenAccessException in project elide by yahoo.
the class AbstractPermissionExecutor method executeExpressions.
/**
* Execute expressions.
*
* @param expression The expression to evaluate.
* @param annotationClass The permission associated with the expression.
* @param mode The evaluation mode of the expression.
*/
protected ExpressionResult executeExpressions(final Expression expression, final Class<? extends Annotation> annotationClass, Expression.EvaluationMode mode) {
ExpressionResult result = expression.evaluate(mode);
// Record the check
if (log.isTraceEnabled()) {
String checkKey = expression.toString();
Long checkOccurrences = checkStats.getOrDefault(checkKey, 0L) + 1;
checkStats.put(checkKey, checkOccurrences);
}
if (result == DEFERRED) {
/*
* Checking user checks only are an optimization step. We don't need to defer these checks because
* INLINE_ONLY checks will be evaluated later. Also, the user checks don't have
* the correct context to evaluate as COMMIT checks later.
*/
if (mode == Expression.EvaluationMode.USER_CHECKS_ONLY) {
return DEFERRED;
}
if (isInlineOnlyCheck(annotationClass)) {
// Force evaluation of checks that can only be executed inline.
result = expression.evaluate(Expression.EvaluationMode.ALL_CHECKS);
if (result == FAIL) {
ForbiddenAccessException e = new ForbiddenAccessException(annotationClass, expression, Expression.EvaluationMode.ALL_CHECKS);
if (log.isTraceEnabled()) {
log.trace("{}", e.getLoggedMessage());
}
throw e;
}
return result;
}
commitCheckQueue.add(new AbstractPermissionExecutor.QueuedCheck(expression, annotationClass));
return DEFERRED;
}
if (result == FAIL) {
ForbiddenAccessException e = new ForbiddenAccessException(annotationClass, expression, mode);
if (log.isTraceEnabled()) {
log.trace("{}", e.getLoggedMessage());
}
throw e;
}
return result;
}
use of com.yahoo.elide.core.exceptions.ForbiddenAccessException in project elide by yahoo.
the class ActivePermissionExecutor method executeCommitChecks.
/**
* Execute commmit checks.
*/
@Override
public void executeCommitChecks() {
commitCheckQueue.forEach((expr) -> {
Expression expression = expr.getExpression();
ExpressionResult result = expression.evaluate(Expression.EvaluationMode.ALL_CHECKS);
if (result == FAIL) {
ForbiddenAccessException e = new ForbiddenAccessException(expr.getAnnotationClass(), expression, Expression.EvaluationMode.ALL_CHECKS);
if (log.isTraceEnabled()) {
log.trace("{}", e.getLoggedMessage());
}
throw e;
}
});
commitCheckQueue.clear();
}
use of com.yahoo.elide.core.exceptions.ForbiddenAccessException in project elide by yahoo.
the class VerifyFieldAccessFilterExpressionVisitorTest method testCustomFilterJoin.
@Test
public void testCustomFilterJoin() throws Exception {
RSQLFilterDialect dialect = RSQLFilterDialect.builder().dictionary(scope.getDictionary()).build();
FilterExpression expression = dialect.parseFilterExpression("genre==foo", ClassType.of(Book.class), true);
Book book = new Book();
PersistentResource<Book> resource = new PersistentResource<>(book, "", scope);
PermissionExecutor permissionExecutor = scope.getPermissionExecutor();
DataStoreTransaction tx = scope.getTransaction();
when(permissionExecutor.checkUserPermissions(ClassType.of(Book.class), ReadPermission.class, GENRE)).thenReturn(ExpressionResult.DEFERRED);
when(permissionExecutor.checkSpecificFieldPermissions(resource, null, ReadPermission.class, GENRE)).thenThrow(new ForbiddenAccessException(ReadPermission.class));
when(permissionExecutor.evaluateFilterJoinUserChecks(any(), any())).thenReturn(ExpressionResult.DEFERRED);
when(permissionExecutor.handleFilterJoinReject(any(), any(), any())).thenAnswer(invocation -> {
FilterPredicate filterPredicate = invocation.getArgument(0);
PathElement pathElement = invocation.getArgument(1);
ForbiddenAccessException reason = invocation.getArgument(2);
assertEquals("Book", pathElement.getType().getSimpleName());
assertEquals(GENRE, filterPredicate.getField());
assertEquals("book.genre IN [foo]", filterPredicate.toString());
// custom processing
return "Book".equals(pathElement.getType().getSimpleName()) && filterPredicate.toString().matches("book.genre IN \\[\\w+\\]") && reason.getLoggedMessage().matches(".*Message=ReadPermission Denied.*\\n.*") ? ExpressionResult.DEFERRED : ExpressionResult.FAIL;
});
VerifyFieldAccessFilterExpressionVisitor visitor = new VerifyFieldAccessFilterExpressionVisitor(resource);
// restricted HOME field
assertTrue(expression.accept(visitor));
verify(permissionExecutor, times(1)).evaluateFilterJoinUserChecks(any(), any());
verify(permissionExecutor, times(1)).checkSpecificFieldPermissions(resource, null, ReadPermission.class, GENRE);
verify(permissionExecutor, never()).checkUserPermissions(any(), any(), isA(String.class));
verify(permissionExecutor, times(1)).handleFilterJoinReject(any(), any(), any());
verify(tx, never()).getToManyRelation(any(), any(), any(), any());
}
use of com.yahoo.elide.core.exceptions.ForbiddenAccessException in project elide by yahoo.
the class Elide method handleRuntimeException.
private ElideResponse handleRuntimeException(RuntimeException error, boolean isVerbose) {
CustomErrorException mappedException = mapError(error);
if (mappedException != null) {
return buildErrorResponse(mappedException, isVerbose);
}
if (error instanceof WebApplicationException) {
throw error;
}
if (error instanceof ForbiddenAccessException) {
ForbiddenAccessException e = (ForbiddenAccessException) error;
if (log.isDebugEnabled()) {
log.debug("{}", e.getLoggedMessage());
}
return buildErrorResponse(e, isVerbose);
}
if (error instanceof JsonPatchExtensionException) {
JsonPatchExtensionException e = (JsonPatchExtensionException) error;
log.debug("JSON patch extension exception caught", e);
return buildErrorResponse(e, isVerbose);
}
if (error instanceof HttpStatusException) {
HttpStatusException e = (HttpStatusException) error;
log.debug("Caught HTTP status exception", e);
return buildErrorResponse(e, isVerbose);
}
if (error instanceof ParseCancellationException) {
ParseCancellationException e = (ParseCancellationException) error;
log.debug("Parse cancellation exception uncaught by Elide (i.e. invalid URL)", e);
return buildErrorResponse(new InvalidURLException(e), isVerbose);
}
if (error instanceof ConstraintViolationException) {
ConstraintViolationException e = (ConstraintViolationException) error;
log.debug("Constraint violation exception caught", e);
String message = "Constraint violation";
final ErrorObjects.ErrorObjectsBuilder errorObjectsBuilder = ErrorObjects.builder();
for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
errorObjectsBuilder.addError().withDetail(constraintViolation.getMessage());
final String propertyPathString = constraintViolation.getPropertyPath().toString();
if (!propertyPathString.isEmpty()) {
Map<String, Object> source = new HashMap<>(1);
source.put("property", propertyPathString);
errorObjectsBuilder.with("source", source);
}
}
return buildErrorResponse(new CustomErrorException(HttpStatus.SC_BAD_REQUEST, message, errorObjectsBuilder.build()), isVerbose);
}
log.error("Error or exception uncaught by Elide", error);
throw new RuntimeException(error);
}
Aggregations