diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6f4916df0909..71a178b74322 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,3 +7,4 @@ **/client/** @hazelcast/apis **/hazelcast-client** @hazelcast/apis **/serialization** @hazelcast/apis +**/MessageTaskSecurityTest* @hazelcast/security-working-group diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/CreateProxiesMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/CreateProxiesMessageTask.java index 942f5ddb1971..652beb5d717b 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/CreateProxiesMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/CreateProxiesMessageTask.java @@ -22,10 +22,14 @@ import com.hazelcast.core.MemberLeftException; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.SecurityContext; +import com.hazelcast.security.permission.ActionConstants; import com.hazelcast.spi.impl.operationservice.Operation; +import com.hazelcast.spi.impl.proxyservice.ProxyService; import com.hazelcast.spi.impl.proxyservice.impl.ProxyInfo; import com.hazelcast.spi.impl.proxyservice.impl.operations.PostJoinProxyOperation; +import java.security.AccessControlException; import java.security.Permission; import java.util.ArrayList; import java.util.Collection; @@ -37,6 +41,8 @@ public class CreateProxiesMessageTask extends AbstractMultiTargetMessageTask>> implements Supplier { + private List> filteredProxies; + public CreateProxiesMessageTask(ClientMessage clientMessage, Node node, Connection connection) { super(clientMessage, node, connection); } @@ -47,8 +53,8 @@ protected Supplier createOperationSupplier() { @Override public Operation get() { - List proxyInfos = new ArrayList(parameters.size()); - for (Map.Entry proxy : parameters) { + List proxyInfos = new ArrayList(filteredProxies.size()); + for (Map.Entry proxy : filteredProxies) { proxyInfos.add(new ProxyInfo(proxy.getValue(), proxy.getKey(), endpoint.getUuid())); } return new PostJoinProxyOperation(proxyInfos); @@ -79,11 +85,49 @@ protected ClientMessage encodeResponse(Object response) { return ClientCreateProxiesCodec.encodeResponse(); } + /** + *@see #beforeProcess() + */ @Override public Permission getRequiredPermission() { return null; } + @Override + protected void beforeProcess() { + // replacement for getRequiredPermission-based checks, we have to check multiple permission + SecurityContext securityContext = clientEngine.getSecurityContext(); + if (securityContext != null) { + filteredProxies = new ArrayList<>(parameters.size()); + ProxyService proxyService = clientEngine.getProxyService(); + for (Map.Entry proxy : parameters) { + String objectName = proxy.getKey(); + String serviceName = proxy.getValue(); + if (proxyService.existsDistributedObject(serviceName, objectName)) { + continue; + } + try { + Permission permission = ActionConstants.getPermission(objectName, serviceName, + ActionConstants.ACTION_CREATE); + securityContext.checkPermission(endpoint.getSubject(), permission); + filteredProxies.add(proxy); + } catch (AccessControlException ace) { + logger.info("Insufficient client permissions. Proxy won't be created for type '" + serviceName + "': " + + objectName); + if (logger.isFineEnabled()) { + logger.fine("Skipping proxy creation due to AccessControlException", ace); + } + } catch (Exception e) { + // unknown serviceName or another unexpected issue + logger.warning("Proxy won't be created for type '" + serviceName + "': " + objectName, e); + } + } + } else { + filteredProxies = parameters; + } + super.beforeProcess(); + } + @Override public String getServiceName() { return null; diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddNearCacheInvalidationListenerTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddNearCacheInvalidationListenerTask.java index 696628ba029a..02ca6faeb7b5 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddNearCacheInvalidationListenerTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddNearCacheInvalidationListenerTask.java @@ -26,6 +26,8 @@ import com.hazelcast.internal.nearcache.impl.invalidation.Invalidation; import com.hazelcast.internal.nio.Connection; import com.hazelcast.internal.serialization.Data; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import java.security.Permission; import java.util.List; @@ -118,7 +120,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_LISTEN); } } diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddPartitionLostListenerMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddPartitionLostListenerMessageTask.java index ae18b4a9e866..4480dec610b2 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddPartitionLostListenerMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheAddPartitionLostListenerMessageTask.java @@ -26,6 +26,8 @@ import com.hazelcast.client.impl.protocol.task.AbstractAddListenerMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.eventservice.EventFilter; import com.hazelcast.spi.impl.eventservice.EventRegistration; import com.hazelcast.spi.impl.eventservice.EventService; @@ -96,7 +98,7 @@ public Object[] getParameters() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheCreateConfigMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheCreateConfigMessageTask.java index 569e884a14b8..0ff53773e84c 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheCreateConfigMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheCreateConfigMessageTask.java @@ -16,6 +16,7 @@ package com.hazelcast.client.impl.protocol.task.cache; +import com.hazelcast.cache.CacheUtil; import com.hazelcast.cache.impl.CacheService; import com.hazelcast.cache.impl.ICacheService; import com.hazelcast.cache.impl.PreJoinCacheConfig; @@ -26,6 +27,8 @@ import com.hazelcast.config.CacheConfig; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.InternalCompletableFuture; import com.hazelcast.spi.merge.SplitBrainMergePolicyProvider; @@ -79,7 +82,8 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + CacheConfig cacheConfig = parameters.cacheConfig.asCacheConfig(serializationService); + return new CachePermission(CacheUtil.getDistributedObjectName(cacheConfig.getName()), ActionConstants.ACTION_CREATE); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheDestroyMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheDestroyMessageTask.java index 2ff9cfb57d37..f53200e6856a 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheDestroyMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheDestroyMessageTask.java @@ -23,6 +23,8 @@ import com.hazelcast.client.impl.protocol.task.AbstractInvocationMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.operationservice.InvocationBuilder; import com.hazelcast.spi.impl.operationservice.Operation; import com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl; @@ -66,12 +68,12 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters, ActionConstants.ACTION_DESTROY); } @Override public String getDistributedObjectName() { - return null; + return parameters; } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheGetConfigMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheGetConfigMessageTask.java index 5072c5620214..236ed78542eb 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheGetConfigMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheGetConfigMessageTask.java @@ -24,6 +24,8 @@ import com.hazelcast.config.CacheConfig; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.operationservice.Operation; import java.security.Permission; @@ -70,7 +72,7 @@ public Object[] getParameters() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_READ); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheIterateMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheIterateMessageTask.java index e14af7f6628b..ca983d38f7e7 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheIterateMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheIterateMessageTask.java @@ -24,8 +24,11 @@ import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.iteration.IterationPointer; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.operationservice.Operation; +import java.security.Permission; import java.util.Collections; import static com.hazelcast.internal.iteration.IterationPointer.decodePointers; @@ -79,4 +82,9 @@ public Object[] getParameters() { public String getMethodName() { return "iterator"; } + + @Override + public Permission getRequiredPermission() { + return new CachePermission(parameters.name, ActionConstants.ACTION_READ); + } } diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheListenerRegistrationMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheListenerRegistrationMessageTask.java index 3f2858b3201f..b04ad58ada8f 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheListenerRegistrationMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheListenerRegistrationMessageTask.java @@ -23,6 +23,8 @@ import com.hazelcast.client.impl.protocol.task.AbstractTargetMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.operationservice.Operation; import javax.cache.configuration.CacheEntryListenerConfiguration; @@ -69,7 +71,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheLoadAllMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheLoadAllMessageTask.java index ee92dfa9612e..ac52c1fcba54 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheLoadAllMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheLoadAllMessageTask.java @@ -25,6 +25,8 @@ import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; import com.hazelcast.internal.serialization.Data; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import com.hazelcast.spi.impl.operationservice.OperationFactory; import javax.cache.CacheException; @@ -84,7 +86,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_READ); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheManagementConfigMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheManagementConfigMessageTask.java index 7e88e72e0cce..b787c8feb77c 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheManagementConfigMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheManagementConfigMessageTask.java @@ -23,6 +23,7 @@ import com.hazelcast.client.impl.protocol.task.AbstractTargetMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ConfigPermission; import com.hazelcast.spi.impl.operationservice.Operation; import java.security.Permission; @@ -67,7 +68,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new ConfigPermission(); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemoveInvalidationListenerMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemoveInvalidationListenerMessageTask.java index ef40eb3b77b9..111dc7427c90 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemoveInvalidationListenerMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemoveInvalidationListenerMessageTask.java @@ -22,6 +22,8 @@ import com.hazelcast.client.impl.protocol.task.AbstractRemoveListenerMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import java.security.Permission; import java.util.UUID; @@ -72,7 +74,7 @@ public String getDistributedObjectName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemovePartitionLostListenerMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemovePartitionLostListenerMessageTask.java index 9caec8865425..d2ae37e6a783 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemovePartitionLostListenerMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/cache/CacheRemovePartitionLostListenerMessageTask.java @@ -23,6 +23,8 @@ import com.hazelcast.client.impl.protocol.task.AbstractRemoveListenerMessageTask; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.CachePermission; import java.security.Permission; import java.util.UUID; @@ -70,7 +72,7 @@ public String getDistributedObjectName() { @Override public Permission getRequiredPermission() { - return null; + return new CachePermission(parameters.name, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapDestroyCacheMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapDestroyCacheMessageTask.java index c962ad022b72..6fc14c5dd8f6 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapDestroyCacheMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapDestroyCacheMessageTask.java @@ -23,6 +23,8 @@ import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; import com.hazelcast.map.impl.querycache.subscriber.operation.DestroyQueryCacheOperation; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.spi.impl.operationservice.Operation; import java.security.Permission; @@ -76,7 +78,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.mapName, ActionConstants.ACTION_DESTROY); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchEntriesMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchEntriesMessageTask.java index dc5f909130ad..5627e204f34e 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchEntriesMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchEntriesMessageTask.java @@ -24,6 +24,8 @@ import com.hazelcast.map.impl.MapService; import com.hazelcast.map.impl.iterator.MapEntriesWithCursor; import com.hazelcast.map.impl.operation.MapOperationProvider; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.spi.impl.operationservice.Operation; import java.security.Permission; @@ -67,7 +69,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.name, ActionConstants.ACTION_READ); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchKeysMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchKeysMessageTask.java index c259b1040692..108a943c8386 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchKeysMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapFetchKeysMessageTask.java @@ -24,6 +24,8 @@ import com.hazelcast.map.impl.MapService; import com.hazelcast.map.impl.iterator.MapKeysWithCursor; import com.hazelcast.map.impl.operation.MapOperationProvider; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.spi.impl.operationservice.Operation; import java.security.Permission; @@ -67,7 +69,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.name, ActionConstants.ACTION_READ); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateMessageTask.java index 3dbdcaaae5d5..62bd02a7afb2 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateMessageTask.java @@ -33,6 +33,8 @@ import com.hazelcast.internal.nio.Connection; import com.hazelcast.internal.serialization.Data; import com.hazelcast.query.Predicate; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.spi.impl.operationservice.InvocationBuilder; import com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl; import com.hazelcast.internal.util.ExceptionUtil; @@ -136,7 +138,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.mapName, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateWithValueMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateWithValueMessageTask.java index cf23663face8..1d37518cf30c 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateWithValueMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapPublisherCreateWithValueMessageTask.java @@ -33,6 +33,8 @@ import com.hazelcast.internal.nio.Connection; import com.hazelcast.internal.serialization.Data; import com.hazelcast.query.Predicate; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.spi.impl.operationservice.InvocationBuilder; import com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl; import com.hazelcast.internal.util.ExceptionUtil; @@ -98,8 +100,8 @@ private static Set> fetchMapSnapshotFrom(List futu Object result; try { result = future.get(); - } catch (Throwable t) { - throw ExceptionUtil.rethrow(t); + } catch (Exception e) { + throw ExceptionUtil.rethrow(e); } if (result == null) { continue; @@ -139,7 +141,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.mapName, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapRemovePartitionLostListenerMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapRemovePartitionLostListenerMessageTask.java index c9c49762f728..b5874fb11f07 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapRemovePartitionLostListenerMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapRemovePartitionLostListenerMessageTask.java @@ -22,6 +22,8 @@ import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; import com.hazelcast.map.impl.MapService; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import java.security.Permission; import java.util.UUID; @@ -68,7 +70,7 @@ public String getDistributedObjectName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.name, ActionConstants.ACTION_LISTEN); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapSetReadCursorMessageTask.java b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapSetReadCursorMessageTask.java index bf6346aa50d0..8f54d9a4bed6 100644 --- a/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapSetReadCursorMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/client/impl/protocol/task/map/MapSetReadCursorMessageTask.java @@ -22,6 +22,8 @@ import com.hazelcast.instance.impl.Node; import com.hazelcast.map.impl.MapService; import com.hazelcast.map.impl.querycache.subscriber.operation.SetReadCursorOperation; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.internal.nio.Connection; import com.hazelcast.spi.impl.operationservice.Operation; @@ -55,7 +57,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters.mapName, ActionConstants.ACTION_READ); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/semaphore/client/GetSemaphoreTypeMessageTask.java b/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/semaphore/client/GetSemaphoreTypeMessageTask.java index 66b01074872e..5bbeb51970b0 100644 --- a/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/semaphore/client/GetSemaphoreTypeMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/semaphore/client/GetSemaphoreTypeMessageTask.java @@ -23,6 +23,7 @@ import com.hazelcast.cp.internal.datastructures.semaphore.SemaphoreService; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.SemaphorePermission; import java.security.Permission; @@ -59,7 +60,7 @@ public String getServiceName() { @Override public Permission getRequiredPermission() { - return null; + return new SemaphorePermission(parameters); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/spi/client/DestroyRaftObjectMessageTask.java b/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/spi/client/DestroyRaftObjectMessageTask.java index 87c458242a62..7f9b0ee3a229 100644 --- a/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/spi/client/DestroyRaftObjectMessageTask.java +++ b/hazelcast/src/main/java/com/hazelcast/cp/internal/datastructures/spi/client/DestroyRaftObjectMessageTask.java @@ -22,9 +22,12 @@ import com.hazelcast.cp.internal.datastructures.spi.operation.DestroyRaftObjectOp; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; import java.security.Permission; +import static com.hazelcast.security.permission.ActionConstants.getPermission; + /** * Client message task for destroying Raft objects */ @@ -61,7 +64,7 @@ public String getDistributedObjectName() { @Override public Permission getRequiredPermission() { - return null; + return getPermission(parameters.objectName, parameters.serviceName, ActionConstants.ACTION_DESTROY); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/sql/impl/client/SqlMappingDdlTask.java b/hazelcast/src/main/java/com/hazelcast/sql/impl/client/SqlMappingDdlTask.java index f110f732f924..b0695b02fb28 100644 --- a/hazelcast/src/main/java/com/hazelcast/sql/impl/client/SqlMappingDdlTask.java +++ b/hazelcast/src/main/java/com/hazelcast/sql/impl/client/SqlMappingDdlTask.java @@ -20,6 +20,8 @@ import com.hazelcast.client.impl.protocol.codec.SqlMappingDdlCodec; import com.hazelcast.instance.impl.Node; import com.hazelcast.internal.nio.Connection; +import com.hazelcast.security.permission.ActionConstants; +import com.hazelcast.security.permission.MapPermission; import com.hazelcast.sql.impl.SqlInternalService; import com.hazelcast.sql.impl.SqlServiceImpl; @@ -72,6 +74,6 @@ public Object[] getParameters() { @Override public Permission getRequiredPermission() { - return null; + return new MapPermission(parameters, ActionConstants.ACTION_READ); } } diff --git a/hazelcast/src/test/java/com/hazelcast/client/protocol/MessageTaskSecurityTest.java b/hazelcast/src/test/java/com/hazelcast/client/protocol/MessageTaskSecurityTest.java new file mode 100644 index 000000000000..b5e7b107b973 --- /dev/null +++ b/hazelcast/src/test/java/com/hazelcast/client/protocol/MessageTaskSecurityTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2008-2022, Hazelcast, Inc. All Rights Reserved. + * + * Licensed 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 com.hazelcast.client.protocol; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.reflections.Reflections; + +import com.hazelcast.client.impl.protocol.task.AbstractMessageTask; +import com.hazelcast.client.impl.protocol.task.AddBackupListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.AddClusterViewListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.AddDistributedObjectListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.AddMigrationListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.AddPartitionLostListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.AuthenticationCustomCredentialsMessageTask; +import com.hazelcast.client.impl.protocol.task.AuthenticationMessageTask; +import com.hazelcast.client.impl.protocol.task.ClientStatisticsMessageTask; +import com.hazelcast.client.impl.protocol.task.CreateProxiesMessageTask; +import com.hazelcast.client.impl.protocol.task.GetDistributedObjectsMessageTask; +import com.hazelcast.client.impl.protocol.task.MessageTask; +import com.hazelcast.client.impl.protocol.task.NoSuchMessageTask; +import com.hazelcast.client.impl.protocol.task.PingMessageTask; +import com.hazelcast.client.impl.protocol.task.RemoveDistributedObjectListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.RemoveMigrationListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.RemovePartitionLostListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.TriggerPartitionAssignmentMessageTask; +import com.hazelcast.client.impl.protocol.task.cache.CacheFetchNearCacheInvalidationMetadataTask; +import com.hazelcast.client.impl.protocol.task.map.MapAddListenerMessageTask; +import com.hazelcast.client.impl.protocol.task.map.MapFetchNearCacheInvalidationMetadataTask; +import com.hazelcast.client.impl.protocol.task.map.MapMadePublishableMessageTask; +import com.hazelcast.client.impl.protocol.task.schema.FetchSchemaMessageTask; +import com.hazelcast.client.impl.protocol.task.schema.SendAllSchemasMessageTask; +import com.hazelcast.client.impl.protocol.task.schema.SendSchemaMessageTask; +import com.hazelcast.cp.internal.client.AddCPGroupAvailabilityListenerMessageTask; +import com.hazelcast.cp.internal.client.AddCPMembershipListenerMessageTask; +import com.hazelcast.cp.internal.client.RemoveCPGroupAvailabilityListenerMessageTask; +import com.hazelcast.cp.internal.client.RemoveCPMembershipListenerMessageTask; +import com.hazelcast.cp.internal.datastructures.spi.client.CreateRaftGroupMessageTask; +import com.hazelcast.sql.impl.client.SqlCloseMessageTask; +import com.hazelcast.sql.impl.client.SqlExecuteMessageTask; +import com.hazelcast.sql.impl.client.SqlFetchMessageTask; +import com.hazelcast.test.HazelcastParallelClassRunner; +import com.hazelcast.test.annotation.QuickTest; + +import javassist.ClassPool; +import javassist.NotFoundException; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.ClassFile; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.Mnemonic; + +/** + * Verifies the {@code getRequiredPermission()} method doesn't simply return null in client {@link MessageTask} instances. + */ +@RunWith(HazelcastParallelClassRunner.class) +@Category({ QuickTest.class }) +public class MessageTaskSecurityTest { + + private static final String[] RETURN_NULL_OPS = { "aconst_null", "areturn" }; + private static final Map SKIP_CLASS_MAP = new ConcurrentHashMap<>(); + + static { + skip(AddBackupListenerMessageTask.class, + "Adds listener called for every invocation for smart clients if backupAcks are enabled"); + skip(AddClusterViewListenerMessageTask.class, "Adds listener for listening to member list and partition table changes"); + skip(AddDistributedObjectListenerMessageTask.class, "Adds distributed object listener by user's request"); + skip(AddMigrationListenerMessageTask.class, "Adds an internal listener"); + skip(AddPartitionLostListenerMessageTask.class, "Adds an internal listener"); + skip(CacheFetchNearCacheInvalidationMetadataTask.class, "Internal task used by RepairingTask"); + skip(ClientStatisticsMessageTask.class, "Client statistics collection task"); + skip(GetDistributedObjectsMessageTask.class, "Gets proxies"); + skip(MapAddListenerMessageTask.class, "Permissions checked by subsequent MapPublisherCreate* tasks"); + skip(MapFetchNearCacheInvalidationMetadataTask.class, "Internal task used by RepairingTask"); + skip(MapMadePublishableMessageTask.class, "Internal task used by RepairingTask"); + skip(NoSuchMessageTask.class, "Fallback MessageTask type - no cluster action performed"); + skip(PingMessageTask.class, "Heart beat type - no cluster action performed"); + skip(RemoveDistributedObjectListenerMessageTask.class, "Removes distributed object listener by user's request"); + skip(RemoveMigrationListenerMessageTask.class, "Adds an internal listener"); + skip(RemovePartitionLostListenerMessageTask.class, "Adds an internal listener"); + skip(FetchSchemaMessageTask.class, "Fetch compact-serialization schema"); + skip(SendAllSchemasMessageTask.class, "Send compact-serialization schemas"); + skip(SendSchemaMessageTask.class, "Send a compact-serialization schema"); + skip(TriggerPartitionAssignmentMessageTask.class, "Triggers first partition arrangement"); + skip(AddCPGroupAvailabilityListenerMessageTask.class, "Listener for the cluster-topology change"); + skip(AddCPMembershipListenerMessageTask.class, "Listener for the cluster-topology change"); + skip(RemoveCPGroupAvailabilityListenerMessageTask.class, "Listener for the cluster-topology change"); + skip(RemoveCPMembershipListenerMessageTask.class, "Listener for the cluster-topology change"); + skip(CreateRaftGroupMessageTask.class, "Initial message while creating a Client proxy for any CP object"); + skip(SqlExecuteMessageTask.class, "Permissions for specific objects are checked based on parsed query text"); + skip(SqlCloseMessageTask.class, "Follow up SQL message where queryId is present"); + skip(SqlFetchMessageTask.class, "Follow up SQL message where queryId is present"); + skip(AuthenticationMessageTask.class, "Authentication message processing"); + skip(AuthenticationCustomCredentialsMessageTask.class, "Authentication message processing"); + skip(CreateProxiesMessageTask.class, "Permissions handled in beforeProcess() method"); + } + + @Test + public void testGetRequiredPermissions() throws Exception { + Reflections reflections = new Reflections("com.hazelcast"); + Set> subTypes = reflections.getSubTypesOf(AbstractMessageTask.class); + for (Class clazz : subTypes) { + if (!Modifier.isAbstract(clazz.getModifiers())) { + assertGetRequiredPermission(clazz.getName()); + } + } + } + + @Test + public void testCreateProxiesOverridesBeforeProcess() throws Exception { + assertNotNull(CreateProxiesMessageTask.class.getDeclaredMethod("beforeProcess")); + } + + private void assertGetRequiredPermission(String clsname) throws Exception { + if (SKIP_CLASS_MAP.containsKey(clsname)) { + return; + } + boolean returnsNull = doesGetRequiredPermissionSimpleReturnNull(clsname); + assertFalse(clsname + " returns null in getRequiredPermission()", returnsNull); + } + + private boolean doesGetRequiredPermissionSimpleReturnNull(String clsname) throws NotFoundException, Exception, BadBytecode { + if (clsname == null) { + fail("Class with getRequiredPermission() method implementation not found"); + } + ClassPool cp = ClassPool.getDefault(); + ClassFile cf = cp.get(clsname).getClassFile(); + MethodInfo minfo = cf.getMethod("getRequiredPermission"); + if (minfo == null) { + return doesGetRequiredPermissionSimpleReturnNull(cf.getSuperclass()); + } + CodeAttribute ca = minfo.getCodeAttribute(); + CodeIterator ci = ca.iterator(); + String[] ops = new String[RETURN_NULL_OPS.length]; + int i = 0; + while (ci.hasNext()) { + i++; + if (i > RETURN_NULL_OPS.length) { + return false; + } + int index = ci.next(); + int op = ci.byteAt(index); + ops[i - 1] = Mnemonic.OPCODE[op]; + } + return Arrays.equals(RETURN_NULL_OPS, ops); + } + + private static void skip(Class classToSkip, String reason) { + Assertions.assertThat(reason).isNotEmpty(); + SKIP_CLASS_MAP.put(classToSkip.getName(), reason); + } +}