use of com.yahoo.elide.core.Path.PathElement 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.Path.PathElement in project elide by yahoo.
the class VerifyFieldAccessFilterExpressionVisitorTest method testReject.
@Test
public void testReject() {
Path p1Path = new Path(Arrays.asList(new PathElement(Book.class, Author.class, AUTHORS), new PathElement(Author.class, String.class, NAME)));
FilterPredicate p1 = new InPredicate(p1Path, "foo", "bar");
Path p2Path = new Path(Arrays.asList(new PathElement(Book.class, String.class, HOME)));
FilterPredicate p2 = new InPredicate(p2Path, "blah");
Path p3Path = new Path(Arrays.asList(new PathElement(Book.class, String.class, GENRE)));
FilterPredicate p3 = new InPredicate(p3Path, SCIFI);
// P4 is a duplicate of P3
Path p4Path = new Path(Arrays.asList(new PathElement(Book.class, String.class, GENRE)));
FilterPredicate p4 = new InPredicate(p4Path, SCIFI);
OrFilterExpression or = new OrFilterExpression(p2, p3);
AndFilterExpression and1 = new AndFilterExpression(or, p1);
AndFilterExpression and2 = new AndFilterExpression(and1, p4);
NotFilterExpression not = new NotFilterExpression(and2);
Book book = new Book();
Author author = new Author();
book.setAuthors(Collections.singleton(author));
author.setBooks(Collections.singleton(book));
PersistentResource<Book> resource = new PersistentResource<>(book, "", scope);
PermissionExecutor permissionExecutor = scope.getPermissionExecutor();
when(permissionExecutor.checkSpecificFieldPermissions(resource, null, ReadPermission.class, HOME)).thenThrow(ForbiddenAccessException.class);
VerifyFieldAccessFilterExpressionVisitor visitor = new VerifyFieldAccessFilterExpressionVisitor(resource);
// restricted HOME field
assertFalse(not.accept(visitor));
assertFalse(and1.accept(visitor));
assertFalse(and2.accept(visitor));
assertFalse(or.accept(visitor));
assertFalse(p2.accept(visitor));
// unrestricted fields
assertTrue(p1.accept(visitor));
assertTrue(p3.accept(visitor));
assertTrue(p4.accept(visitor));
verify(permissionExecutor, times(8)).evaluateFilterJoinUserChecks(any(), any());
verify(permissionExecutor, times(5)).checkSpecificFieldPermissions(resource, null, ReadPermission.class, HOME);
verify(permissionExecutor, times(9)).checkUserPermissions(any(), any(), isA(String.class));
verify(permissionExecutor, times(5)).handleFilterJoinReject(any(), any(), any());
}
use of com.yahoo.elide.core.Path.PathElement in project elide by yahoo.
the class AsyncAPICancelRunnable method cancelAsyncAPI.
/**
* This method cancels queries based on threshold.
* @param type AsyncAPI Type Implementation.
*/
protected <T extends AsyncAPI> void cancelAsyncAPI(Class<T> type) {
try {
TransactionRegistry transactionRegistry = elide.getTransactionRegistry();
Map<UUID, DataStoreTransaction> runningTransactionMap = transactionRegistry.getRunningTransactions();
// Running transaction UUIDs
Set<UUID> runningTransactionUUIDs = runningTransactionMap.keySet();
// Construct filter expression
PathElement statusPathElement = new PathElement(type, QueryStatus.class, "status");
FilterExpression fltStatusExpression = new InPredicate(statusPathElement, QueryStatus.CANCELLED, QueryStatus.PROCESSING, QueryStatus.QUEUED);
Iterable<T> asyncAPIIterable = asyncAPIDao.loadAsyncAPIByFilter(fltStatusExpression, type);
// Active AsyncAPI UUIDs
Set<UUID> asyncTransactionUUIDs = StreamSupport.stream(asyncAPIIterable.spliterator(), false).filter(query -> query.getStatus() == QueryStatus.CANCELLED || TimeUnit.SECONDS.convert(Math.abs(new Date(System.currentTimeMillis()).getTime() - query.getCreatedOn().getTime()), TimeUnit.MILLISECONDS) > maxRunTimeSeconds).map(query -> UUID.fromString(query.getRequestId())).collect(Collectors.toSet());
// AsyncAPI UUIDs that have active transactions
Set<UUID> queryUUIDsToCancel = Sets.intersection(runningTransactionUUIDs, asyncTransactionUUIDs);
// AsyncAPI IDs that need to be cancelled
Set<String> queryIDsToCancel = queryUUIDsToCancel.stream().map(uuid -> StreamSupport.stream(asyncAPIIterable.spliterator(), false).filter(query -> query.getRequestId().equals(uuid.toString())).map(T::getId).findFirst().orElseThrow(IllegalStateException::new)).collect(Collectors.toSet());
// Cancel Transactions
queryUUIDsToCancel.stream().forEach((uuid) -> {
DataStoreTransaction runningTransaction = transactionRegistry.getRunningTransaction(uuid);
if (runningTransaction != null) {
JsonApiDocument jsonApiDoc = new JsonApiDocument();
MultivaluedMap<String, String> queryParams = new MultivaluedHashMap<>();
RequestScope scope = new RequestScope("", "query", NO_VERSION, jsonApiDoc, runningTransaction, null, queryParams, Collections.emptyMap(), uuid, elide.getElideSettings());
runningTransaction.cancel(scope);
}
});
// Change queryStatus for cancelled queries
if (!queryIDsToCancel.isEmpty()) {
PathElement idPathElement = new PathElement(type, String.class, "id");
FilterExpression fltIdExpression = new InPredicate(idPathElement, queryIDsToCancel);
asyncAPIDao.updateStatusAsyncAPIByFilter(fltIdExpression, QueryStatus.CANCEL_COMPLETE, type);
}
} catch (Exception e) {
log.error("Exception in scheduled cancellation: {}", e.toString());
}
}
use of com.yahoo.elide.core.Path.PathElement in project elide by yahoo.
the class VerifyFieldAccessFilterExpressionVisitor method evaluateUserChecks.
/**
* Scan the Path for user checks.
* <ol>
* <li>If all are PASS, return PASS
* <li>If any FAIL, return FAIL
* <li>Otherwise return DEFERRED
* </ol>
* @param filterPredicate filterPredicate
* @param permissionExecutor permissionExecutor
* @return ExpressionResult
*/
private ExpressionResult evaluateUserChecks(FilterPredicate filterPredicate, PermissionExecutor permissionExecutor) {
PermissionExecutor executor = resource.getRequestScope().getPermissionExecutor();
ExpressionResult ret = ExpressionResult.PASS;
for (PathElement element : filterPredicate.getPath().getPathElements()) {
ExpressionResult result;
try {
result = executor.checkUserPermissions(element.getType(), ReadPermission.class, element.getFieldName());
} catch (ForbiddenAccessException e) {
result = permissionExecutor.handleFilterJoinReject(filterPredicate, element, e);
}
if (result == ExpressionResult.FAIL) {
return ExpressionResult.FAIL;
}
if (result != ExpressionResult.PASS) {
ret = ExpressionResult.DEFERRED;
}
}
return ret;
}
use of com.yahoo.elide.core.Path.PathElement in project elide by yahoo.
the class SubCollectionPageTotalsQueryBuilder method build.
/**
* Constructs a query that returns the count of the members of a relationship.
*
* For a relationship like author#3.books, constructs a query like:
*
* SELECT COUNT(DISTINCT Author_books)
* FROM Author AS Author JOIN Author.books AS Author_books
* WHERE Author.id = :author_books_id;
*
* Rather than query relationship directly (FROM Book), this query starts at the relationship
* owner to support scenarios where there is no inverse relationship from the relationship back to
* the owner.
*
* @return the constructed query
*/
@Override
public Query build() {
Type<?> parentType = dictionary.lookupEntityClass(relationship.getParentType());
Type<?> idType = dictionary.getIdType(parentType);
Object idVal = CoerceUtil.coerce(dictionary.getId(relationship.getParent()), idType);
String idField = dictionary.getIdFieldName(parentType);
// Construct a predicate that selects an individual element of the relationship's parent (Author.id = 3).
FilterPredicate idExpression = new InPredicate(new PathElement(parentType, idType, idField), idVal);
Collection<FilterPredicate> predicates = new ArrayList<>();
String joinClause = "";
String filterClause = "";
String relationshipName = relationship.getRelationshipName();
// Relationship alias is Author_books
String parentAlias = getTypeAlias(parentType);
String relationshipAlias = appendAlias(parentAlias, relationshipName);
FilterExpression filterExpression = entityProjection.getFilterExpression();
if (filterExpression != null) {
// Copy and scope the filter expression for the join clause
ExpressionScopingVisitor visitor = new ExpressionScopingVisitor(new PathElement(parentType, relationship.getChildType(), relationship.getRelationshipName()));
FilterExpression scoped = filterExpression.accept(visitor);
// For each filter predicate, prepend the predicate with the parent:
// books.title = 'Foobar' becomes author.books.title = 'Foobar'
PredicateExtractionVisitor extractor = new PredicateExtractionVisitor(new ArrayList<>());
predicates = scoped.accept(extractor);
predicates.add(idExpression);
// Join together the provided filter expression with the expression which selects the collection owner.
FilterExpression joinedExpression = new AndFilterExpression(scoped, idExpression);
// Build the JOIN clause from the filter predicate
joinClause = getJoinClauseFromFilters(joinedExpression, true);
// Build the WHERE clause
filterClause = new FilterTranslator(dictionary).apply(joinedExpression, USE_ALIAS);
} else {
// If there is no filter, we still need to explicitly JOIN book and authors.
joinClause = JOIN + parentAlias + PERIOD + relationshipName + SPACE + relationshipAlias + SPACE;
filterClause = new FilterTranslator(dictionary).apply(idExpression, USE_ALIAS);
predicates.add(idExpression);
}
Query query = session.createQuery("SELECT COUNT(DISTINCT " + relationshipAlias + ") " + FROM + parentType.getCanonicalName() + AS + parentAlias + SPACE + joinClause + WHERE + filterClause);
// Fill in the query parameters
supplyFilterQueryParameters(query, predicates);
return query;
}
Aggregations