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>'].