Skip to content
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

Defer inline checks when processing relationships #3040

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public class RequestScope implements com.yahoo.elide.core.security.RequestScope
/* Used to filter across heterogeneous types during the first load */
private FilterExpression globalFilterExpression;

/**
* Used to defer inline checks for the {@link PermissionExecutor}.
*/
@Getter @Setter private boolean deferInlineChecks;

/**
* Create a new RequestScope with specified update status code.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public <A extends Annotation> ExpressionResult checkPermission(

Function<Expression, ExpressionResult> expressionExecutor = (expression) -> {
// for newly created object in PatchRequest limit to User checks
if (resource.isNewlyCreated()) {
if (resource.isNewlyCreated() || requestScope.isDeferInlineChecks()) {
return executeUserChecksDeferInline(annotationClass, expression);
}
return executeExpressions(expression, annotationClass, Expression.EvaluationMode.INLINE_CHECKS_ONLY);
Expand Down Expand Up @@ -189,7 +189,7 @@ public <A extends Annotation> ExpressionResult checkSpecificFieldPermissionsDefe
changeSpec);

Function<Expression, ExpressionResult> expressionExecutor = (expression) -> {
if (requestScope.getNewPersistentResources().contains(resource)) {
if (requestScope.getNewPersistentResources().contains(resource) || requestScope.isDeferInlineChecks()) {
return executeUserChecksDeferInline(expressionAnnotation, expression);
}
return executeExpressions(expression, expressionAnnotation, Expression.EvaluationMode.INLINE_CHECKS_ONLY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ private Supplier<Pair<Integer, JsonApiDocument>> handleRemoveOp(String path,
* @param requestScope request scope
*/
private void postProcessRelationships(JsonApiAtomicOperationsRequestScope requestScope) {
requestScope.setDeferInlineChecks(true);
actions.forEach(action -> action.postProcess(requestScope));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ private Supplier<Pair<Integer, JsonApiDocument>> handleRemoveOp(String path,
* @param requestScope request scope
*/
private void postProcessRelationships(JsonApiJsonPatchRequestScope requestScope) {
requestScope.setDeferInlineChecks(true);
actions.forEach(action -> action.postProcess(requestScope));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2023, the original author or authors.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package example.checks;

import com.yahoo.elide.annotation.SecurityCheck;
import com.yahoo.elide.core.Path;
import com.yahoo.elide.core.filter.Operator;
import com.yahoo.elide.core.filter.expression.FilterExpression;
import com.yahoo.elide.core.filter.predicates.FilterPredicate;
import com.yahoo.elide.core.security.RequestScope;
import com.yahoo.elide.core.security.checks.FilterExpressionCheck;
import com.yahoo.elide.core.type.Type;

import example.models.jpa.PermissionAuthor;
import example.models.jpa.PermissionAuthorBook;
import example.models.jpa.PermissionBook;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Check to test that relationship updates have inline checks deferred.
*/
@SecurityCheck("author has user accessible books")
public class AuthorHasUserAccessibleBooks extends FilterExpressionCheck<PermissionAuthor> {

@Override
public FilterExpression getFilterExpression(Type<?> entityClass, RequestScope requestScope) {
Path.PathElement author = new Path.PathElement(PermissionAuthor.class, PermissionAuthorBook.class, "authorBooks");
Path.PathElement book = new Path.PathElement(PermissionAuthorBook.class, PermissionBook.class, "book");
Path.PathElement id = new Path.PathElement(PermissionBook.class, Long.class, "id");

List<Path.PathElement> pathList = new ArrayList<>();
pathList.add(author);
pathList.add(book);
pathList.add(id);
Path paths = new Path(pathList);

// For simplicity this hard codes the book id to 1 and 2 instead of retrieving from the principal
return new FilterPredicate(paths, Operator.IN, Arrays.asList(1, 2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2021, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package example.models.jpa;


import com.yahoo.elide.annotation.Include;
import com.yahoo.elide.annotation.ReadPermission;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

/**
* Model for author.
* <p>
* Used for testing deferred inline checks for
* {@link example.checks.AuthorHasUserAccessibleBooks}.
*/
@Entity
@Table(name = "permissionauthor")
@Include(name = "permissionauthor", description = "Author")
@Getter
@Setter
@ReadPermission(expression = "author has user accessible books")
public class PermissionAuthor {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;

@OneToMany(mappedBy = "author")
private List<PermissionAuthorBook> authorBooks;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2021, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package example.models.jpa;

import com.yahoo.elide.annotation.Include;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

import lombok.Getter;
import lombok.Setter;

/**
* Model for author book.
*/
@Entity
@Table(name = "permissionauthorbook")
@Include(name = "permissionauthorBook", description = "Author Book")
@Getter
@Setter
public class PermissionAuthorBook {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@ManyToOne
@JoinColumn(name = "AUTHOR_ID")
private PermissionAuthor author;

@ManyToOne
@JoinColumn(name = "BOOK_ID")
private PermissionBook book;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2021, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package example.models.jpa;

import com.yahoo.elide.annotation.Include;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

/**
* Model for books.
*/
@Entity
@Table(name = "permissionbook")
@Include(name = "permissionbook", description = "A Book")
@Getter
@Setter
public class PermissionBook {
@Id
private long id;
private String title;

@OneToMany(mappedBy = "book")
private List<PermissionAuthorBook> authorBooks;
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ public void apiDocsDocumentTest() {
.body("tags.name", containsInAnyOrder("group", "argument", "metric",
"dimension", "column", "table", "asyncQuery",
"timeDimensionGrain", "timeDimension", "product", "playerCountry", "version", "playerStats",
"stats", "tableExport", "namespace", "tableSource", "maintainer", "book", "publisher", "person"));
"stats", "tableExport", "namespace", "tableSource", "maintainer", "book", "publisher", "person",
"permissionauthor", "permissionbook", "permissionauthorBook"));
}
}