This is an automated email from the ASF dual-hosted git repository.

echobravo pushed a commit to branch release/1.12.0
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/release/1.12.0 by this push:
     new 9ca8043  GEODE-7756: Do not use authorization cache for CQs (#4677)
9ca8043 is described below

commit 9ca80438c392db7ab2cd8523662bcb4c76c0ad29
Author: Juan José Ramos <jujora...@users.noreply.github.com>
AuthorDate: Thu Feb 13 11:33:25 2020 +0000

    GEODE-7756: Do not use authorization cache for CQs (#4677)
    
    CQs are executed on individual entries whenever an event is triggered
    for them and the execution context is always cleared before the actual
    evaluation, thus using an internal cache to keep already authorized
    methods is useless (will always be a cache miss and the computation
    required just adds overhead).
    
    - Fixed the CQ creation to always set the 'cqQueryContext' flag.
    - Modified the query engine to avoid using the internal cache for
      already authorized methods when the context belongs to a CQ.
    - Avoid the creation and concatenation of unnecessary Strings, using
      the Method class directly instead.
    
    (cherry picked from commit 5dd7a8420bbe43657abc82e5b8d073eb01b51d8d)
---
 .../cache/query/internal/AttributeDescriptor.java  |  40 ++++----
 .../geode/cache/query/internal/MethodDispatch.java |  40 ++++----
 .../query/internal/QueryExecutionContext.java      |  10 +-
 .../query/internal/AttributeDescriptorTest.java    |  93 +++++++++++++++++--
 .../cache/query/internal/MethodDispatchTest.java   | 103 ++++++++++++++++++++-
 ...tyExecutionContextTamperingDistributedTest.java |   8 +-
 .../geode/cache/query/cq/internal/CqQueryImpl.java |   2 +-
 7 files changed, 248 insertions(+), 48 deletions(-)

diff --git 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/AttributeDescriptor.java
 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/AttributeDescriptor.java
index 2c3fd51..ef8e36d 100644
--- 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/AttributeDescriptor.java
+++ 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/AttributeDescriptor.java
@@ -34,6 +34,7 @@ import org.apache.geode.cache.EntryDestroyedException;
 import org.apache.geode.cache.query.NameNotFoundException;
 import org.apache.geode.cache.query.QueryInvocationTargetException;
 import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.security.MethodInvocationAuthorizer;
 import org.apache.geode.internal.cache.Token;
 import org.apache.geode.internal.lang.JavaWorkarounds;
 import org.apache.geode.pdx.JSONFormatter;
@@ -98,23 +99,30 @@ public class AttributeDescriptor {
       if (m instanceof Method) {
         try {
           Method method = (Method) m;
-          // Try to use previous result so authorizer gets invoked only once 
per query.
-          boolean authorizationResult;
-          String cacheKey = target.getClass().getCanonicalName() + "." + 
method.getName();
-          Boolean cachedResult = (Boolean) executionContext.cacheGet(cacheKey);
-
-          if (cachedResult == null) {
-            // First time, evaluate and cache result.
-            authorizationResult =
-                
executionContext.getMethodInvocationAuthorizer().authorize(method, target);
-            executionContext.cachePut(cacheKey, authorizationResult);
-          } else {
-            // Use cached result.
-            authorizationResult = cachedResult;
-          }
+          MethodInvocationAuthorizer authorizer = 
executionContext.getMethodInvocationAuthorizer();
 
-          if (!authorizationResult) {
-            throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
method.getName());
+          // CQs are generally executed on individual events, so caching is 
just an overhead.
+          if (executionContext.isCqQueryContext()) {
+            if (!authorizer.authorize(method, target)) {
+              throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
method.getName());
+            }
+          } else {
+            // Try to use previous result so authorizer gets invoked only once 
per query.
+            boolean authorizationResult;
+            Boolean cachedResult = (Boolean) executionContext.cacheGet(method);
+
+            if (cachedResult == null) {
+              // First time, evaluate and cache result.
+              authorizationResult = authorizer.authorize(method, target);
+              executionContext.cachePut(method, authorizationResult);
+            } else {
+              // Use cached result.
+              authorizationResult = cachedResult;
+            }
+
+            if (!authorizationResult) {
+              throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
method.getName());
+            }
           }
 
           return method.invoke(target, (Object[]) null);
diff --git 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodDispatch.java
 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodDispatch.java
index 06044aa..98a1d27 100644
--- 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodDispatch.java
+++ 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodDispatch.java
@@ -29,6 +29,7 @@ import org.apache.geode.cache.query.NameNotFoundException;
 import org.apache.geode.cache.query.NameResolutionException;
 import org.apache.geode.cache.query.QueryInvocationTargetException;
 import org.apache.geode.cache.query.internal.types.TypeUtils;
+import org.apache.geode.cache.query.security.MethodInvocationAuthorizer;
 import org.apache.geode.security.NotAuthorizedException;
 
 /**
@@ -57,23 +58,30 @@ public class MethodDispatch {
     Object[] argsArray = args.toArray();
 
     try {
-      // Try to use cached result so authorizer gets invoked only once per 
query.
-      boolean authorizationResult;
-      String cacheKey = target.getClass().getCanonicalName() + "." + 
_method.getName();
-      Boolean cachedResult = (Boolean) executionContext.cacheGet(cacheKey);
-
-      if (cachedResult != null) {
-        // Use cached result.
-        authorizationResult = cachedResult;
-      } else {
-        // First time, evaluate and cache result.
-        authorizationResult =
-            
executionContext.getMethodInvocationAuthorizer().authorize(_method, target);
-        executionContext.cachePut(cacheKey, authorizationResult);
-      }
+      MethodInvocationAuthorizer authorizer = 
executionContext.getMethodInvocationAuthorizer();
 
-      if (!authorizationResult) {
-        throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
_method.getName());
+      // CQs are generally executed on individual events, so caching is just 
an overhead.
+      if (executionContext.isCqQueryContext()) {
+        if (!authorizer.authorize(_method, target)) {
+          throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
_method.getName());
+        }
+      } else {
+        // Try to use cached result so authorizer gets invoked only once per 
query.
+        boolean authorizationResult;
+        Boolean cachedResult = (Boolean) executionContext.cacheGet(_method);
+
+        if (cachedResult != null) {
+          // Use cached result.
+          authorizationResult = cachedResult;
+        } else {
+          // First time, evaluate and cache result.
+          authorizationResult = authorizer.authorize(_method, target);
+          executionContext.cachePut(_method, authorizationResult);
+        }
+
+        if (!authorizationResult) {
+          throw new NotAuthorizedException(UNAUTHORIZED_STRING + 
_method.getName());
+        }
       }
 
       return _method.invoke(target, argsArray);
diff --git 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/QueryExecutionContext.java
 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/QueryExecutionContext.java
index 7371fde..790cac8 100644
--- 
a/geode-core/src/main/java/org/apache/geode/cache/query/internal/QueryExecutionContext.java
+++ 
b/geode-core/src/main/java/org/apache/geode/cache/query/internal/QueryExecutionContext.java
@@ -37,7 +37,7 @@ public class QueryExecutionContext extends ExecutionContext {
 
   private final Query query;
 
-  private boolean cqQueryContext = false;
+  private final boolean cqQueryContext;
 
   private List bucketList;
 
@@ -67,6 +67,14 @@ public class QueryExecutionContext extends ExecutionContext {
   public QueryExecutionContext(Object[] bindArguments, InternalCache cache) {
     super(bindArguments, cache);
     this.query = null;
+    this.cqQueryContext = false;
+  }
+
+  public QueryExecutionContext(Object[] bindArguments, InternalCache cache,
+      boolean cqQueryContext) {
+    super(bindArguments, cache);
+    this.query = null;
+    this.cqQueryContext = cqQueryContext;
   }
 
   public QueryExecutionContext(Object[] bindArguments, InternalCache cache, 
Query query) {
diff --git 
a/geode-core/src/test/java/org/apache/geode/cache/query/internal/AttributeDescriptorTest.java
 
b/geode-core/src/test/java/org/apache/geode/cache/query/internal/AttributeDescriptorTest.java
index f7c2a20..cf6235d 100644
--- 
a/geode-core/src/test/java/org/apache/geode/cache/query/internal/AttributeDescriptorTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/cache/query/internal/AttributeDescriptorTest.java
@@ -75,7 +75,7 @@ public class AttributeDescriptorTest {
     
when(mockService.getMethodAuthorizer()).thenReturn(methodInvocationAuthorizer);
 
     
when(mockCache.getService(QueryConfigurationService.class)).thenReturn(mockService);
-    queryExecutionContext = new QueryExecutionContext(null, mockCache);
+    queryExecutionContext = spy(new QueryExecutionContext(null, mockCache));
   }
 
   @Test
@@ -241,8 +241,23 @@ public class AttributeDescriptorTest {
     AttributeDescriptor attributeDescriptor = new 
AttributeDescriptor(typeRegistry, attributeName);
     attributeDescriptor.readReflection(testBean, queryExecutionContext);
 
-    String cacheKey = TestBean.class.getCanonicalName() + "." + methodName;
-    assertThat(queryExecutionContext.cacheGet(cacheKey)).isEqualTo(true);
+    Method method = TestBean.class.getMethod(methodName);
+    assertThat(queryExecutionContext.cacheGet(method)).isEqualTo(true);
+  }
+
+  @Test
+  @Parameters({
+      "nonPublicAttributeWithPublicAccessor, 
nonPublicAttributeWithPublicAccessor",
+      "nonPublicAttributeWithPublicGetterMethod, 
getNonPublicAttributeWithPublicGetterMethod"})
+  public void 
readReflectionForImplicitMethodInvocationShouldReturnCorrectlyWhenMethodIsAuthorizedAndDoNotCacheResultForCqs(
+      String attributeName, String methodName) throws Exception {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any());
+    AttributeDescriptor attributeDescriptor = new 
AttributeDescriptor(typeRegistry, attributeName);
+    attributeDescriptor.readReflection(testBean, queryExecutionContext);
+
+    Method method = TestBean.class.getMethod(methodName);
+    assertThat(queryExecutionContext.cacheGet(method)).isNull();
   }
 
   @Test
@@ -295,6 +310,31 @@ public class AttributeDescriptorTest {
   }
 
   @Test
+  public void 
readReflectionForImplicitMethodInvocationShouldNotUseCachedAuthorizerResultForCqs()
 {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any());
+    AttributeDescriptor nonPublicAttributeWithPublicAccessor =
+        new AttributeDescriptor(typeRegistry, PRIVATE_ACCESSOR_BY_NAME);
+    AttributeDescriptor nonPublicAttributeWithPublicGetterMethod =
+        new AttributeDescriptor(typeRegistry, PRIVATE_ACCESSOR_BY_GETTER);
+
+    // Same QueryExecutionContext but CQ -> Do not use cache.
+    IntStream.range(0, 10).forEach(element -> {
+      try {
+        assertThat(
+            nonPublicAttributeWithPublicAccessor.readReflection(testBean, 
queryExecutionContext))
+                
.isInstanceOf(String.class).isEqualTo(PRIVATE_ACCESSOR_BY_NAME);
+        
assertThat(nonPublicAttributeWithPublicGetterMethod.readReflection(testBean,
+            queryExecutionContext)).isInstanceOf(String.class)
+                .isEqualTo(PRIVATE_ACCESSOR_BY_GETTER);
+      } catch (NameNotFoundException | QueryInvocationTargetException 
exception) {
+        throw new RuntimeException(exception);
+      }
+    });
+    verify(methodInvocationAuthorizer, times(20)).authorize(any(), any());
+  }
+
+  @Test
   @Parameters({PRIVATE_ACCESSOR_BY_NAME, PRIVATE_ACCESSOR_BY_GETTER})
   public void 
readReflectionForImplicitMethodInvocationShouldThrowExceptionWhenMethodIsNotAuthorized(
       String attributeName) {
@@ -310,8 +350,26 @@ public class AttributeDescriptorTest {
   @Parameters({
       "nonPublicAttributeWithPublicAccessor, 
nonPublicAttributeWithPublicAccessor",
       "nonPublicAttributeWithPublicGetterMethod, 
getNonPublicAttributeWithPublicGetterMethod"})
-  public void 
readReflectionForImplicitMethodInvocationShouldShouldThrowExceptionWhenMethodIsNotAuthorizedAndCacheResult(
-      String attributeName, String methodName) {
+  public void 
readReflectionForImplicitMethodInvocationShouldThrowExceptionWhenMethodIsNotAuthorizedAndCacheResult(
+      String attributeName, String methodName) throws NoSuchMethodException {
+    doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
+    AttributeDescriptor attributeDescriptor = new 
AttributeDescriptor(typeRegistry, attributeName);
+
+    assertThatThrownBy(() -> attributeDescriptor.readReflection(testBean, 
queryExecutionContext))
+        .isInstanceOf(NotAuthorizedException.class)
+        
.hasMessageStartingWith(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING);
+
+    Method method = TestBean.class.getMethod(methodName);
+    assertThat(queryExecutionContext.cacheGet(method)).isEqualTo(false);
+  }
+
+  @Test
+  @Parameters({
+      "nonPublicAttributeWithPublicAccessor, 
nonPublicAttributeWithPublicAccessor",
+      "nonPublicAttributeWithPublicGetterMethod, 
getNonPublicAttributeWithPublicGetterMethod"})
+  public void 
readReflectionForImplicitMethodInvocationShouldThrowExceptionWhenMethodIsNotAuthorizedAndDoNotCacheResultForCqs(
+      String attributeName, String methodName) throws NoSuchMethodException {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
     doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
     AttributeDescriptor attributeDescriptor = new 
AttributeDescriptor(typeRegistry, attributeName);
 
@@ -319,8 +377,8 @@ public class AttributeDescriptorTest {
         .isInstanceOf(NotAuthorizedException.class)
         
.hasMessageStartingWith(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING);
 
-    String cacheKey = TestBean.class.getCanonicalName() + "." + methodName;
-    assertThat(queryExecutionContext.cacheGet(cacheKey)).isEqualTo(false);
+    Method method = TestBean.class.getMethod(methodName);
+    assertThat(queryExecutionContext.cacheGet(method)).isNull();
   }
 
   @Test
@@ -369,6 +427,27 @@ public class AttributeDescriptorTest {
   }
 
   @Test
+  public void 
readReflectionForImplicitMethodInvocationShouldNotUseCachedAuthorizerResultWhenMethodIsForbiddenForCqs()
 {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
+    AttributeDescriptor nonPublicAttributeWithPublicAccessor =
+        new AttributeDescriptor(typeRegistry, PRIVATE_ACCESSOR_BY_NAME);
+    AttributeDescriptor nonPublicAttributeWithPublicGetterMethod =
+        new AttributeDescriptor(typeRegistry, PRIVATE_ACCESSOR_BY_GETTER);
+
+    // Same QueryExecutionContext but CQ -> Do not use cache.
+    IntStream.range(0, 10).forEach(element -> {
+      assertThatThrownBy(() -> 
nonPublicAttributeWithPublicAccessor.readReflection(testBean,
+          queryExecutionContext)).isInstanceOf(NotAuthorizedException.class)
+              
.hasMessageStartingWith(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING);
+      assertThatThrownBy(() -> 
nonPublicAttributeWithPublicGetterMethod.readReflection(testBean,
+          queryExecutionContext)).isInstanceOf(NotAuthorizedException.class)
+              
.hasMessageStartingWith(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING);
+    });
+    verify(methodInvocationAuthorizer, times(20)).authorize(any(), any());
+  }
+
+  @Test
   public void readShouldReturnUndefinedForNullOrUndefinedTargetObject()
       throws NameNotFoundException, QueryInvocationTargetException {
     AttributeDescriptor attributeDescriptor = new 
AttributeDescriptor(typeRegistry, "whatever");
diff --git 
a/geode-core/src/test/java/org/apache/geode/cache/query/internal/MethodDispatchTest.java
 
b/geode-core/src/test/java/org/apache/geode/cache/query/internal/MethodDispatchTest.java
index eaae032..8e58223 100644
--- 
a/geode-core/src/test/java/org/apache/geode/cache/query/internal/MethodDispatchTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/cache/query/internal/MethodDispatchTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.IntStream;
@@ -47,7 +48,7 @@ public class MethodDispatchTest {
   private static final String EXTENDED_PUBLIC_METHOD_NAME = "publicMethod";
   private static final String EXTENDED_PUBLIC_METHOD_RETURN_VALUE = 
"extendedPublic";
 
-  private List emptyList;
+  private List<?> emptyList;
   private TestBean testBean;
   private QueryExecutionContext queryExecutionContext;
   private MethodInvocationAuthorizer methodInvocationAuthorizer;
@@ -63,7 +64,7 @@ public class MethodDispatchTest {
     InternalCache mockCache = mock(InternalCache.class);
     
when(mockCache.getService(QueryConfigurationService.class)).thenReturn(mockService);
 
-    queryExecutionContext = new QueryExecutionContext(null, mockCache);
+    queryExecutionContext = spy(new QueryExecutionContext(null, mockCache));
   }
 
   @Test
@@ -95,8 +96,21 @@ public class MethodDispatchTest {
     MethodDispatch methodDispatch =
         new MethodDispatch(Integer.class, "toString", Collections.emptyList());
 
+    Method toStringMethod = Integer.class.getMethod("toString");
     methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext);
-    
assertThat(queryExecutionContext.cacheGet("java.lang.Integer.toString")).isEqualTo(true);
+    assertThat(queryExecutionContext.cacheGet(toStringMethod)).isEqualTo(true);
+  }
+
+  @Test
+  public void invokeShouldNotCacheResultForAllowedMethodOnCqs() throws 
Exception {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any());
+    MethodDispatch methodDispatch =
+        new MethodDispatch(Integer.class, "toString", Collections.emptyList());
+
+    Method toStringMethod = Integer.class.getMethod("toString");
+    methodDispatch.invoke(new Integer("0"), emptyList, queryExecutionContext);
+    assertThat(queryExecutionContext.cacheGet(toStringMethod)).isNull();
   }
 
   @Test
@@ -109,7 +123,25 @@ public class MethodDispatchTest {
         () -> methodDispatch.invoke(new Integer("0"), emptyList, 
queryExecutionContext))
             .isInstanceOf(NotAuthorizedException.class)
             .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + 
"toString");
-    
assertThat(queryExecutionContext.cacheGet("java.lang.Integer.toString")).isEqualTo(false);
+
+    Method toStringMethod = Integer.class.getMethod("toString");
+    
assertThat(queryExecutionContext.cacheGet(toStringMethod)).isEqualTo(false);
+  }
+
+  @Test
+  public void invokeShouldNotCacheResultForForbiddenMethodOnCqs() throws 
Exception {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
+    MethodDispatch methodDispatch =
+        new MethodDispatch(Integer.class, "toString", Collections.emptyList());
+
+    assertThatThrownBy(
+        () -> methodDispatch.invoke(new Integer("0"), emptyList, 
queryExecutionContext))
+            .isInstanceOf(NotAuthorizedException.class)
+            .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + 
"toString");
+
+    Method toStringMethod = Integer.class.getMethod("toString");
+    assertThat(queryExecutionContext.cacheGet(toStringMethod)).isNull();
   }
 
   @Test
@@ -174,6 +206,36 @@ public class MethodDispatchTest {
   }
 
   @Test
+  public void 
invokeShouldNotUseCachedAuthorizerResultWhenMethodIsAuthorizedAndQueryContextIsTheSameForCqs()
+      throws NameResolutionException {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(true).when(methodInvocationAuthorizer).authorize(any(), any());
+    MethodDispatch publicMethodDispatch =
+        new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, 
Collections.emptyList());
+    MethodDispatch anotherPublicMethodDispatch =
+        new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, 
Collections.emptyList());
+    MethodDispatch extendedPublicMethodDispatch =
+        new MethodDispatch(TestBeanExtension.class, 
EXTENDED_PUBLIC_METHOD_NAME,
+            Collections.emptyList());
+
+    // Same QueryExecutionContext -> Use cache.
+    IntStream.range(0, 10).forEach(element -> {
+      try {
+        assertThat(publicMethodDispatch.invoke(testBean, emptyList, 
queryExecutionContext))
+            .isInstanceOf(String.class).isEqualTo(PUBLIC_METHOD_RETURN_VALUE);
+        assertThat(anotherPublicMethodDispatch.invoke(testBean, emptyList, 
queryExecutionContext))
+            
.isInstanceOf(String.class).isEqualTo(ANOTHER_PUBLIC_METHOD_RETURN_VALUE);
+        assertThat(extendedPublicMethodDispatch
+            .invoke(new TestBeanExtension(), emptyList, queryExecutionContext))
+                
.isInstanceOf(String.class).isEqualTo(EXTENDED_PUBLIC_METHOD_RETURN_VALUE);
+      } catch (NameNotFoundException | QueryInvocationTargetException 
exception) {
+        throw new RuntimeException(exception);
+      }
+    });
+    verify(methodInvocationAuthorizer, times(30)).authorize(any(), any());
+  }
+
+  @Test
   public void 
invokeShouldUseCachedAuthorizerResultWhenMethodIsNotAuthorizedAndContextIsTheSame()
       throws NameResolutionException {
     doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
@@ -237,6 +299,39 @@ public class MethodDispatchTest {
     verify(methodInvocationAuthorizer, times(30)).authorize(any(), any());
   }
 
+  @Test
+  public void 
invokeShouldNotUseCachedAuthorizerResultWhenMethodIsNotAuthorizedAndContextIsTheSameForCqs()
+      throws NameResolutionException {
+    when(queryExecutionContext.isCqQueryContext()).thenReturn(true);
+    doReturn(false).when(methodInvocationAuthorizer).authorize(any(), any());
+    MethodDispatch publicMethodDispatch =
+        new MethodDispatch(TestBean.class, PUBLIC_METHOD_NAME, 
Collections.emptyList());
+    MethodDispatch anotherPublicMethodDispatch =
+        new MethodDispatch(TestBean.class, ANOTHER_PUBLIC_METHOD_NAME, 
Collections.emptyList());
+    MethodDispatch extendedPublicMethodDispatch =
+        new MethodDispatch(TestBeanExtension.class, 
EXTENDED_PUBLIC_METHOD_NAME,
+            Collections.emptyList());
+
+    // Same QueryExecutionContext -> Use cache.
+    IntStream.range(0, 10).forEach(element -> {
+      assertThatThrownBy(
+          () -> publicMethodDispatch.invoke(testBean, emptyList, 
queryExecutionContext))
+              .isInstanceOf(NotAuthorizedException.class)
+              .hasMessage(RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + 
PUBLIC_METHOD_NAME);
+      assertThatThrownBy(
+          () -> anotherPublicMethodDispatch.invoke(testBean, emptyList, 
queryExecutionContext))
+              .isInstanceOf(NotAuthorizedException.class)
+              .hasMessage(
+                  RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + 
ANOTHER_PUBLIC_METHOD_NAME);
+      assertThatThrownBy(() -> extendedPublicMethodDispatch
+          .invoke(new TestBeanExtension(), emptyList, queryExecutionContext))
+              .isInstanceOf(NotAuthorizedException.class)
+              .hasMessage(
+                  RestrictedMethodAuthorizer.UNAUTHORIZED_STRING + 
EXTENDED_PUBLIC_METHOD_NAME);
+    });
+    verify(methodInvocationAuthorizer, times(30)).authorize(any(), any());
+  }
+
   @SuppressWarnings("unused")
   private static class TestBean {
     public String publicMethod() {
diff --git 
a/geode-cq/src/distributedTest/java/org/apache/geode/cache/query/cq/internal/CqSecurityExecutionContextTamperingDistributedTest.java
 
b/geode-cq/src/distributedTest/java/org/apache/geode/cache/query/cq/internal/CqSecurityExecutionContextTamperingDistributedTest.java
index d681739..92eccbf 100644
--- 
a/geode-cq/src/distributedTest/java/org/apache/geode/cache/query/cq/internal/CqSecurityExecutionContextTamperingDistributedTest.java
+++ 
b/geode-cq/src/distributedTest/java/org/apache/geode/cache/query/cq/internal/CqSecurityExecutionContextTamperingDistributedTest.java
@@ -21,6 +21,7 @@ import static 
org.apache.geode.test.awaitility.GeodeAwaitility.await;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.Serializable;
+import java.lang.reflect.Method;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -49,7 +50,7 @@ import org.apache.geode.test.junit.categories.SecurityTest;
 import 
org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
 
 /**
- * Verifies that users can tamper the {@link ExecutionContext}.
+ * Verifies that users can not tamper the {@link ExecutionContext}.
  * It needs to be part of the {@link org.apache.geode.cache.query.cq.internal} 
package because the
  * method {@link CqQueryImpl#getQueryExecutionContext()} has package access.
  */
@@ -121,8 +122,9 @@ public class 
CqSecurityExecutionContextTamperingDistributedTest implements Seria
       assertThat(internalCache.getCqService().getAllCqs().size()).isEqualTo(1);
       CqQueryImpl cqQueryImpl =
           (CqQueryImpl) 
internalCache.getCqService().getAllCqs().iterator().next();
-      
ExecutionContextTamperer.tamperContextCache(cqQueryImpl.getQueryExecutionContext(),
-          "org.apache.geode.security.query.data.QueryTestObject.getName", 
true);
+      Method method = QueryTestObject.class.getMethod("getName");
+      
ExecutionContextTamperer.tamperContextCache(cqQueryImpl.getQueryExecutionContext(),
 method,
+          true);
 
       Region<String, QueryTestObject> region = 
ClusterStartupRule.getCache().getRegion(regionName);
       region.put("1", new QueryTestObject(1, "Beth"));
diff --git 
a/geode-cq/src/main/java/org/apache/geode/cache/query/cq/internal/CqQueryImpl.java
 
b/geode-cq/src/main/java/org/apache/geode/cache/query/cq/internal/CqQueryImpl.java
index 66fc6cf..eed5568 100644
--- 
a/geode-cq/src/main/java/org/apache/geode/cache/query/cq/internal/CqQueryImpl.java
+++ 
b/geode-cq/src/main/java/org/apache/geode/cache/query/cq/internal/CqQueryImpl.java
@@ -210,7 +210,7 @@ public abstract class CqQueryImpl implements 
InternalCqQuery {
 
     // Set Query ExecutionContext, that will be used in later execution.
     this.setQueryExecutionContext(
-        new QueryExecutionContext(null, (InternalCache) 
this.cqService.getCache()));
+        new QueryExecutionContext(null, (InternalCache) 
this.cqService.getCache(), true));
   }
 
   /**

Reply via email to