You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ja...@apache.org on 2017/10/03 21:54:18 UTC

[geode] branch develop updated: GEODE-3247:Improve OQL expression execution (#837)

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

jasonhuynh pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 02b9646  GEODE-3247:Improve OQL expression execution (#837)
02b9646 is described below

commit 02b9646618e074f80b3d5fed0e5b512a34b5897a
Author: Jason Huynh <hu...@gmail.com>
AuthorDate: Tue Oct 3 14:54:15 2017 -0700

    GEODE-3247:Improve OQL expression execution (#837)
    
    * GEODE-3247: Restrict method invocation in OQL when new security service is enabled
    
    * GEODE-3248: Regions being passed in through bind parameters should have authorized access
    
    * GEODE-3247: MethodInvocationAuthorizer is created at start up
    
      * MethodInvocationAuthorizer can now be passed down from InternalCache
      * Broke apart new tests into smaller files and removed complex query maps from UserPerms
      * Modified Customer Test Object for Rest Security tests
---
 .../rest/internal/web/controllers/Customer.java    |   2 +-
 .../cache/query/internal/AttributeDescriptor.java  |  58 ++--
 .../cache/query/internal/CompiledOperation.java    |   4 +-
 .../geode/cache/query/internal/CompiledPath.java   |   2 +-
 .../geode/cache/query/internal/CompiledSelect.java |   3 +-
 .../geode/cache/query/internal/DefaultQuery.java   |   1 -
 .../cache/query/internal/DefaultQueryService.java  |  21 +-
 .../cache/query/internal/ExecutionContext.java     |   2 +-
 .../cache/query/internal/InternalQueryService.java |  22 ++
 .../geode/cache/query/internal/MethodDispatch.java |  27 +-
 .../query/internal/MethodInvocationAuthorizer.java |  21 ++
 .../geode/cache/query/internal/PathUtils.java      |  33 ++-
 .../RestrictedMethodInvocationAuthorizer.java      | 135 +++++++++
 .../cache/query/internal/RuntimeIterator.java      |  22 +-
 .../geode/cache/query/internal/StructImpl.java     |  17 +-
 .../geode/internal/cache/GemFireCacheImpl.java     |   9 +-
 .../apache/geode/internal/cache/InternalCache.java |   3 +
 .../internal/cache/xmlcache/CacheCreation.java     |  10 +-
 .../geode/internal/i18n/LocalizedStrings.java      |   2 +
 .../RestrictedMethodInvocationAuthorizerTest.java  | 264 +++++++++++++++++
 ...ServerBridgeClientMembershipRegressionTest.java |   3 +-
 .../apache/geode/security/TestSecurityManager.java |  25 +-
 .../security/query/IndexSecurityDUnitTest.java     | 105 +++++++
 .../query/PartitionedIndexSecurityDUnitTest.java   |  33 +++
 ...tionedQuerySecurityAllowedQueriesDUnitTest.java |  37 +++
 ...nedQuerySecurityRestrictedQueriesDUnitTest.java |  37 +++
 ...tionedQuerySecurityAllowedQueriesDUnitTest.java |  33 +++
 .../PdxQuerySecurityAllowedQueriesDUnitTest.java   |  74 +++++
 ...PdxQuerySecurityRestrictedQueriesDUnitTest.java |  80 ++++++
 .../QuerySecurityAllowedQueriesDUnitTest.java      | 255 +++++++++++++++++
 ...curityAuthorizedUserBindParameterDUnitTest.java |  53 ++++
 .../geode/security/query/QuerySecurityBase.java    | 222 ++++++++++++++
 .../QuerySecurityRestrictedQueriesDUnitTest.java   | 318 +++++++++++++++++++++
 ...rityUnauthorizedUserBindParameterDUnitTest.java |  76 +++++
 .../geode/security/query/UserPermissions.java      |  46 +++
 .../security/query/data/PdxQueryTestObject.java    |  73 +++++
 .../geode/security/query/data/QueryTestObject.java |  65 +++++
 .../management/internal/security/clientServer.json | 104 ++++++-
 .../dunit/CqSecurityAuthorizedUserDUnitTest.java   | 267 +++++++++++++++++
 ...SecurityPartitionedAuthorizedUserDUnitTest.java |  65 +++++
 ...curityPartitionedUnauthorizedUserDUnitTest.java |  45 +++
 .../dunit/CqSecurityUnauthorizedUserDUnitTest.java | 215 ++++++++++++++
 42 files changed, 2803 insertions(+), 86 deletions(-)

diff --git a/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/controllers/Customer.java b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/controllers/Customer.java
index 9732aa0..c2dacbf 100644
--- a/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/controllers/Customer.java
+++ b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/controllers/Customer.java
@@ -31,7 +31,7 @@ import java.io.Serializable;
 public class Customer implements Serializable {
 
   @JsonProperty("id")
-  private Long customerId;
+  public Long customerId;
   private String firstName;
   private String lastName;
   @JsonProperty("ssn")
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 5bfba0e..55cfe2c 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
@@ -15,19 +15,6 @@
 
 package org.apache.geode.cache.query.internal;
 
-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.types.ObjectType;
-import org.apache.geode.internal.cache.Token;
-import org.apache.geode.internal.i18n.LocalizedStrings;
-import org.apache.geode.pdx.JSONFormatter;
-import org.apache.geode.pdx.PdxInstance;
-import org.apache.geode.pdx.PdxSerializationException;
-import org.apache.geode.pdx.internal.FieldNotFoundInPdxVersion;
-import org.apache.geode.pdx.internal.PdxInstanceImpl;
-
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -41,6 +28,18 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+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.internal.cache.Token;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.pdx.JSONFormatter;
+import org.apache.geode.pdx.PdxInstance;
+import org.apache.geode.pdx.PdxSerializationException;
+import org.apache.geode.pdx.internal.FieldNotFoundInPdxVersion;
+import org.apache.geode.pdx.internal.PdxInstanceImpl;
+
 /**
  * Utility for managing an attribute
  *
@@ -50,12 +49,14 @@ import java.util.concurrent.ConcurrentMap;
 
 public class AttributeDescriptor {
   private final String _name;
+  private final MethodInvocationAuthorizer _methodInvocationAuthorizer;
   /** cache for remembering the correct Member for a class and attribute */
-  private static final ConcurrentMap _cache = new ConcurrentHashMap();
+  private static final ConcurrentMap<List, Member> _localCache = new ConcurrentHashMap();
 
 
 
-  public AttributeDescriptor(String name) {
+  public AttributeDescriptor(MethodInvocationAuthorizer methodInvocationAuthorizer, String name) {
+    _methodInvocationAuthorizer = methodInvocationAuthorizer;
     _name = name;
   }
 
@@ -97,6 +98,7 @@ public class AttributeDescriptor {
     try {
       if (m instanceof Method) {
         try {
+          _methodInvocationAuthorizer.authorizeMethodInvocation((Method) m, target);
           return ((Method) m).invoke(target, (Object[]) null);
         } catch (EntryDestroyedException e) {
           // eat the Exception
@@ -136,38 +138,31 @@ public class AttributeDescriptor {
     }
   }
 
-
-  Member getReadMember(ObjectType targetType) throws NameNotFoundException {
-    return getReadMember(targetType.resolveClass());
-  }
-
   Member getReadMember(Class targetClass) throws NameNotFoundException {
+
     // mapping: public field (same name), method (getAttribute()),
     // method (attribute())
     List key = new ArrayList();
     key.add(targetClass);
     key.add(_name);
 
-    Member m = (Member) _cache.get(key);
-    if (m != null)
-      return m;
+    Member m = _localCache.computeIfAbsent(key, k -> {
+      Member member = getReadField(targetClass);
+      return member == null ? getReadMethod(targetClass) : member;
+    });
 
-    m = getReadField(targetClass);
-    if (m == null)
-      m = getReadMethod(targetClass);
-    if (m != null)
-      _cache.putIfAbsent(key, m);
-    else
+    if (m == null) {
       throw new NameNotFoundException(
           LocalizedStrings.AttributeDescriptor_NO_PUBLIC_ATTRIBUTE_NAMED_0_WAS_FOUND_IN_CLASS_1
               .toLocalizedString(new Object[] {_name, targetClass.getName()}));
+    }
+
     // override security for nonpublic derived classes with public members
     ((AccessibleObject) m).setAccessible(true);
     return m;
   }
 
 
-
   private Field getReadField(Class targetType) {
     try {
       return targetType.getField(_name);
@@ -180,10 +175,13 @@ public class AttributeDescriptor {
 
   private Method getReadMethod(Class targetType) {
     Method m;
+    // Check for a getter method for this _name
     String beanMethod = "get" + _name.substring(0, 1).toUpperCase() + _name.substring(1);
     m = getReadMethod(targetType, beanMethod);
+
     if (m != null)
       return m;
+
     return getReadMethod(targetType, _name);
   }
 
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledOperation.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledOperation.java
index e544d8c..0e5bd14 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledOperation.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledOperation.java
@@ -258,7 +258,9 @@ public class CompiledOperation extends AbstractCompiledValue {
     methodDispatch = (MethodDispatch) CompiledOperation.cache.get(key);
     if (methodDispatch == null) {
       try {
-        methodDispatch = new MethodDispatch(resolutionType, this.methodName, argTypes);
+        methodDispatch =
+            new MethodDispatch(context.getCache().getQueryService().getMethodInvocationAuthorizer(),
+                resolutionType, this.methodName, argTypes);
       } catch (NameResolutionException nre) {
         if (!org.apache.geode.cache.query.Struct.class.isAssignableFrom(resolutionType)
             && (DefaultQueryService.QUERY_HETEROGENEOUS_OBJECTS
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledPath.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledPath.java
index ec7a84a..4df2287 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledPath.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledPath.java
@@ -136,7 +136,7 @@ public class CompiledPath extends AbstractCompiledValue {
     // getTailID());
     // }
 
-    Object obj = PathUtils.evaluateAttribute(evalRcvr, getTailID());
+    Object obj = PathUtils.evaluateAttribute(context, evalRcvr, getTailID());
     // check for BucketRegion substitution
     PartitionedRegion pr = context.getPartitionedRegion();
     if (pr != null && (obj instanceof Region)) {
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledSelect.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledSelect.java
index 808ea9d..3ab90aa 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledSelect.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/CompiledSelect.java
@@ -50,6 +50,7 @@ import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.pdx.PdxInstance;
 import org.apache.geode.pdx.internal.PdxString;
+import org.apache.geode.security.NotAuthorizedException;
 
 public class CompiledSelect extends AbstractCompiledValue {
 
@@ -1115,7 +1116,7 @@ public class CompiledSelect extends AbstractCompiledValue {
       List pathOnItr = cv.getPathOnIterator(rit, context);
       if (pathOnItr != null) {
         String path[] = (String[]) pathOnItr.toArray(new String[pathOnItr.size()]);
-        ObjectType ot[] = PathUtils.calculateTypesAlongPath(rit.getElementType(), path);
+        ObjectType ot[] = PathUtils.calculateTypesAlongPath(context, rit.getElementType(), path);
         retType = ot[ot.length - 1];
       }
     } catch (NameNotFoundException ignore) {
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQuery.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQuery.java
index 48658fe..c242d89 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQuery.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQuery.java
@@ -138,7 +138,6 @@ public class DefaultQuery implements Query {
   private boolean keepSerialized = false;
 
   public static final Set<String> reservedKeywords = new HashSet<>();
-
   static {
     reservedKeywords.add("hint");
     reservedKeywords.add("all");
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQueryService.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQueryService.java
index e3b2449..8fc1c9e 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQueryService.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/DefaultQueryService.java
@@ -39,13 +39,14 @@ import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.logging.log4j.Logger;
 
+import java.lang.reflect.Method;
 import java.util.*;
 import java.util.Map.Entry;
 
 /**
  * @version $Revision: 1.2 $
  */
-public class DefaultQueryService implements QueryService {
+public class DefaultQueryService implements InternalQueryService {
   private static final Logger logger = LogService.getLogger();
 
   /**
@@ -62,22 +63,37 @@ public class DefaultQueryService implements QueryService {
           DistributionConfig.GEMFIRE_PREFIX + "QueryService.CopyOnReadAtEntryLevel", "false"))
       .booleanValue();
 
+  public static boolean ALLOW_UNTRUSTED_METHOD_INVOCATION = Boolean.getBoolean(
+      DistributionConfig.GEMFIRE_PREFIX + "QueryService.allowUntrustedMethodInvocation");
+
 
   /** Test purpose only */
   public static boolean TEST_QUERY_HETEROGENEOUS_OBJECTS = false;
 
   private final InternalCache cache;
 
+  private final MethodInvocationAuthorizer methodInvocationAuthorizer;
+
   private InternalPool pool;
 
   private Map<Region, HashSet<IndexCreationData>> indexDefinitions =
       Collections.synchronizedMap(new HashMap<Region, HashSet<IndexCreationData>>());
 
+
   public DefaultQueryService(InternalCache cache) {
     if (cache == null)
       throw new IllegalArgumentException(
           LocalizedStrings.DefaultQueryService_CACHE_MUST_NOT_BE_NULL.toLocalizedString());
     this.cache = cache;
+    if (!cache.getSecurityService().isIntegratedSecurity() || ALLOW_UNTRUSTED_METHOD_INVOCATION) {
+      // A no-op authorizer, allow method invocation
+      this.methodInvocationAuthorizer = ((Method m, Object t) -> {
+      });
+    } else {
+      this.methodInvocationAuthorizer =
+          new RestrictedMethodInvocationAuthorizer(cache.getSecurityService());
+
+    }
   }
 
   /**
@@ -950,4 +966,7 @@ public class DefaultQueryService implements QueryService {
     return pool;
   }
 
+  public MethodInvocationAuthorizer getMethodInvocationAuthorizer() {
+    return methodInvocationAuthorizer;
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/ExecutionContext.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/ExecutionContext.java
index 6675e02..10cab17 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/ExecutionContext.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/ExecutionContext.java
@@ -332,7 +332,7 @@ public class ExecutionContext {
         // If Element type is ObjectType then we don't need to apply reflection to find out field or
         // method. This save lot of CPU time.
         if (!TypeUtils.OBJECT_TYPE.equals(itr.getElementType())
-            && itr.containsProperty(name, numArgs, mustBeMethod)) {
+            && itr.containsProperty(this, name, numArgs, mustBeMethod)) {
           hits.add(itr);
         } else if (TypeUtils.OBJECT_TYPE.equals(itr.getElementType())) {
           if (foundOneUnknown) {
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/InternalQueryService.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/InternalQueryService.java
new file mode 100644
index 0000000..2fd462e
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/InternalQueryService.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.internal;
+
+import org.apache.geode.cache.query.QueryService;
+
+public interface InternalQueryService extends QueryService {
+
+  MethodInvocationAuthorizer getMethodInvocationAuthorizer();
+}
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 53eba89..7b5e65c 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
@@ -16,10 +16,18 @@
 package org.apache.geode.cache.query.internal;
 
 
-import java.util.*;
-import java.lang.reflect.*;
-
-import org.apache.geode.cache.query.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.geode.cache.query.AmbiguousNameException;
+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.internal.i18n.LocalizedStrings;
 
@@ -35,13 +43,14 @@ public class MethodDispatch {
   private String _methodName;
   private Class[] _argTypes;
   private Method _method; // remember the right method
+  private MethodInvocationAuthorizer _methodInvocationAuthorizer;
 
-
-  public MethodDispatch(Class targetClass, String methodName, List argTypes)
-      throws NameResolutionException {
+  public MethodDispatch(MethodInvocationAuthorizer methodInvocationAuthorizer, Class targetClass,
+      String methodName, List argTypes) throws NameResolutionException {
     _targetClass = targetClass;
     _methodName = methodName;
     _argTypes = (Class[]) argTypes.toArray(new Class[argTypes.size()]);
+    _methodInvocationAuthorizer = methodInvocationAuthorizer;
 
     resolve();
     // override security in case this is a method on a nonpublic class
@@ -49,14 +58,12 @@ public class MethodDispatch {
     _method.setAccessible(true);
   }
 
-
-
   public Object invoke(Object target, List args)
       throws NameNotFoundException, QueryInvocationTargetException {
     Object[] argsArray = args.toArray();
 
-
     try {
+      _methodInvocationAuthorizer.authorizeMethodInvocation(_method, target);
       return _method.invoke(target, argsArray);
     } catch (IllegalAccessException e) {
       throw new NameNotFoundException(
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodInvocationAuthorizer.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodInvocationAuthorizer.java
new file mode 100644
index 0000000..412c2d5
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/MethodInvocationAuthorizer.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.internal;
+
+import java.lang.reflect.Method;
+
+public interface MethodInvocationAuthorizer {
+  void authorizeMethodInvocation(Method method, Object target);
+}
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/PathUtils.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/PathUtils.java
index bf931a2..f7daac6 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/PathUtils.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/PathUtils.java
@@ -14,19 +14,23 @@
  */
 package org.apache.geode.cache.query.internal;
 
-import java.lang.reflect.*;
-import java.util.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
 
 import org.apache.geode.cache.query.AmbiguousNameException;
+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.NameNotFoundException;
 import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.cache.query.Struct;
 import org.apache.geode.cache.query.TypeMismatchException;
 import org.apache.geode.cache.query.internal.parse.OQLLexerTokenTypes;
-import org.apache.geode.cache.query.types.*;
-import org.apache.geode.cache.query.internal.types.*;
+import org.apache.geode.cache.query.internal.types.TypeUtils;
+import org.apache.geode.cache.query.types.ObjectType;
 
 
 /**
@@ -57,7 +61,7 @@ public class PathUtils {
     return buf.toString();
   }
 
-  public static Object evaluateAttribute(Object target, String attribute)
+  public static Object evaluateAttribute(ExecutionContext context, Object target, String attribute)
       throws NameNotFoundException, QueryInvocationTargetException {
     if (target instanceof Struct) {
       Struct struct = (Struct) target;
@@ -68,7 +72,9 @@ public class PathUtils {
       }
     }
     try {
-      return new AttributeDescriptor(attribute).read(target);
+      return new AttributeDescriptor(
+          context.getCache().getQueryService().getMethodInvocationAuthorizer(), attribute)
+              .read(target);
     } catch (NameNotFoundException nfe) {
       if (DefaultQueryService.QUERY_HETEROGENEOUS_OBJECTS
           || DefaultQueryService.TEST_QUERY_HETEROGENEOUS_OBJECTS) {
@@ -98,15 +104,18 @@ public class PathUtils {
    * @throws NameNotFoundException if could not find an attribute along path
    *
    */
-  public static ObjectType[] calculateTypesAlongPath(ObjectType initialType, String[] pathArray)
-      throws NameNotFoundException {
+  public static ObjectType[] calculateTypesAlongPath(ExecutionContext context,
+      ObjectType initialType, String[] pathArray) throws NameNotFoundException {
     ObjectType[] types = new ObjectType[pathArray.length + 1];
     // initialClass goes in front
     types[0] = initialType;
 
     for (int i = 1; i < types.length; i++) {
       ObjectType currentType = types[i - 1];
-      Member member = new AttributeDescriptor(pathArray[i - 1]).getReadMember(currentType);
+      Member member = new AttributeDescriptor(
+          context.getCache().getQueryService().getMethodInvocationAuthorizer(), pathArray[i - 1])
+              .getReadMember(currentType.resolveClass());
+
       if (member instanceof Field)
         types[i] = TypeUtils.getObjectType(((Field) member).getType());
       else if (member instanceof Method)
@@ -161,7 +170,9 @@ public class PathUtils {
             stepStr = stepStr.substring(0, stepStr.length() - 2);
             member = clazz.getMethod(stepStr, (Class[]) null);
           } else {
-            member = new AttributeDescriptor(stepStr).getReadMember(clazz);
+            member = new AttributeDescriptor(
+                context.getCache().getQueryService().getMethodInvocationAuthorizer(), stepStr)
+                    .getReadMember(clazz);
           }
           if (member instanceof Field) {
             clazz = ((Field) member).getType();
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizer.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizer.java
new file mode 100644
index 0000000..e97fe34
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizer.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.internal;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.internal.index.DummyQRegion;
+import org.apache.geode.internal.cache.EntrySnapshot;
+import org.apache.geode.internal.cache.LocalRegion;
+import org.apache.geode.internal.cache.PartitionedRegion;
+import org.apache.geode.internal.security.SecurityService;
+import org.apache.geode.security.NotAuthorizedException;
+import org.apache.geode.security.ResourcePermission;
+
+public class RestrictedMethodInvocationAuthorizer implements MethodInvocationAuthorizer {
+
+  public static final String UNAUTHORIZED_STRING = "Unauthorized access to method: ";
+
+  private SecurityService securityService;
+
+  // List of methods that can be invoked by
+  private final HashMap<String, Set> whiteListedMethodsToClass;
+
+
+  public RestrictedMethodInvocationAuthorizer(SecurityService securityService) {
+    this.securityService = securityService;
+    whiteListedMethodsToClass = createWhiteList();
+  }
+
+  private HashMap<String, Set> createWhiteList() {
+    HashMap<String, Set> whiteListMap = new HashMap();
+    Set<Class> objectCallers = new HashSet();
+    objectCallers.add(Object.class);
+    whiteListMap.put("toString", objectCallers);
+    whiteListMap.put("equals", objectCallers);
+    whiteListMap.put("compareTo", objectCallers);
+
+    Set<Class> booleanCallers = new HashSet();
+    booleanCallers.add(Boolean.class);
+    whiteListMap.put("booleanValue", booleanCallers);
+
+    Set<Class> numericCallers = new HashSet();
+    numericCallers.add(Number.class);
+    whiteListMap.put("byteValue", numericCallers);
+    whiteListMap.put("intValue", numericCallers);
+    whiteListMap.put("doubleValue", numericCallers);
+    whiteListMap.put("floatValue", numericCallers);
+    whiteListMap.put("longValue", numericCallers);
+    whiteListMap.put("shortValue", numericCallers);
+
+    Set<Class> mapCallers = new HashSet();
+    mapCallers.add(Collection.class);
+    mapCallers.add(Map.class);
+    whiteListMap.put("entrySet", mapCallers);
+    whiteListMap.put("keySet", mapCallers);
+    whiteListMap.put("values", mapCallers);
+    whiteListMap.put("getEntries", mapCallers);
+    whiteListMap.put("getValues", mapCallers);
+    whiteListMap.put("containsKey", mapCallers);
+
+    Set<Class> mapEntryCallers = new HashSet();
+    mapEntryCallers.add(Map.Entry.class);
+    whiteListMap.put("getKey", mapEntryCallers);
+    whiteListMap.put("getValue", mapEntryCallers);
+
+    Set<Class> dateCallers = new HashSet<>();
+    dateCallers.add(Date.class);
+    whiteListMap.put("after", dateCallers);
+    whiteListMap.put("before", dateCallers);
+    whiteListMap.put("getNanos", dateCallers);
+    whiteListMap.put("getTime", dateCallers);
+
+    Set<Class> stringCallers = new HashSet<>();
+    stringCallers.add(String.class);
+    whiteListMap.put("toLowerCase", stringCallers);
+    whiteListMap.put("toUpperCase", stringCallers);
+    return whiteListMap;
+  }
+
+  boolean isWhitelisted(Method method) {
+    String methodName = method.getName();
+
+    Set<Class> allowedClasses = whiteListedMethodsToClass.get(methodName);
+    if (allowedClasses == null) {
+      return false;
+    }
+    for (Class clazz : allowedClasses) {
+      if (clazz.isAssignableFrom(method.getDeclaringClass())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public void authorizeMethodInvocation(Method method, Object target) {
+    if (!isWhitelisted(method)) {
+      throw new NotAuthorizedException(UNAUTHORIZED_STRING + method.getName());
+    }
+    authorizeRegionAccess(securityService, target);
+  }
+
+  private void authorizeRegionAccess(SecurityService securityService, Object target) {
+    if (target instanceof Region) {
+      String regionName = ((Region) target).getName();
+      securityService.authorize(ResourcePermission.Resource.DATA, ResourcePermission.Operation.READ,
+          regionName);
+    }
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/RuntimeIterator.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/RuntimeIterator.java
index 8a3fcf3..3373b96 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/RuntimeIterator.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/RuntimeIterator.java
@@ -14,13 +14,21 @@
  */
 package org.apache.geode.cache.query.internal;
 
-import org.apache.geode.internal.i18n.LocalizedStrings;
-import java.util.*;
 import java.lang.reflect.Method;
-import org.apache.geode.cache.query.*;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.geode.cache.query.AmbiguousNameException;
+import org.apache.geode.cache.query.FunctionDomainException;
+import org.apache.geode.cache.query.NameResolutionException;
+import org.apache.geode.cache.query.QueryInvocationTargetException;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.TypeMismatchException;
 import org.apache.geode.cache.query.internal.index.IndexCreationHelper;
 import org.apache.geode.cache.query.internal.types.TypeUtils;
-import org.apache.geode.cache.query.types.*;
+import org.apache.geode.cache.query.types.ObjectType;
+import org.apache.geode.cache.query.types.StructType;
+import org.apache.geode.internal.i18n.LocalizedStrings;
 
 /**
  * Value representing a current iteration element. This is the representation used during
@@ -151,7 +159,7 @@ public class RuntimeIterator extends AbstractCompiledValue {
     return this.current;
   }
 
-  boolean containsProperty(String name, int numArgs, boolean mustBeMethod)
+  boolean containsProperty(ExecutionContext context, String name, int numArgs, boolean mustBeMethod)
       throws AmbiguousNameException {
     // first handle structs
     if ((this.elementType instanceof StructType) && !mustBeMethod) {
@@ -190,7 +198,9 @@ public class RuntimeIterator extends AbstractCompiledValue {
     // if there are zero arguments and it's an attribute, then defer to
     // AttributeDescriptor
     // to see if there's a match
-    return new AttributeDescriptor(name).validateReadType(clazz);
+    return new AttributeDescriptor(
+        context.getCache().getQueryService().getMethodInvocationAuthorizer(), name)
+            .validateReadType(clazz);
   }
 
   // private SelectResults prepareIteratorDef(Object obj)
diff --git a/geode-core/src/main/java/org/apache/geode/cache/query/internal/StructImpl.java b/geode-core/src/main/java/org/apache/geode/cache/query/internal/StructImpl.java
index 08b6819..f7a46e1 100644
--- a/geode-core/src/main/java/org/apache/geode/cache/query/internal/StructImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/cache/query/internal/StructImpl.java
@@ -15,15 +15,20 @@
 
 package org.apache.geode.cache.query.internal;
 
-import java.util.*;
-import java.io.*;
-import org.apache.geode.cache.query.*;
-import org.apache.geode.cache.query.types.*;
-import org.apache.geode.cache.query.internal.types.StructTypeImpl;
-import org.apache.geode.internal.i18n.LocalizedStrings;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+
 import org.apache.geode.DataSerializer;
+import org.apache.geode.cache.query.Struct;
+import org.apache.geode.cache.query.internal.types.StructTypeImpl;
+import org.apache.geode.cache.query.types.ObjectType;
+import org.apache.geode.cache.query.types.StructType;
 import org.apache.geode.internal.DataSerializableFixedID;
 import org.apache.geode.internal.Version;
+import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.pdx.PdxInstance;
 import org.apache.geode.pdx.internal.PdxString;
 
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
index c5b5d01..e2975ea 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
@@ -79,6 +79,9 @@ import com.sun.jna.Platform;
 import org.apache.commons.lang.StringUtils;
 import org.apache.logging.log4j.Logger;
 
+import org.apache.geode.cache.query.internal.InternalQueryService;
+import org.apache.geode.cache.query.internal.MethodInvocationAuthorizer;
+import org.apache.geode.cache.query.internal.RestrictedMethodInvocationAuthorizer;
 import org.apache.geode.internal.cache.event.EventTrackerExpiryTask;
 import org.apache.geode.internal.cache.wan.GatewaySenderQueueEntrySynchronizationListener;
 import org.apache.geode.internal.security.SecurityServiceFactory;
@@ -177,7 +180,6 @@ import org.apache.geode.internal.SystemTimer;
 import org.apache.geode.internal.cache.control.InternalResourceManager;
 import org.apache.geode.internal.cache.control.InternalResourceManager.ResourceType;
 import org.apache.geode.internal.cache.control.ResourceAdvisor;
-import org.apache.geode.internal.cache.event.EventTrackerExpiryTask;
 import org.apache.geode.internal.cache.execute.util.FindRestEnabledServersFunction;
 import org.apache.geode.internal.cache.extension.Extensible;
 import org.apache.geode.internal.cache.extension.ExtensionPoint;
@@ -215,7 +217,6 @@ import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.net.SocketCreator;
 import org.apache.geode.internal.offheap.MemoryAllocator;
 import org.apache.geode.internal.security.SecurityService;
-import org.apache.geode.internal.security.SecurityServiceFactory;
 import org.apache.geode.internal.sequencelog.SequenceLoggerImpl;
 import org.apache.geode.internal.tcp.ConnectionTable;
 import org.apache.geode.internal.util.concurrent.FutureResult;
@@ -4192,7 +4193,7 @@ public class GemFireCacheImpl implements InternalCache, InternalClientCache, Has
   }
 
   @Override
-  public QueryService getQueryService() {
+  public InternalQueryService getQueryService() {
     if (!isClient()) {
       return new DefaultQueryService(this);
     }
@@ -4201,7 +4202,7 @@ public class GemFireCacheImpl implements InternalCache, InternalClientCache, Has
       throw new IllegalStateException(
           "Client cache does not have a default pool. Use getQueryService(String poolName) instead.");
     }
-    return defaultPool.getQueryService();
+    return (InternalQueryService) defaultPool.getQueryService();
   }
 
   @Override
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalCache.java b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalCache.java
index 66bc44a..5ef8fcd 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalCache.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalCache.java
@@ -39,6 +39,7 @@ import org.apache.geode.cache.asyncqueue.AsyncEventQueue;
 import org.apache.geode.cache.asyncqueue.internal.AsyncEventQueueImpl;
 import org.apache.geode.cache.client.internal.ClientMetadataService;
 import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.internal.InternalQueryService;
 import org.apache.geode.cache.query.internal.QueryMonitor;
 import org.apache.geode.cache.query.internal.cq.CqService;
 import org.apache.geode.cache.server.CacheServer;
@@ -320,4 +321,6 @@ public interface InternalCache extends Cache, Extensible<Cache>, CacheTime {
   void invokeRegionEntrySynchronizationListenersAfterSynchronization(
       InternalDistributedMember sender, LocalRegion region,
       List<InitialImageOperation.Entry> entriesToSynchronize);
+
+  InternalQueryService getQueryService();
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheCreation.java b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheCreation.java
index 9c80ce1..3cd1971 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheCreation.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/xmlcache/CacheCreation.java
@@ -58,6 +58,8 @@ import org.apache.geode.cache.query.Query;
 import org.apache.geode.cache.query.QueryInvalidException;
 import org.apache.geode.cache.query.QueryService;
 import org.apache.geode.cache.query.RegionNotFoundException;
+import org.apache.geode.cache.query.internal.InternalQueryService;
+import org.apache.geode.cache.query.internal.MethodInvocationAuthorizer;
 import org.apache.geode.cache.query.internal.QueryMonitor;
 import org.apache.geode.cache.query.internal.cq.CqService;
 import org.apache.geode.cache.server.CacheServer;
@@ -943,7 +945,7 @@ public class CacheCreation implements InternalCache {
   }
 
   @Override
-  public QueryService getQueryService() {
+  public InternalQueryService getQueryService() {
     return this.queryService;
   }
 
@@ -1735,7 +1737,7 @@ public class CacheCreation implements InternalCache {
     return null;
   }
 
-  private final QueryService queryService = new QueryService() {
+  private final InternalQueryService queryService = new InternalQueryService() {
 
     private final Map<String, List<Index>> indexes = new HashMap<>();
 
@@ -1958,6 +1960,10 @@ public class CacheCreation implements InternalCache {
       throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
     }
 
+    @Override
+    public MethodInvocationAuthorizer getMethodInvocationAuthorizer() {
+      throw new UnsupportedOperationException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
+    }
   };
 
   @Override
diff --git a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
index 83b2883..885aa3a 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
@@ -2086,6 +2086,8 @@ public class LocalizedStrings {
           "Method '' {0} '' in class '' {1} '' is not accessible to the query processor");
   public static final StringId AttributeDescriptor_NO_PUBLIC_ATTRIBUTE_NAMED_0_WAS_FOUND_IN_CLASS_1 =
       new StringId(2253, "No public attribute named '' {0} '' was found in class  {1}");
+  public static final StringId AttributeDescriptor_NO_ACCESS_BECAUSE_METHOD_WAS_BLACKLISTED =
+      new StringId(2254, "No access to attribute named '' {0} '' because it has been blacklisted");
 
   public static final StringId AvailablePort_UNKNOWN_PROTOCOL_0 =
       new StringId(2258, "Unknown protocol:  {0}");
diff --git a/geode-core/src/test/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizerTest.java b/geode-core/src/test/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizerTest.java
new file mode 100644
index 0000000..45ddf27
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/query/internal/RestrictedMethodInvocationAuthorizerTest.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.internal;
+
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.query.internal.index.DummyQRegion;
+import org.apache.geode.internal.cache.EntrySnapshot;
+import org.apache.geode.internal.cache.LocalRegion;
+import org.apache.geode.internal.cache.PartitionedRegion;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category({UnitTest.class, SecurityTest.class})
+public class RestrictedMethodInvocationAuthorizerTest {
+  RestrictedMethodInvocationAuthorizer methodInvocationAuthorizer =
+      new RestrictedMethodInvocationAuthorizer(null);
+
+  @Test
+  public void getClassShouldFail() throws Exception {
+    Method method = Integer.class.getMethod("getClass");
+    assertFalse(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void toStringOnAnyObject() throws Exception {
+    Method stringMethod = Integer.class.getMethod("toString");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(stringMethod));
+  }
+
+  @Test
+  public void equalsOnAnyObject() throws Exception {
+    Method equalsMethod = Integer.class.getMethod("equals", Object.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(equalsMethod));
+  }
+
+  @Test
+  public void booleanMethodsAreWhiteListed() throws Exception {
+    Method booleanValue = Boolean.class.getMethod("booleanValue");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(booleanValue));
+  }
+
+  @Test
+  public void toLowerCaseOnStringObject() throws Exception {
+    Method stringMethod = String.class.getMethod("toLowerCase");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(stringMethod));
+  }
+
+  @Test
+  public void toUpperCaseOnStringObject() throws Exception {
+    Method stringMethod = String.class.getMethod("toUpperCase");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(stringMethod));
+  }
+
+  @Test
+  public void utilDateAfterMethodIsWhiteListed() throws Exception {
+    Method method = Date.class.getMethod("after", Date.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void sqlDateAfterMethodIsWhiteListed() throws Exception {
+    Method method = java.sql.Date.class.getMethod("after", Date.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void utilDateBeforeMethodIsWhiteListed() throws Exception {
+    Method method = Date.class.getMethod("before", Date.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void sqlDateBeforeMethodIsWhiteListed() throws Exception {
+    Method method = java.sql.Date.class.getMethod("before", Date.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void timestampAfterMethodIsWhiteListed() throws Exception {
+    Method method = Timestamp.class.getMethod("after", Timestamp.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void sqlTimestampBeforeMethodIsWhiteListed() throws Exception {
+    Method method = Timestamp.class.getMethod("before", Timestamp.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void sqlTimestampGetNanosIsWhiteListed() throws Exception {
+    Method method = Timestamp.class.getMethod("getNanos");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+  @Test
+  public void sqlTimestampGetTimeIsWhiteListed() throws Exception {
+    Method method = Timestamp.class.getMethod("getTime");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(method));
+  }
+
+
+  @Test
+  public void getKeyForMapEntryIsWhiteListed() throws Exception {
+    Method getKeyMethod = Map.Entry.class.getMethod("getKey");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getKeyMethod));
+  }
+
+  @Test
+  public void getValueForMapEntryIsWhiteListed() throws Exception {
+    Method getValueMethod = Map.Entry.class.getMethod("getValue");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getValueMethod));
+  }
+
+  @Test
+  public void getKeyForMapEntrySnapShotIsWhiteListed() throws Exception {
+    Method getKeyMethod = EntrySnapshot.class.getMethod("getKey");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getKeyMethod));
+  }
+
+  @Test
+  public void getValueForMapEntrySnapShotIsWhiteListed() throws Exception {
+    Method getValueMethod = EntrySnapshot.class.getMethod("getValue");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getValueMethod));
+  }
+
+  @Test
+  public void getKeyForNonTXEntryIsWhiteListed() throws Exception {
+    Method getKeyMethod = LocalRegion.NonTXEntry.class.getMethod("getKey");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getKeyMethod));
+  }
+
+  @Test
+  public void getValueForNonTXEntryIsWhiteListed() throws Exception {
+    Method getValueMethod = LocalRegion.NonTXEntry.class.getMethod("getValue");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getValueMethod));
+  }
+
+  @Test
+  public void mapMethodsForQRegionAreWhiteListed() throws Exception {
+    testMapMethods(QRegion.class);
+  }
+
+  @Test
+  public void mapMethodsForDummyQRegionAreWhiteListed() throws Exception {
+    testMapMethods(DummyQRegion.class);
+  }
+
+  @Test
+  public void mapMethodsForPartitionedRegionAreWhiteListed() throws Exception {
+    Class clazz = PartitionedRegion.class;
+    Method entrySet = clazz.getMethod("entrySet");
+    Method keySet = clazz.getMethod("keySet");
+    Method values = clazz.getMethod("values");
+    Method containsKey = clazz.getMethod("containsKey", Object.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(entrySet));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(keySet));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(values));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(containsKey));
+  }
+
+  @Test
+  public void numberMethodsForByteAreWhiteListed() throws Exception {
+    testNumberMethods(Byte.class);
+  }
+
+  @Test
+  public void numberMethodsForDoubleAreWhiteListed() throws Exception {
+    testNumberMethods(Double.class);
+  }
+
+  @Test
+  public void numberMethodsForFloatAreWhiteListed() throws Exception {
+    testNumberMethods(Float.class);
+  }
+
+  @Test
+  public void numberMethodsForIntegerAreWhiteListed() throws Exception {
+    testNumberMethods(Integer.class);
+  }
+
+  @Test
+  public void numberMethodsForShortAreWhiteListed() throws Exception {
+    testNumberMethods(Short.class);
+  }
+
+  @Test
+  public void numberMethodsForBigDecimalAreWhiteListed() throws Exception {
+    testNumberMethods(BigDecimal.class);
+  }
+
+  @Test
+  public void numberMethodsForNumberAreWhiteListed() throws Exception {
+    testNumberMethods(BigInteger.class);
+  }
+
+  @Test
+  public void numberMethodsForAtomicIntegerAreWhiteListed() throws Exception {
+    testNumberMethods(AtomicInteger.class);
+  }
+
+  @Test
+  public void numberMethodsForAtomicLongAreWhiteListed() throws Exception {
+    testNumberMethods(AtomicLong.class);
+  }
+
+  private void testNumberMethods(Class clazz) throws NoSuchMethodException {
+    Method byteValue = clazz.getMethod("byteValue");
+    Method doubleValue = clazz.getMethod("doubleValue");
+    Method intValue = clazz.getMethod("intValue");
+    Method floatValue = clazz.getMethod("longValue");
+    Method longValue = clazz.getMethod("floatValue");
+    Method shortValue = clazz.getMethod("shortValue");
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(byteValue));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(doubleValue));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(intValue));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(floatValue));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(longValue));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(shortValue));
+  }
+
+  private void testMapMethods(Class clazz) throws NoSuchMethodException {
+    Method entrySet = clazz.getMethod("entrySet");
+    Method keySet = clazz.getMethod("keySet");
+    Method values = clazz.getMethod("values");
+    Method getEntries = clazz.getMethod("getEntries");
+    Method getValues = clazz.getMethod("getValues");
+    Method containsKey = clazz.getMethod("containsKey", Object.class);
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(entrySet));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(keySet));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(values));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getEntries));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(getValues));
+    assertTrue(methodInvocationAuthorizer.isWhitelisted(containsKey));
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/beans/CacheServerBridgeClientMembershipRegressionTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/beans/CacheServerBridgeClientMembershipRegressionTest.java
index 232df0a..1cc73c3 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/beans/CacheServerBridgeClientMembershipRegressionTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/beans/CacheServerBridgeClientMembershipRegressionTest.java
@@ -34,6 +34,7 @@ import org.junit.experimental.categories.Category;
 import org.apache.geode.cache.CacheClosedException;
 import org.apache.geode.cache.CacheFactory;
 import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.internal.InternalQueryService;
 import org.apache.geode.internal.cache.CacheServerImpl;
 import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.internal.cache.tier.sockets.AcceptorImpl;
@@ -75,7 +76,7 @@ public class CacheServerBridgeClientMembershipRegressionTest {
     this.acceptor = mock(AcceptorImpl.class);
     this.monitor = mock(MBeanStatsMonitor.class);
 
-    when(cache.getQueryService()).thenReturn(mock(QueryService.class));
+    when(cache.getQueryService()).thenReturn(mock(InternalQueryService.class));
     when(acceptor.getStats()).thenReturn(mock(CacheServerStats.class));
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/security/TestSecurityManager.java b/geode-core/src/test/java/org/apache/geode/security/TestSecurityManager.java
index e42cf6b..e7fbf8f 100644
--- a/geode-core/src/test/java/org/apache/geode/security/TestSecurityManager.java
+++ b/geode-core/src/test/java/org/apache/geode/security/TestSecurityManager.java
@@ -158,23 +158,24 @@ public class TestSecurityManager implements SecurityManager {
       readUsers(this.userNameToUser, jsonNode, roleMap);
       return true;
     } catch (IOException ex) {
+      ex.printStackTrace();
       return false;
     }
   }
 
-  boolean initializeFromJsonResource(final String jsonResource) {
+  public boolean initializeFromJsonResource(final String jsonResource) {
     try {
       InputStream input = ClassLoader.getSystemResourceAsStream(jsonResource);
       if (input != null) {
-        initializeFromJson(readJsonFromInputStream(input));
-        return true;
+        return initializeFromJson(readJsonFromInputStream(input));
       }
     } catch (IOException ex) {
+      ex.printStackTrace();
     }
     return false;
   }
 
-  User getUser(final String user) {
+  public User getUser(final String user) {
     return this.userNameToUser.get(user);
   }
 
@@ -258,12 +259,28 @@ public class TestSecurityManager implements SecurityManager {
     List<ResourcePermission> permissions = new ArrayList<>();
     String name;
     String serverGroup;
+
+    public String getName() {
+      return name;
+    }
+
+    public List<ResourcePermission> getPermissions() {
+      return permissions;
+    }
   }
 
   public static class User {
     String name;
     Set<Role> roles = new HashSet<>();
     String password;
+
+    public Set<Role> getRoles() {
+      return roles;
+    }
+
+    public String getPassword() {
+      return password;
+    }
   }
 
 }
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/IndexSecurityDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/IndexSecurityDUnitTest.java
new file mode 100644
index 0000000..ea0f920
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/IndexSecurityDUnitTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import static org.apache.geode.internal.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.query.Index;
+import org.apache.geode.cache.query.IndexInvalidException;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+public class IndexSecurityDUnitTest extends QuerySecurityBase {
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.REPLICATE;
+  }
+
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataWriter"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+
+  @Before
+  public void configureSpecificUserAndKeyAndValues() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1", "key-2"};
+    values = new Object[] {new QueryTestObject(1, "Mary"), new QueryTestObject(2, "Joe"),
+        new QueryTestObject(3, "Joe")};
+  }
+
+  @Test
+  public void indexCreatedButPutWithNoReadCredentialsShouldNotThrowSecurityException()
+      throws Exception {
+    QueryService queryService = server.getCache().getQueryService();
+    Index idIndex = queryService.createIndex("IdIndex", "id", "/" + regionName);
+    putIntoRegion(specificUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void indexCreatedWithRegionEntriesButPutWithNoReadCredentialsShouldNotThrowSecurityException()
+      throws Exception {
+    QueryService queryService = server.getCache().getQueryService();
+    Index idIndex = queryService.createIndex("IdIndex", "e.id", "/" + regionName + ".entries e");
+    putIntoRegion(specificUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void indexCreatedWithMethodInvocationOnPrepopulatedRegionShouldThrowSecurityException()
+      throws Exception {
+    QueryService queryService = server.getCache().getQueryService();
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    try {
+      queryService.createIndex("IdIndex", "e.getName()", "/" + regionName + " e");
+      fail("Index creation should have failed due to method invocation");
+    } catch (IndexInvalidException e) {
+      assertTrue(e.getMessage().contains("Unauthorized access to method: getName"));
+    }
+  }
+
+  @Test
+  public void indexCreatedWithMethodInvocationOnUnpopulatedRegionAndPutShouldMarkIndexInvalid()
+      throws Exception {
+    QueryService queryService = server.getCache().getQueryService();
+    Index index = queryService.createIndex("IdIndex", "e.getName()", "/" + regionName + " e");
+    putIntoRegion(superUserClient, keys, values, regionName);
+    assertFalse(index.isValid());
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PartitionedIndexSecurityDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedIndexSecurityDUnitTest.java
new file mode 100644
index 0000000..593bd4e
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedIndexSecurityDUnitTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import junitparams.JUnitParamsRunner;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+public class PartitionedIndexSecurityDUnitTest extends IndexSecurityDUnitTest {
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityAllowedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityAllowedQueriesDUnitTest.java
new file mode 100644
index 0000000..2d5b057
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityAllowedQueriesDUnitTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import junitparams.JUnitParamsRunner;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class PartitionedQuerySecurityAllowedQueriesDUnitTest
+    extends QuerySecurityAllowedQueriesDUnitTest {
+
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityRestrictedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityRestrictedQueriesDUnitTest.java
new file mode 100644
index 0000000..28a01ff
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PartitionedQuerySecurityRestrictedQueriesDUnitTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import junitparams.JUnitParamsRunner;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class PartitionedQuerySecurityRestrictedQueriesDUnitTest
+    extends QuerySecurityRestrictedQueriesDUnitTest {
+
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PdxPartitionedQuerySecurityAllowedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PdxPartitionedQuerySecurityAllowedQueriesDUnitTest.java
new file mode 100644
index 0000000..b9d0fda
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PdxPartitionedQuerySecurityAllowedQueriesDUnitTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+public class PdxPartitionedQuerySecurityAllowedQueriesDUnitTest
+    extends PdxQuerySecurityAllowedQueriesDUnitTest {
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityAllowedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityAllowedQueriesDUnitTest.java
new file mode 100644
index 0000000..e25f6cf
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityAllowedQueriesDUnitTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.pdx.PdxReader;
+import org.apache.geode.pdx.PdxSerializable;
+import org.apache.geode.pdx.PdxWriter;
+import org.apache.geode.security.query.data.PdxQueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+public class PdxQuerySecurityAllowedQueriesDUnitTest extends QuerySecurityBase {
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataReader", "dataReaderRegion", "clusterManagerDataReader",
+        "clusterManagerDataReaderRegion", "super-user"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  @Before
+  public void configureSpecificClientAndKeyAndValues() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new PdxQueryTestObject(0, "John"), new PdxQueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectOnPublicFieldQuery() {
+    String query = "select * from /" + regionName + " r where r.id = 3";
+    List<Object> expectedResults = Arrays.asList(values[1]);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectWithPdxFieldNamedGetQuery() {
+    server.getCache().setReadSerialized(true);
+    String query = "select * from /" + regionName + " r where r.getName = 'Beth'";
+    List<Object> expectedResults = Arrays.asList(values[1]);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityRestrictedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityRestrictedQueriesDUnitTest.java
new file mode 100644
index 0000000..f7e0258
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/PdxQuerySecurityRestrictedQueriesDUnitTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.pdx.PdxReader;
+import org.apache.geode.pdx.PdxSerializable;
+import org.apache.geode.pdx.PdxWriter;
+import org.apache.geode.security.query.data.PdxQueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+public class PdxQuerySecurityRestrictedQueriesDUnitTest extends QuerySecurityBase {
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataReader", "dataReaderRegion", "clusterManagerDataReader",
+        "clusterManagerDataReaderRegion", "super-user"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  private String regexForExpectedExceptions = ".*Unauthorized access.*";
+
+  @Before
+  public void configureCache() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new PdxQueryTestObject(0, "John"), new PdxQueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void selectWhenInvokingMethodOnPdxObjectQueryShouldBeRestricted() {
+    String query = "select r.getClass from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void invokingMethodOnPdxObjectShouldBeRestricted() {
+    String query = "select r.getAge from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void selectWithPdxFieldNoExistingPublicFieldQueryShouldBeRestricted() {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAllowedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAllowedQueriesDUnitTest.java
new file mode 100644
index 0000000..318086e
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAllowedQueriesDUnitTest.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class QuerySecurityAllowedQueriesDUnitTest extends QuerySecurityBase {
+
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataReader", "dataReaderRegion", "clusterManagerDataReader",
+        "clusterManagerDataReaderRegion", "super-user"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  @Before
+  public void configureCache() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new QueryTestObject(1, "John"), new QueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+
+  @Test
+  public void checkUserAuthorizationsForSelectAllQuery() {
+    String query = "select * from /" + regionName;
+    List<Object> expectedResults = Arrays.asList(values);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  /* ----- Public Field Tests ----- */
+  @Test
+  public void checkUserAuthorizationsForSelectByPublicFieldQuery() {
+    String query = "select * from /" + regionName + " r where r.id = 1";
+    List<Object> expectedResults = Arrays.asList(values[0]);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectPublicFieldQuery() {
+    String query = "select r.id from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(1, 3);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectCountOfPublicFieldQuery() {
+    String query = "select count(r.id) from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(2);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMaxOfPublicFieldQuery() {
+    String query = "select max(r.id) from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(3);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMinOfPublicFieldQuery() {
+    String query = "select min(r.id) from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(1);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectPublicFieldFromRegionByPublicFieldFromRegionQuery() {
+    String query = "select * from /" + regionName + " r1 where r1.id in (select r2.id from /"
+        + regionName + " r2)";
+    List<Object> expectedResults = Arrays.asList(values);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectRegionContainsKeyQuery() {
+    String query = "select * from /" + regionName + ".containsKey('" + keys[0] + "')";
+    List<Object> expectedResults = Arrays.asList(true);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectValuesQuery() {
+    String query = "select * from /" + regionName + ".values";
+    List<Object> expectedResults = Arrays.asList(values);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectKeySetQuery() {
+    String query = "select * from /" + regionName + ".keySet";
+    List<Object> expectedResults = Arrays.asList(keys);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectEntriesQuery() {
+    String query = "select e.getKey from /" + regionName + ".entries e";
+    List<Object> expectedResults = Arrays.asList(keys);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectKeyFromEntrySetQuery() {
+    String query = "select e.key from /" + regionName + ".entrySet e";
+    List<Object> expectedResults = Arrays.asList(keys);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetKeyFromEntrySetQuery() {
+    String query = "select e.getKey from /" + regionName + ".entrySet e";
+    List<Object> expectedResults = Arrays.asList(keys);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectValueFromEntrySetQuery() {
+    String query = "select e.value from /" + regionName + ".entrySet e";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetValueFromEntrySetQuery() {
+    String query = "select e.getValue from /" + regionName + ".entrySet e";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByToStringQuery() {
+    String query = "select * from /" + regionName + " r where r.toString = 'Test_Object'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectToStringQuery() {
+    String query = "select r.toString() from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList("Test_Object", "Test_Object");
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByToStringToUpperCaseQuery() {
+    String query =
+        "select * from /" + regionName + " r where r.toString().toUpperCase = 'TEST_OBJECT'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByToStringToLowerCaseQuery() {
+    String query =
+        "select * from /" + regionName + " r where r.toString().toLowerCase = 'test_object'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectIntValueQuery() {
+    String query = "select r.id.intValue() from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(1, 3);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectLongValueQuery() {
+    String query = "select r.id.longValue() from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(1L, 3L);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectDoubleValueQuery() {
+    String query = "select r.id.doubleValue() from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList(1d, 3d);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectShortValueQuery() {
+    String query = "select r.id.shortValue() from /" + regionName + " r";
+    List<Object> expectedResults = Arrays.asList((short) 1, (short) 3);
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        expectedResults);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectToDateQuery() {
+    String query =
+        "SELECT * FROM /" + regionName + " where dateField = to_date('08/08/2018', 'MM/dd/yyyy')";
+
+    QueryTestObject obj1 = new QueryTestObject(0, "John");
+    obj1.setDateField("08/08/2018");
+    QueryTestObject obj2 = new QueryTestObject(3, "Beth");
+    obj2.setDateField("08/08/2018");
+    Object[] values = {obj1, obj2};
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        Arrays.asList(values));
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAuthorizedUserBindParameterDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAuthorizedUserBindParameterDUnitTest.java
new file mode 100644
index 0000000..493fb73
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityAuthorizedUserBindParameterDUnitTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+
+@Category({DistributedTest.class, SecurityTest.class})
+public class QuerySecurityAuthorizedUserBindParameterDUnitTest extends QuerySecurityBase {
+
+  @Before
+  public void configureCache() {
+    String user = "dataReaderRegion";
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new QueryTestObject(1, "John"), new QueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void userWithRegionAccessAndPassingInWrappedBindParameterShouldReturnCorrectResults() {
+    String query = "select v from $1 r, r.values() v";
+    specificUserClient.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      HashSet hashset = new HashSet();
+      hashset.add(region);
+      assertQueryResults(getClientCache(), query, new Object[] {hashset}, Arrays.asList(values));
+    });
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityBase.java b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityBase.java
new file mode 100644
index 0000000..1e1815e
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityBase.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.client.Pool;
+import org.apache.geode.cache.client.PoolManager;
+import org.apache.geode.cache.query.FunctionDomainException;
+import org.apache.geode.cache.query.NameResolutionException;
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.QueryInvocationTargetException;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.TypeMismatchException;
+import org.apache.geode.security.SecurityTestUtil;
+import org.apache.geode.security.TestSecurityManager;
+import org.apache.geode.test.dunit.Host;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
+import org.apache.geode.test.junit.rules.ServerStarterRule;
+
+public class QuerySecurityBase extends JUnit4DistributedTestCase {
+
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.REPLICATE;
+  }
+
+  protected String regionName = "region";
+  protected Object[] keys;
+  protected Object[] values;
+
+  @Rule
+  public ServerStarterRule server =
+      new ServerStarterRule().withProperty(SECURITY_MANAGER, TestSecurityManager.class.getName())
+          .withProperty(TestSecurityManager.SECURITY_JSON,
+              "org/apache/geode/management/internal/security/clientServer.json")
+          .withRegion(getRegionType(), regionName);
+
+  // Varibles used to store caches between invoke methods
+  private static ClientCache clientCache;
+
+  protected transient UserPermissions userPerms = new UserPermissions();
+
+  protected Host host;
+  protected VM superUserClient;
+  protected VM specificUserClient;
+
+  @Before
+  public void configureTest() {
+    host = Host.getHost(0);
+    superUserClient = host.getVM(1);
+    specificUserClient = host.getVM(2);
+    createClientCache(superUserClient, "super-user", userPerms.getUserPassword("super-user"));
+    createProxyRegion(superUserClient, regionName);
+  }
+
+  public void setClientCache(ClientCache cache) {
+    clientCache = cache;
+  }
+
+  public ClientCache getClientCache() {
+    return clientCache;
+  }
+
+  public void createClientCache(VM vm, String userName, String password) {
+    vm.invoke(() -> {
+      ClientCache cache = SecurityTestUtil.createClientCache(userName, password, server.getPort());
+      setClientCache(cache);
+    });
+  }
+
+  public void createProxyRegion(VM vm, String regionName) {
+    vm.invoke(() -> {
+      SecurityTestUtil.createProxyRegion(getClientCache(), regionName);
+    });
+  }
+
+  @After
+  public void closeClientCaches() {
+    closeClientCache(superUserClient);
+    closeClientCache(specificUserClient);
+  }
+
+  public void closeClientCache(VM vm) {
+    vm.invoke(() -> {
+      if (getClientCache() != null) {
+        getClientCache().close();
+      }
+    });
+  }
+
+  protected void assertExceptionOccurred(QueryService qs, String query, String authErrorRegexp) {
+    try {
+      qs.newQuery(query).execute();
+    } catch (Exception e) {
+      if (!e.getMessage().matches(authErrorRegexp)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage().matches(authErrorRegexp)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail();
+      }
+    }
+  }
+
+  protected void assertExceptionOccurred(QueryService qs, String query, Object[] bindParams,
+      String authErrorRegexp) {
+    System.out.println("Execution exception should match:" + authErrorRegexp);
+    try {
+      qs.newQuery(query).execute(bindParams);
+      fail();
+    } catch (Exception e) {
+      if (!e.getMessage().matches(authErrorRegexp)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage().matches(authErrorRegexp)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail();
+      }
+    }
+  }
+
+  protected void assertQueryResults(ClientCache clientCache, String query,
+      List<Object> expectedResults) throws FunctionDomainException, TypeMismatchException,
+      NameResolutionException, QueryInvocationTargetException {
+    assertQueryResults(clientCache, query, null, expectedResults);
+  }
+
+  protected void assertQueryResults(ClientCache clientCache, String queryString,
+      Object[] bindParameters, List<Object> expectedResults) throws FunctionDomainException,
+      TypeMismatchException, NameResolutionException, QueryInvocationTargetException {
+    Query query = clientCache.getQueryService().newQuery(queryString);
+    Collection results;
+    if (bindParameters == null) {
+      results = (Collection) query.execute();
+    } else {
+      results = (Collection) query.execute(bindParameters);
+    }
+    assertNotNull(results);
+    assertEquals("Query results size did not match expected for " + query, expectedResults.size(),
+        results.size());
+
+    results.forEach((i) -> {
+      assertTrue("Result:" + i + " was not found in the expectedResults",
+          expectedResults.contains(i));
+    });
+  }
+
+  public void executeAndConfirmRegionMatches(VM vm, String regionName,
+      List<Object> expectedRegionResults) throws Exception {
+    vm.invoke(() -> {
+      assertQueryResults(getClientCache(), "select * from /" + regionName, expectedRegionResults);
+    });
+  }
+
+  protected void putIntoRegion(VM vm, Object[] keys, Object[] values, String regionName) {
+    vm.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      assertEquals(
+          "Bad region put. The list of keys does not have the same length as the list of values.",
+          keys.length, values.length);
+      for (int i = 0; i < keys.length; i++) {
+        region.put(keys[i], values[i]);
+      }
+    });
+  }
+
+  protected void executeQueryWithCheckForAccessPermissions(VM vm, String query, String regionName,
+      List<Object> expectedSuccessfulQueryResults) {
+    vm.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      assertQueryResults(getClientCache(), query, expectedSuccessfulQueryResults);
+    });
+  }
+
+
+  protected void executeQueryWithCheckForAccessPermissions(VM vm, String query, String regionName,
+      String regexForExpectedExceptions) {
+    vm.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      assertExceptionOccurred(getClientCache().getQueryService(), query,
+          regexForExpectedExceptions);
+      Pool pool = PoolManager.find(region);
+      assertExceptionOccurred(pool.getQueryService(), query, regexForExpectedExceptions);
+    });
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityRestrictedQueriesDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityRestrictedQueriesDUnitTest.java
new file mode 100644
index 0000000..bf39447
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityRestrictedQueriesDUnitTest.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionDestroyedException;
+import org.apache.geode.cache.client.Pool;
+import org.apache.geode.cache.client.PoolManager;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class QuerySecurityRestrictedQueriesDUnitTest extends QuerySecurityBase {
+
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataReader", "dataReaderRegion", "clusterManagerDataReader",
+        "clusterManagerDataReaderRegion", "super-user"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  @Before
+  public void configureCache() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new QueryTestObject(1, "John"), new QueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+  protected String regexForExpectedExceptions = ".*Unauthorized access.*";
+
+
+  /* ----- Implicit Getter Tests ----- */
+  @Test
+  public void checkUserAuthorizationsForSelectByImplicitGetterQuery() {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectImplicitGetterQuery() {
+    String query = "select r.name from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectCountOfImplicitGetterQuery() {
+    String query = "select count(r.name) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMaxOfImplicitGetterQuery() {
+    String query = "select max(r.name) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMinOfImplicitGetterQuery() {
+    String query = "select min(r.name) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectImplicitGetterFromRegionByImplicitGetterFromRegionQuery() {
+    String query = "select * from /" + regionName + " r1 where r1.name in (select r2.name from /"
+        + regionName + " r2)";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+  /* ----- Implicit Getter Tests ----- */
+
+  /* ----- Direct Getter Tests ----- */
+  @Test
+  public void checkUserAuthorizationsForSelectByDirectGetterQuery() {
+    String query = "select * from /" + regionName + " r where r.getName = 'Beth'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectDirectGetterQuery() {
+    String query = "select r.getName() from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectCountOfDirectGetterQuery() {
+    String query = "select count(r.getId) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMaxOfDirectGetterQuery() {
+    String query = "select max(r.getId()) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectMinOfDirectGetterQuery() {
+    String query = "select min(getId()) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectDirectGetterFromRegionByDirectGetterFromRegionQuery() {
+    String query = "select * from /" + regionName
+        + " r1 where r1.getName in (select r2.getName from /" + regionName + " r2)";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectRegionContainsValueQuery() {
+    String query = "select * from /" + regionName + ".containsValue('value')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void usersWhoCanExecuteQueryShouldNotInvokeRegionCreateForSelectRegionCreateQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".create('key2', 15)";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+
+  @Test
+  // @Parameters(method = "getAllUsersWhoCanExecuteQuery")
+  public void usersWhoCanExecuteQueryShouldGetResultsForSelectCreateFromRegionQuery()
+      throws Exception {
+    String query = "select r.create('key2', 15) from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+  @Test
+  // @Parameters(method = "getAllUsersWhoCanExecuteQuery")
+  public void usersWhoCanExecuteQueryShouldNotInvokeDestroyForSelectRegionDestroyQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".destroyKey('" + keys[0] + "')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectRegionDestroyRegionQuery() {
+    String query = "select * from /" + regionName + ".destroyRegion()";
+    try {
+      executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+          regexForExpectedExceptions);
+    } catch (Throwable throwable) {
+      if (!(throwable.getCause().getCause() instanceof RegionDestroyedException)) {
+        throw throwable;
+      }
+    }
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectRegionGetQuery() {
+    String query = "select * from /" + regionName + ".getKey('" + keys[0] + "')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void usersWhoCanExecuteQueryShouldNotInvokeRegionPutForSelectRegionPutQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".put('key-2', 'something')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+
+
+  @Test
+  public void usersWhoCanExecuteQueryShouldNotInvokePutIfAbsentForSelectRegionPutIfAbsentQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".putIfAbsent('key-2', 'something')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+
+  @Test
+  @Parameters(method = "getAllUsersWhoCanExecuteQuery")
+  public void usersWhoCanExecuteQueryShouldNotInvokedRegionRemoveForSelectRegionRemoveQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".remove('key-0')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+  @Test
+  @Parameters(method = "getAllUsersWhoCanExecuteQuery")
+  public void usersWhoCannExecuteQueryShouldReceiveExpectedResultsForSelectRegionReplaceQuery()
+      throws Exception {
+    String query = "select * from /" + regionName + ".replace('key-0', 'something')";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+    executeAndConfirmRegionMatches(specificUserClient, regionName, Arrays.asList(values));
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetInterestListRegexRegionQuery() {
+    String query = "select r.getInterestListRegex from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetInterestListRegexParenRegionQuery() {
+    String query = "select r.getInterestListRegex() from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByGetClassQuery() {
+    String query = "select * from /" + regionName + " r where r.getClass != '1'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetClassRegionQuery() {
+    String query = "select r.getClass from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectGetClassWithParenthesisRegionQuery() {
+    String query = "select r.getClass() from /" + regionName + " r";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByClassQuery() {
+    String query = "select * from /" + regionName + " r where r.getClass != 'blah'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectByGetClassWithParenthesisQuery() {
+    String query = "select * from /" + regionName + " r where r.getClass() != '1'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+
+  @Test
+  public void checkUserAuthorizationsForSelectByCapitalClassQuery() {
+    String query = "select * from /" + regionName + " r where r.Class != '1'";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+
+  @Test
+  public void checkUserAuthorizationsForSelectRegionCloneQuery() {
+    String query = "select * from /" + regionName + ".clone";
+    executeQueryWithCheckForAccessPermissions(specificUserClient, query, regionName,
+        regexForExpectedExceptions);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityUnauthorizedUserBindParameterDUnitTest.java b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityUnauthorizedUserBindParameterDUnitTest.java
new file mode 100644
index 0000000..47647be
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/QuerySecurityUnauthorizedUserBindParameterDUnitTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.util.HashSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.internal.index.DummyQRegion;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+public class QuerySecurityUnauthorizedUserBindParameterDUnitTest extends QuerySecurityBase {
+
+  @Before
+  public void configureCache() {
+    String user = "dataReaderRegionKey";
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0", "key-1"};
+    values = new Object[] {new QueryTestObject(1, "John"), new QueryTestObject(3, "Beth")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+  }
+
+  @Test
+  public void userWithoutRegionAccessAndPassingInWrappedBindParameterShouldThrowException() {
+    String query = "select v from $1 r, r.values() v";
+    String regexForExpectedException = ".*DATA:READ.*";
+    specificUserClient.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      HashSet hashset = new HashSet();
+      hashset.add(region);
+      assertExceptionOccurred(getClientCache().getQueryService(), query, new Object[] {hashset},
+          regexForExpectedException);
+    });
+  }
+
+
+  // If DummyQRegion is ever serializable, then this test will fail and a security hole with query
+  // will have been opened
+  // That means a user could wrap a region in a dummy region and bypass the
+  // RestrictedMethodInvocationAuthorizer
+  @Test
+  public void userWithoutRegionAccessAndPassingInWrappedInDummyQRegionBindParameterShouldThrowSerializationException() {
+    String query = "select v from $1 r, r.values() v";
+    String regexForExpectedException = ".*failed serializing object.*";
+    specificUserClient.invoke(() -> {
+      Region region = getClientCache().getRegion(regionName);
+      HashSet hashset = new HashSet();
+      hashset.add(new DummyQRegion(region));
+      assertExceptionOccurred(getClientCache().getQueryService(), query, new Object[] {hashset},
+          regexForExpectedException);
+    });
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/UserPermissions.java b/geode-core/src/test/java/org/apache/geode/security/query/UserPermissions.java
new file mode 100644
index 0000000..bd87ae9
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/UserPermissions.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.geode.cache.query.internal.RestrictedMethodInvocationAuthorizer;
+import org.apache.geode.security.ResourcePermission;
+import org.apache.geode.security.ResourcePermission.Operation;
+import org.apache.geode.security.ResourcePermission.Resource;
+import org.apache.geode.security.TestSecurityManager;
+
+public class UserPermissions {
+  private TestSecurityManager manager = new TestSecurityManager();
+  public HashMap<String, List<String>> queryPermissions;
+
+  public UserPermissions() {
+    if (!manager.initializeFromJsonResource(
+        "org/apache/geode/management/internal/security/clientServer.json"))
+      throw new RuntimeException(
+          "Something bad happened while trying to load the TestSecurityManager with the org/apache/geode/management/internal/security/clientServer.json");
+  }
+
+
+  public String getUserPassword(String user) {
+    return manager.getUser(user).getPassword();
+  }
+
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/data/PdxQueryTestObject.java b/geode-core/src/test/java/org/apache/geode/security/query/data/PdxQueryTestObject.java
new file mode 100644
index 0000000..e2c2ff4
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/data/PdxQueryTestObject.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query.data;
+
+import java.io.Serializable;
+
+import org.apache.geode.pdx.PdxReader;
+import org.apache.geode.pdx.PdxSerializable;
+import org.apache.geode.pdx.PdxWriter;
+
+public class PdxQueryTestObject
+    implements PdxSerializable, Serializable /* just to pass around in test code */ {
+  public int id = -1;
+  private String name;
+  private int age = 1;
+
+  private boolean shouldThrowException = true;
+
+
+  public PdxQueryTestObject() {
+
+  }
+
+  public PdxQueryTestObject(int id, String name) {
+    this.id = id;
+    this.name = name;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String toString() {
+    return "Test_Object";
+  }
+
+  public int getAge() {
+    return age;
+  }
+
+  @Override
+  public void toData(PdxWriter writer) {
+    writer.writeInt("id", id);
+    writer.writeString("getName", name);
+  }
+
+  @Override
+  public void fromData(PdxReader reader) {
+    id = reader.readInt("id");
+    name = reader.readString("getName");
+  }
+
+  public boolean equals(Object o) {
+    if (o instanceof PdxQueryTestObject) {
+      PdxQueryTestObject other = (PdxQueryTestObject) o;
+      return other.id == this.id && other.name.equals(this.name);
+    }
+    return false;
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/security/query/data/QueryTestObject.java b/geode-core/src/test/java/org/apache/geode/security/query/data/QueryTestObject.java
new file mode 100644
index 0000000..70b535d
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/security/query/data/QueryTestObject.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.security.query.data;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class QueryTestObject implements Serializable {
+  public int id = -1;
+
+  private String name;
+
+  public Date dateField;
+
+  public QueryTestObject(int id, String name) {
+    this.id = id;
+    this.name = name;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String someMethod() {
+    return name + ":" + id;
+  }
+
+  public void setDateField(String dateString) {
+    try {
+      SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
+      dateField = sdf.parse(dateString);
+    } catch (ParseException e) {
+
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "Test_Object";
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    QueryTestObject qto = (QueryTestObject) obj;
+    return (this.id == qto.id && this.name.equals(qto.getName()));
+  }
+}
diff --git a/geode-core/src/test/resources/org/apache/geode/management/internal/security/clientServer.json b/geode-core/src/test/resources/org/apache/geode/management/internal/security/clientServer.json
index 8dedd78..b1ca997 100644
--- a/geode-core/src/test/resources/org/apache/geode/management/internal/security/clientServer.json
+++ b/geode-core/src/test/resources/org/apache/geode/management/internal/security/clientServer.json
@@ -104,6 +104,24 @@
       "operationsAllowed": [
         "DATA:WRITE:AuthRegion"
       ]
+    },
+    {
+      "name": "data-read-region",
+      "operationsAllowed": [
+        "DATA:READ:region"
+      ]
+    },
+    {
+      "name": "data-read-region-key",
+      "operationsAllowed": [
+        "DATA:READ:region:key"
+      ]
+    },
+    {
+      "name": "cluster-manage-query",
+      "operationsAllowed": [
+        "CLUSTER:MANAGE:QUERY"
+      ]
     }
   ],
   "users": [
@@ -144,39 +162,115 @@
       ]
     },
     {
-      "name":"authRegionUser",
+      "name": "authRegionUser",
       "password": "1234567",
       "roles": [
         "region-use"
       ]
     },
     {
-      "name":"authRegionManager",
+      "name": "authRegionManager",
       "password": "1234567",
       "roles": [
         "region-manage"
       ]
     },
     {
-      "name":"authRegionReader",
+      "name": "authRegionReader",
       "password": "1234567",
       "roles": [
         "region-read"
       ]
     },
     {
-      "name":"authRegionWriter",
+      "name": "authRegionWriter",
       "password": "1234567",
       "roles": [
         "region-write"
       ]
     },
     {
-      "name":"key1User",
+      "name": "key1User",
       "password": "1234567",
       "roles": [
         "region-use-key1"
       ]
+    },
+    {
+      "name": "dataReaderRegion",
+      "password": "1234567",
+      "roles": [
+        "data-read-region"
+      ]
+    },
+    {
+      "name": "dataReaderRegionKey",
+      "password": "1234567",
+      "roles": [
+        "data-read-region-key"
+      ]
+    },
+    {
+      "name": "clusterManager",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage"
+      ]
+    },
+    {
+      "name": "clusterManagerQuery",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage-query"
+      ]
+    },
+    {
+      "name": "clusterManagerDataReader",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage",
+        "data-read"
+      ]
+    },
+    {
+      "name": "clusterManagerQueryDataReader",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage-query",
+        "data-read"
+      ]
+    },
+    {
+      "name": "clusterManagerDataReaderRegion",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage",
+        "data-read-region"
+      ]
+    },
+    {
+      "name": "clusterManagerQueryDataReaderRegion",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage-query",
+        "data-read-region"
+      ]
+    },
+    {
+      "name": "clusterManagerDataReaderRegionKey",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage",
+        "data-read-region-key"
+      ]
+    },
+    {
+      "name": "clusterManagerQueryDataReaderRegionKey",
+      "password": "1234567",
+      "roles": [
+        "cluster-manage-query",
+        "data-read-region-key"
+      ]
     }
   ]
 }
diff --git a/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityAuthorizedUserDUnitTest.java b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityAuthorizedUserDUnitTest.java
new file mode 100644
index 0000000..298a273
--- /dev/null
+++ b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityAuthorizedUserDUnitTest.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.cq.dunit;
+
+import static org.apache.geode.internal.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import junitparams.Parameters;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.query.CqAttributesFactory;
+import org.apache.geode.cache.query.CqEvent;
+import org.apache.geode.cache.query.CqException;
+import org.apache.geode.cache.query.CqListener;
+import org.apache.geode.cache.query.CqQuery;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.RegionNotFoundException;
+import org.apache.geode.security.query.QuerySecurityBase;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class CqSecurityAuthorizedUserDUnitTest extends QuerySecurityBase {
+
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"dataReader", "dataReaderRegion", "clusterManagerDataReader",
+        "clusterManagerDataReaderRegion", "super-user"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  @Before
+  public void configureSpecificClientAndKeyAndValues() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0"};
+    values = new Object[] {new QueryTestObject(0, "John")};
+  }
+
+  // Variables that need to be shared across invoke calls.
+  protected static CqSecurityTestCqListener cqListener = null;
+
+  private String regexForExpectedExceptions = ".*Unauthorized access.*";
+
+  @Test
+  public void cqExecuteNoMethodInvocationWithUsersWithCqPermissionsWithPrepopulatedRegionShouldBeAllowed()
+      throws Exception {
+    putIntoRegion(superUserClient, keys, values, regionName);
+    String query = "select * from /" + regionName + " r where r.id = 0";
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.execute();
+    });
+
+    putIntoRegion(superUserClient, keys, new Object[] {new QueryTestObject(0, "Bethany")},
+        regionName);
+
+    specificUserClient.invoke(() -> {
+      Awaitility.await().atMost(30, TimeUnit.SECONDS)
+          .until(() -> assertEquals(1, cqListener.getNumEvent()));
+    });
+  }
+
+  @Test
+  public void cqExecuteWithMethodInvocationWithUsersWithCqPermissionsWithPrepopulatedRegionIsGettingExceptionInReplicatedRegion()
+      throws Exception {
+    putIntoRegion(superUserClient, keys, values, regionName);
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      executeCqButExpectException(cq, user, regexForExpectedExceptions);
+    });
+  }
+
+  @Test
+  public void cqExecuteWithInitialResultsWithMethodInvocationWithUsersWithCqPermissionsWithPrepopulatedRegionShouldBeDeniedBecauseOfInvocation()
+      throws Exception {
+    putIntoRegion(superUserClient, keys, values, regionName);
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      executeCqWithInitialResultsButExpectException(cq, user, regexForExpectedExceptions);
+    });
+  }
+
+
+  @Test
+  public void cqExecuteWithInitialResultsWithMethodInvocationWithUnpopulatedRegionAndFollowedByAPutShouldTriggerCqError()
+      throws Exception {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.executeWithInitialResults();
+    });
+
+    Object[] keys = {"key-0"};
+    Object[] values = {new QueryTestObject(1, "Mary")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    specificUserClient.invoke(() -> {
+      Awaitility.await().atMost(30, TimeUnit.SECONDS)
+          .until(() -> assertEquals(1, cqListener.getNumErrors()));
+    });
+  }
+
+  @Test
+  public void cqExecuteWithMethodInvocationWithUnpopulatedRegionAndFollowedByAPutShouldTriggerCqError()
+      throws Exception {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";;
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.execute();
+    });
+
+    Object[] keys = {"key-0"};
+    Object[] values = {new QueryTestObject(1, "Mary")};
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    specificUserClient.invoke(() -> {
+      Awaitility.await().atMost(30, TimeUnit.SECONDS)
+          .until(() -> assertEquals(1, cqListener.getNumErrors()));
+    });
+  }
+
+  @Test
+  public void cqCanBeClosedByTheCreator() throws Exception {
+    String query = "select * from /" + regionName + " r where r.id = 0";
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.execute();
+      cq.close();
+      assertTrue(cq.isClosed());
+    });
+    assertEquals(0, server.getCache().getCqService().getAllCqs().size());
+  }
+
+
+  protected CqQuery createCq(QueryService queryService, String query, CqListener cqListener)
+      throws CqException {
+    CqAttributesFactory cqaf = new CqAttributesFactory();
+    cqaf.addCqListener(cqListener);
+    CqQuery cq = queryService.newCq(query, cqaf.create());
+    return cq;
+  }
+
+  protected void executeCqButExpectException(CqQuery cq, String user,
+      String regexForExpectedException) {
+    try {
+      cq.execute();
+      fail("Expected an exception when executing cq:" + cq.getQueryString() + " with user:" + user);
+    } catch (RegionNotFoundException | CqException e) {
+      if (!e.getMessage().matches(regexForExpectedException)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage().matches(regexForExpectedException)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail("Exception thrown did not match:" + regexForExpectedException + ".  Instead was:" + e);
+      }
+    }
+  }
+
+  private void executeCqWithInitialResultsButExpectException(CqQuery cq, String user,
+      String regexForExpectedException) {
+    try {
+      cq.executeWithInitialResults();
+      fail("Expected an exception when executing cq:" + cq + " with user:" + user);
+    } catch (RegionNotFoundException | CqException e) {
+      e.printStackTrace();
+      if (!e.getMessage().matches(regexForExpectedException)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage() != null && cause.getMessage().matches(regexForExpectedException)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail("Exception thrown did not match:" + regexForExpectedException + ".  Instead was:" + e);
+      }
+    }
+
+  }
+
+  public class CqSecurityTestCqListener implements CqListener {
+
+    private int numEvents = 0;
+    private int numErrors = 0;
+
+    @Override
+    public void onEvent(CqEvent aCqEvent) {
+      numEvents++;
+    }
+
+    @Override
+    public void onError(CqEvent aCqEvent) {
+      numErrors++;
+    }
+
+    public int getNumEvent() {
+      return numEvents;
+    }
+
+    public int getNumErrors() {
+      return numErrors;
+    }
+
+    @Override
+    public void close() {
+
+    }
+  }
+}
diff --git a/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedAuthorizedUserDUnitTest.java b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedAuthorizedUserDUnitTest.java
new file mode 100644
index 0000000..6fe9f87
--- /dev/null
+++ b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedAuthorizedUserDUnitTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.cq.dunit;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.query.CqQuery;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class CqSecurityPartitionedAuthorizedUserDUnitTest
+    extends CqSecurityAuthorizedUserDUnitTest {
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+  @Test
+  public void cqExecuteWithMethodInvocationWithUsersWithCqPermissionsWithPrepopulatedRegionIsGettingExceptionInReplicatedRegion()
+      throws Exception {
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityAuthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.execute();
+    });
+
+    putIntoRegion(superUserClient, keys, values, regionName);
+
+    specificUserClient.invoke(() -> {
+      Awaitility.await().atMost(30, TimeUnit.SECONDS)
+          .until(() -> assertEquals(1, cqListener.getNumErrors()));
+    });
+  }
+}
diff --git a/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedUnauthorizedUserDUnitTest.java b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedUnauthorizedUserDUnitTest.java
new file mode 100644
index 0000000..d3e3adb
--- /dev/null
+++ b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityPartitionedUnauthorizedUserDUnitTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.cq.dunit;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.query.CqQuery;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.security.query.UserPermissions;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class CqSecurityPartitionedUnauthorizedUserDUnitTest
+    extends CqSecurityUnauthorizedUserDUnitTest {
+  public RegionShortcut getRegionType() {
+    return RegionShortcut.PARTITION;
+  }
+
+}
diff --git a/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityUnauthorizedUserDUnitTest.java b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityUnauthorizedUserDUnitTest.java
new file mode 100644
index 0000000..2e9fc8a
--- /dev/null
+++ b/geode-cq/src/test/java/org/apache/geode/cache/query/cq/dunit/CqSecurityUnauthorizedUserDUnitTest.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.cache.query.cq.dunit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.query.CqAttributesFactory;
+import org.apache.geode.cache.query.CqEvent;
+import org.apache.geode.cache.query.CqException;
+import org.apache.geode.cache.query.CqListener;
+import org.apache.geode.cache.query.CqQuery;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.RegionNotFoundException;
+import org.apache.geode.security.query.QuerySecurityBase;
+import org.apache.geode.security.query.data.QueryTestObject;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@Category({DistributedTest.class, SecurityTest.class})
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
+public class CqSecurityUnauthorizedUserDUnitTest extends QuerySecurityBase {
+
+  @Parameterized.Parameters
+  public static Object[] usersAllowed() {
+    return new Object[] {"stranger", "dataReaderRegionKey", "clusterManagerQuery",
+        "clusterManagerDataReaderRegionKey", "dataWriter"};
+  }
+
+  @Parameterized.Parameter
+  public String user;
+
+  @Before
+  public void configureSpecificClientAndKeyAndValues() {
+    createClientCache(specificUserClient, user, userPerms.getUserPassword(user));
+    createProxyRegion(specificUserClient, regionName);
+
+    keys = new Object[] {"key-0"};
+    values = new Object[] {new QueryTestObject(0, "John")};
+  }
+
+  // Variables that need to be shared across invoke calls.
+  protected static CqSecurityTestCqListener cqListener = null;
+
+  public List<String> getAllUsersOnlyAllowedWrite() {
+    return Arrays.asList("dataWriter");
+  }
+
+
+  private String regexForExpectedExceptions = ".*DATA:READ:.*";
+
+  @Test
+  public void cqExecuteNoMethodInvocationWithUsersWithoutCqPermissionsWithPrepopulatedRegionShouldThrowSecurityException()
+      throws Exception {
+    putIntoRegion(superUserClient, keys, values, regionName);
+    String query = "select * from /" + regionName + " r where r.id = 0";
+
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityUnauthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      executeCqButExpectException(cq, user, regexForExpectedExceptions);
+    });
+  }
+
+  @Test
+  public void cqExecuteWithInitialResultsWithMethodInvocationWithoutPermissionWithUnpopulatedRegionThrowSecurityException()
+      throws Exception {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityUnauthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      executeCqWithInitialResultsButExpectException(cq, user, regexForExpectedExceptions);
+    });
+  }
+
+  @Test
+  public void cqExecuteWithOutPermissionsWithUnpopulatedRegionShouldNotAllowCq() throws Exception {
+    String query = "select * from /" + regionName + " r where r.name = 'Beth'";
+    specificUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityUnauthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      executeCqButExpectException(cq, user, regexForExpectedExceptions);
+    });
+  }
+
+  @Test
+  public void cqCreatedByAllowedUserButPutDoneByUnallowedReaderShouldStillExecuteWithCqEvent()
+      throws Exception {
+    assumeTrue(user.equals("dataWriter"));
+    String query = "select * from /" + regionName + " r where r.id = 1";
+    superUserClient.invoke(() -> {
+      QueryService queryService = getClientCache().getQueryService();
+      CqSecurityTestCqListener cqListener = new CqSecurityTestCqListener();
+      CqSecurityUnauthorizedUserDUnitTest.cqListener = cqListener;
+      CqQuery cq = createCq(queryService, query, cqListener);
+      cq.execute();
+    });
+
+    Object[] keys = {"key-0"};
+    Object[] values = {new QueryTestObject(1, "Mary")};
+    putIntoRegion(specificUserClient, keys, values, regionName);
+
+    superUserClient.invoke(() -> {
+      Awaitility.await().atMost(30, TimeUnit.SECONDS)
+          .until(() -> assertEquals(1, cqListener.getNumEvent()));
+    });
+  }
+
+
+  protected CqQuery createCq(QueryService queryService, String query, CqListener cqListener)
+      throws CqException {
+    CqAttributesFactory cqaf = new CqAttributesFactory();
+    cqaf.addCqListener(cqListener);
+    CqQuery cq = queryService.newCq(query, cqaf.create());
+    return cq;
+  }
+
+  protected void executeCqButExpectException(CqQuery cq, String user,
+      String regexForExpectedException) {
+    try {
+      cq.execute();
+      fail("Expected an exception when executing cq:" + cq.getQueryString() + " with user:" + user);
+    } catch (RegionNotFoundException | CqException e) {
+      if (!e.getMessage().matches(regexForExpectedException)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage().matches(regexForExpectedException)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail("Exception thrown did not match:" + regexForExpectedException + ".  Instead was:" + e);
+      }
+    }
+  }
+
+  private void executeCqWithInitialResultsButExpectException(CqQuery cq, String user,
+      String regexForExpectedException) {
+    try {
+      cq.executeWithInitialResults();
+      fail("Expected an exception when executing cq:" + cq + " with user:" + user);
+    } catch (RegionNotFoundException | CqException e) {
+      e.printStackTrace();
+      if (!e.getMessage().matches(regexForExpectedException)) {
+        Throwable cause = e.getCause();
+        while (cause != null) {
+          if (cause.getMessage() != null && cause.getMessage().matches(regexForExpectedException)) {
+            return;
+          }
+          cause = cause.getCause();
+        }
+        e.printStackTrace();
+        fail("Exception thrown did not match:" + regexForExpectedException + ".  Instead was:" + e);
+      }
+    }
+  }
+
+  public class CqSecurityTestCqListener implements CqListener {
+
+    private int numEvents = 0;
+
+    @Override
+    public void onEvent(CqEvent aCqEvent) {
+      numEvents++;
+    }
+
+    @Override
+    public void onError(CqEvent aCqEvent) {
+
+    }
+
+    public int getNumEvent() {
+      return numEvents;
+    }
+
+
+    @Override
+    public void close() {
+
+    }
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].