You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by bs...@apache.org on 2017/08/17 22:24:08 UTC
geode git commit: GEODE-3447 Implement client authorization for the
new protocol
Repository: geode
Updated Branches:
refs/heads/feature/GEODE-3447 [created] 7d6a0a3e3
GEODE-3447 Implement client authorization for the new protocol
Implementation of authorization checks for the new protocol.
Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/7d6a0a3e
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/7d6a0a3e
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/7d6a0a3e
Branch: refs/heads/feature/GEODE-3447
Commit: 7d6a0a3e31a11c5bf584fe59efbfaebaf98e6c4d
Parents: 7cbbf67
Author: Bruce Schuchardt <bs...@pivotal.io>
Authored: Thu Aug 17 15:22:57 2017 -0700
Committer: Bruce Schuchardt <bs...@pivotal.io>
Committed: Thu Aug 17 15:22:57 2017 -0700
----------------------------------------------------------------------
.../sockets/ClientProtocolMessageHandler.java | 6 +-
.../GenericProtocolServerConnection.java | 3 +-
.../geode/security/NoOpStreamAuthenticator.java | 7 +-
.../geode/security/NoOpStreamAuthorizer.java | 26 +++
.../geode/security/StreamAuthenticator.java | 10 +-
.../apache/geode/security/StreamAuthorizer.java | 8 +
.../GenericProtocolServerConnectionTest.java | 2 +-
.../protocol/protobuf/OperationContext.java | 10 +-
.../protocol/protobuf/ProtobufOpsProcessor.java | 17 +-
.../protobuf/ProtobufSimpleAuthenticator.java | 18 +-
.../protobuf/ProtobufSimpleAuthorizer.java | 42 ++++
.../protobuf/ProtobufStreamProcessor.java | 13 +-
.../registry/OperationContextRegistry.java | 39 +++-
.../protocol/AuthorizationIntegrationTest.java | 205 +++++++++++++++++++
.../protobuf/ProtobufStreamProcessorTest.java | 4 +-
15 files changed, 377 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientProtocolMessageHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientProtocolMessageHandler.java b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientProtocolMessageHandler.java
index 32e9e4b..3d85d45 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientProtocolMessageHandler.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/ClientProtocolMessageHandler.java
@@ -16,6 +16,7 @@
package org.apache.geode.internal.cache.tier.sockets;
import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.security.StreamAuthorizer;
import java.io.IOException;
import java.io.InputStream;
@@ -30,6 +31,7 @@ import java.io.OutputStream;
* {@link GenericProtocolServerConnection}.
*/
public interface ClientProtocolMessageHandler {
- void receiveMessage(InputStream inputStream, OutputStream outputStream, InternalCache cache)
- throws IOException;
+ // TODO: refactor this to use execution context
+ public void receiveMessage(InputStream inputStream, OutputStream outputStream,
+ InternalCache cache, StreamAuthorizer authorizer) throws IOException;
}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnection.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnection.java b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnection.java
index 93a7f6f..c65b2a4 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnection.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnection.java
@@ -61,7 +61,8 @@ public class GenericProtocolServerConnection extends ServerConnection {
if (!authenticator.isAuthenticated()) {
authenticator.receiveMessage(inputStream, outputStream, securityManager);
} else {
- messageHandler.receiveMessage(inputStream, outputStream, this.getCache());
+ messageHandler.receiveMessage(inputStream, outputStream, this.getCache(),
+ authenticator.getAuthorizer());
}
} catch (IOException e) {
logger.warn(e);
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthenticator.java b/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthenticator.java
index 0a6dde1..62f49fe 100644
--- a/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthenticator.java
+++ b/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthenticator.java
@@ -23,8 +23,6 @@ import java.io.OutputStream;
* returns true.
*/
public class NoOpStreamAuthenticator implements StreamAuthenticator {
-
-
@Override
public void receiveMessage(InputStream inputStream, OutputStream outputStream,
SecurityManager securityManager) throws IOException {
@@ -37,6 +35,11 @@ public class NoOpStreamAuthenticator implements StreamAuthenticator {
}
@Override
+ public StreamAuthorizer getAuthorizer() {
+ return new NoOpStreamAuthorizer();
+ }
+
+ @Override
public String implementationID() {
return "NOOP";
}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthorizer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthorizer.java b/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthorizer.java
new file mode 100644
index 0000000..1b21576
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/security/NoOpStreamAuthorizer.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * An implementation of {@link StreamAuthorizer} that doesn't use its parameters and always returns
+ * true.
+ */
+public class NoOpStreamAuthorizer implements StreamAuthorizer {
+ @Override
+ public boolean authorize(ResourcePermission permissionRequested) {
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/security/StreamAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/security/StreamAuthenticator.java b/geode-core/src/main/java/org/apache/geode/security/StreamAuthenticator.java
index 7db1a2b..5cbc4f8 100644
--- a/geode-core/src/main/java/org/apache/geode/security/StreamAuthenticator.java
+++ b/geode-core/src/main/java/org/apache/geode/security/StreamAuthenticator.java
@@ -14,8 +14,6 @@
*/
package org.apache.geode.security;
-import org.apache.geode.security.SecurityManager;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -46,6 +44,14 @@ public interface StreamAuthenticator {
boolean isAuthenticated();
/**
+ * Return an authorization object which can be used to determine which permissions this stream has
+ * according to the provided securityManager.
+ *
+ * Calling this before authentication has succeeded may result in a null return object.
+ */
+ StreamAuthorizer getAuthorizer();
+
+ /**
* @return a unique identifier for this particular implementation (NOOP, PASSTHROUGH, etc.)
*/
String implementationID();
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/main/java/org/apache/geode/security/StreamAuthorizer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/security/StreamAuthorizer.java b/geode-core/src/main/java/org/apache/geode/security/StreamAuthorizer.java
new file mode 100644
index 0000000..c6cef33
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/security/StreamAuthorizer.java
@@ -0,0 +1,8 @@
+package org.apache.geode.security;
+
+/**
+ * Created by bschuchardt on 8/17/17.
+ */
+public interface StreamAuthorizer {
+ boolean authorize(ResourcePermission permissionRequested);
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnectionTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnectionTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnectionTest.java
index 383fbf0..6817b13 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnectionTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/tier/sockets/GenericProtocolServerConnectionTest.java
@@ -53,7 +53,7 @@ public class GenericProtocolServerConnectionTest {
when(socketMock.getInetAddress()).thenReturn(InetAddress.getByName("localhost"));
ClientProtocolMessageHandler clientProtocolMock = mock(ClientProtocolMessageHandler.class);
- doThrow(new IOException()).when(clientProtocolMock).receiveMessage(any(), any(), any());
+ doThrow(new IOException()).when(clientProtocolMock).receiveMessage(any(), any(), any(), any());
return new GenericProtocolServerConnection(socketMock, mock(InternalCache.class),
mock(CachedRegionHelper.class), mock(CacheServerStats.class), 0, 0, "",
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/OperationContext.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/OperationContext.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/OperationContext.java
index 5191007..488dfc4 100644
--- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/OperationContext.java
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/OperationContext.java
@@ -19,6 +19,7 @@ import java.util.function.Function;
import org.apache.geode.annotations.Experimental;
import org.apache.geode.protocol.operations.OperationHandler;
+import org.apache.geode.security.ResourcePermission;
@Experimental
public class OperationContext<OperationRequest, OperationResponse> {
@@ -26,14 +27,17 @@ public class OperationContext<OperationRequest, OperationResponse> {
private final Function<ClientProtocol.Request, OperationRequest> fromRequest;
private final Function<OperationResponse, ClientProtocol.Response.Builder> toResponse;
private final Function<BasicTypes.ErrorResponse, ClientProtocol.Response.Builder> toErrorResponse;
+ private final ResourcePermission accessPermissionRequired;
public OperationContext(Function<ClientProtocol.Request, OperationRequest> fromRequest,
OperationHandler<OperationRequest, OperationResponse> operationHandler,
- Function<OperationResponse, ClientProtocol.Response.Builder> toResponse) {
+ Function<OperationResponse, ClientProtocol.Response.Builder> toResponse,
+ ResourcePermission permissionRequired) {
this.operationHandler = operationHandler;
this.fromRequest = fromRequest;
this.toResponse = toResponse;
this.toErrorResponse = OperationContext::makeErrorBuilder;
+ accessPermissionRequired = permissionRequired;
}
public static ClientProtocol.Response.Builder makeErrorBuilder(
@@ -56,4 +60,8 @@ public class OperationContext<OperationRequest, OperationResponse> {
public Function<BasicTypes.ErrorResponse, ClientProtocol.Response.Builder> getToErrorResponse() {
return toErrorResponse;
}
+
+ public ResourcePermission getAccessPermissionRequired() {
+ return accessPermissionRequired;
+ }
}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufOpsProcessor.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufOpsProcessor.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufOpsProcessor.java
index 7d75b4a..4b0ad71 100644
--- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufOpsProcessor.java
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufOpsProcessor.java
@@ -17,6 +17,8 @@ package org.apache.geode.protocol.protobuf;
import org.apache.geode.annotations.Experimental;
import org.apache.geode.cache.Cache;
import org.apache.geode.protocol.protobuf.registry.OperationContextRegistry;
+import org.apache.geode.protocol.protobuf.utilities.ProtobufResponseUtilities;
+import org.apache.geode.security.StreamAuthorizer;
import org.apache.geode.serialization.SerializationService;
/**
@@ -35,12 +37,21 @@ public class ProtobufOpsProcessor {
this.operationContextRegistry = operationContextRegistry;
}
- public ClientProtocol.Response process(ClientProtocol.Request request, Cache cache) {
+ // TODO: refactor this to use an execution context
+ public ClientProtocol.Response process(ClientProtocol.Request request, Cache cache,
+ StreamAuthorizer authorizer) {
ClientProtocol.Request.RequestAPICase requestType = request.getRequestAPICase();
OperationContext operationContext = operationContextRegistry.getOperationContext(requestType);
ClientProtocol.Response.Builder builder;
- Result result = operationContext.getOperationHandler().process(serializationService,
- operationContext.getFromRequest().apply(request), cache);
+ Result result;
+ if (authorizer.authorize(operationContext.getAccessPermissionRequired())) {
+ result = operationContext.getOperationHandler().process(serializationService,
+ operationContext.getFromRequest().apply(request), cache);
+ } else {
+ result = Failure.of(ProtobufResponseUtilities.makeErrorResponse(
+ ProtocolErrorCode.AUTHORIZATION_FAILED.codeValue,
+ "User isn't authorized for this operation."));
+ }
builder = (ClientProtocol.Response.Builder) result.map(operationContext.getToResponse(),
operationContext.getToErrorResponse());
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthenticator.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthenticator.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthenticator.java
index 1517552..98981f9 100644
--- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthenticator.java
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthenticator.java
@@ -17,6 +17,7 @@ package org.apache.geode.protocol.protobuf;
import org.apache.geode.security.StreamAuthenticator;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.SecurityManager;
+import org.apache.geode.security.StreamAuthorizer;
import java.io.EOFException;
import java.io.IOException;
@@ -26,6 +27,7 @@ import java.util.Properties;
public class ProtobufSimpleAuthenticator implements StreamAuthenticator {
private boolean authenticated;
+ private ProtobufSimpleAuthorizer authorizer = null;
@Override
public void receiveMessage(InputStream inputStream, OutputStream outputStream,
@@ -40,20 +42,28 @@ public class ProtobufSimpleAuthenticator implements StreamAuthenticator {
properties.setProperty("username", authenticationRequest.getUsername());
properties.setProperty("password", authenticationRequest.getPassword());
+ authorizer = null; // authenticating a new user clears current authorizer
try {
Object principal = securityManager.authenticate(properties);
- authenticated = principal != null;
+ if (principal != null) {
+ authorizer = new ProtobufSimpleAuthorizer(principal, securityManager);
+ }
} catch (AuthenticationFailedException e) {
- authenticated = false;
+ authorizer = null;
}
- AuthenticationAPI.SimpleAuthenticationResponse.newBuilder().setAuthenticated(authenticated)
+ AuthenticationAPI.SimpleAuthenticationResponse.newBuilder().setAuthenticated(isAuthenticated())
.build().writeDelimitedTo(outputStream);
}
@Override
public boolean isAuthenticated() {
- return authenticated;
+ return authorizer != null;
+ }
+
+ @Override
+ public StreamAuthorizer getAuthorizer() {
+ return authorizer;
}
@Override
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthorizer.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthorizer.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthorizer.java
new file mode 100644
index 0000000..872632a
--- /dev/null
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufSimpleAuthorizer.java
@@ -0,0 +1,42 @@
+/*
+ * 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.protocol.protobuf;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import org.apache.geode.security.AuthenticationFailedException;
+import org.apache.geode.security.ResourcePermission;
+import org.apache.geode.security.SecurityManager;
+import org.apache.geode.security.StreamAuthenticator;
+import org.apache.geode.security.StreamAuthorizer;
+
+public class ProtobufSimpleAuthorizer implements StreamAuthorizer {
+ private final Object authenticatedPrincipal;
+ private final SecurityManager securityManager;
+
+ public ProtobufSimpleAuthorizer(Object authenticatedPrincipal, SecurityManager securityManager) {
+ this.authenticatedPrincipal = authenticatedPrincipal;
+ this.securityManager = securityManager;
+ }
+
+ @Override
+ public boolean authorize(ResourcePermission permissionRequested) {
+ return securityManager.authorize(authenticatedPrincipal, permissionRequested);
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java
index 648ab3c..a5ba6c5 100644
--- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessor.java
@@ -27,6 +27,7 @@ import org.apache.geode.protocol.exception.InvalidProtocolMessageException;
import org.apache.geode.protocol.protobuf.registry.OperationContextRegistry;
import org.apache.geode.protocol.protobuf.serializer.ProtobufProtocolSerializer;
import org.apache.geode.protocol.protobuf.utilities.ProtobufUtilities;
+import org.apache.geode.security.StreamAuthorizer;
import org.apache.geode.serialization.registry.exception.CodecAlreadyRegisteredForTypeException;
/**
@@ -45,15 +46,16 @@ public class ProtobufStreamProcessor implements ClientProtocolMessageHandler {
new OperationContextRegistry());
}
- public void processOneMessage(InputStream inputStream, OutputStream outputStream, Cache cache)
- throws InvalidProtocolMessageException, IOException {
+ // TODO: refactor this to use execution context
+ public void processOneMessage(InputStream inputStream, OutputStream outputStream, Cache cache,
+ StreamAuthorizer authorizer) throws InvalidProtocolMessageException, IOException {
ClientProtocol.Message message = protobufProtocolSerializer.deserialize(inputStream);
if (message == null) {
throw new EOFException("Tried to deserialize protobuf message at EOF");
}
ClientProtocol.Request request = message.getRequest();
- ClientProtocol.Response response = protobufOpsProcessor.process(request, cache);
+ ClientProtocol.Response response = protobufOpsProcessor.process(request, cache, authorizer);
ClientProtocol.MessageHeader responseHeader =
ProtobufUtilities.createMessageHeaderForRequest(message);
ClientProtocol.Message responseMessage =
@@ -61,11 +63,12 @@ public class ProtobufStreamProcessor implements ClientProtocolMessageHandler {
protobufProtocolSerializer.serialize(responseMessage, outputStream);
}
+ // TODO: refactor this to use execution context
@Override
public void receiveMessage(InputStream inputStream, OutputStream outputStream,
- InternalCache cache) throws IOException {
+ InternalCache cache, StreamAuthorizer authorizer) throws IOException {
try {
- processOneMessage(inputStream, outputStream, cache);
+ processOneMessage(inputStream, outputStream, cache, authorizer);
} catch (InvalidProtocolMessageException e) {
throw new IOException(e);
}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/registry/OperationContextRegistry.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/registry/OperationContextRegistry.java b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/registry/OperationContextRegistry.java
index dfe975c..6b541eb 100644
--- a/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/registry/OperationContextRegistry.java
+++ b/geode-protobuf/src/main/java/org/apache/geode/protocol/protobuf/registry/OperationContextRegistry.java
@@ -30,6 +30,7 @@ import org.apache.geode.protocol.protobuf.operations.GetRequestOperationHandler;
import org.apache.geode.protocol.protobuf.operations.PutAllRequestOperationHandler;
import org.apache.geode.protocol.protobuf.operations.PutRequestOperationHandler;
import org.apache.geode.protocol.protobuf.operations.RemoveRequestOperationHandler;
+import org.apache.geode.security.ResourcePermission;
@Experimental
public class OperationContextRegistry {
@@ -47,41 +48,57 @@ public class OperationContextRegistry {
operationContexts.put(RequestAPICase.GETREQUEST,
new OperationContext<>(ClientProtocol.Request::getGetRequest,
new GetRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setGetResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setGetResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.READ)));
operationContexts.put(RequestAPICase.GETALLREQUEST,
new OperationContext<>(ClientProtocol.Request::getGetAllRequest,
new GetAllRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setGetAllResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setGetAllResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.READ)));
operationContexts.put(RequestAPICase.PUTREQUEST,
new OperationContext<>(ClientProtocol.Request::getPutRequest,
new PutRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setPutResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setPutResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.WRITE)));
operationContexts.put(RequestAPICase.PUTALLREQUEST,
new OperationContext<>(ClientProtocol.Request::getPutAllRequest,
new PutAllRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setPutAllResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setPutAllResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.WRITE)));
operationContexts.put(RequestAPICase.REMOVEREQUEST,
new OperationContext<>(ClientProtocol.Request::getRemoveRequest,
new RemoveRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setRemoveResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setRemoveResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.WRITE)));
operationContexts.put(RequestAPICase.GETREGIONNAMESREQUEST,
new OperationContext<>(ClientProtocol.Request::getGetRegionNamesRequest,
new GetRegionNamesRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setGetRegionNamesResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setGetRegionNamesResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.READ)));
operationContexts.put(RequestAPICase.GETREGIONREQUEST,
new OperationContext<>(ClientProtocol.Request::getGetRegionRequest,
new GetRegionRequestOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setGetRegionResponse(opsResp)));
+ opsResp -> ClientProtocol.Response.newBuilder().setGetRegionResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.DATA,
+ ResourcePermission.Operation.READ)));
- operationContexts.put(RequestAPICase.GETAVAILABLESERVERSREQUEST, new OperationContext<>(
- ClientProtocol.Request::getGetAvailableServersRequest,
- new GetAvailableServersOperationHandler(),
- opsResp -> ClientProtocol.Response.newBuilder().setGetAvailableServersResponse(opsResp)));
+ operationContexts.put(RequestAPICase.GETAVAILABLESERVERSREQUEST,
+ new OperationContext<>(ClientProtocol.Request::getGetAvailableServersRequest,
+ new GetAvailableServersOperationHandler(),
+ opsResp -> ClientProtocol.Response.newBuilder().setGetAvailableServersResponse(opsResp),
+ new ResourcePermission(ResourcePermission.Resource.CLUSTER,
+ ResourcePermission.Operation.READ)));
}
}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/test/java/org/apache/geode/protocol/AuthorizationIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/test/java/org/apache/geode/protocol/AuthorizationIntegrationTest.java b/geode-protobuf/src/test/java/org/apache/geode/protocol/AuthorizationIntegrationTest.java
new file mode 100644
index 0000000..9df23d6
--- /dev/null
+++ b/geode-protobuf/src/test/java/org/apache/geode/protocol/AuthorizationIntegrationTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.protocol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.protocol.protobuf.AuthenticationAPI;
+import org.apache.geode.protocol.protobuf.ClientProtocol;
+import org.apache.geode.protocol.protobuf.ProtobufSerializationService;
+import org.apache.geode.protocol.protobuf.ProtocolErrorCode;
+import org.apache.geode.protocol.protobuf.RegionAPI;
+import org.apache.geode.protocol.protobuf.serializer.ProtobufProtocolSerializer;
+import org.apache.geode.protocol.protobuf.utilities.ProtobufUtilities;
+import org.apache.geode.security.ResourcePermission;
+import org.apache.geode.security.SecurityManager;
+import org.apache.geode.serialization.registry.exception.CodecAlreadyRegisteredForTypeException;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+public class AuthorizationIntegrationTest {
+
+ private static final String TEST_USERNAME = "bob";
+ private static final String TEST_PASSWORD = "bobspassword";
+ public static final String TEST_REGION = "testRegion";
+
+ @Rule
+ public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+ private Cache cache;
+ private int cacheServerPort;
+ private CacheServer cacheServer;
+ private Socket socket;
+ private OutputStream outputStream;
+ private ProtobufSerializationService serializationService;
+ private InputStream inputStream;
+ private ProtobufProtocolSerializer protobufProtocolSerializer;
+ private Object securityPrincipal;
+ private SecurityManager mockSecurityManager;
+ private String testRegion;
+ public static final ResourcePermission READ_PERMISSION =
+ new ResourcePermission(ResourcePermission.Resource.DATA, ResourcePermission.Operation.READ);
+ public static final ResourcePermission WRITE_PERMISSION =
+ new ResourcePermission(ResourcePermission.Resource.DATA, ResourcePermission.Operation.WRITE);
+
+ @Before
+ public void setUp() throws IOException, CodecAlreadyRegisteredForTypeException {
+ Properties expectedAuthProperties = new Properties();
+ expectedAuthProperties.setProperty("username", TEST_USERNAME);
+ expectedAuthProperties.setProperty("password", TEST_PASSWORD);
+
+ securityPrincipal = new Object();
+ mockSecurityManager = mock(SecurityManager.class);
+ when(mockSecurityManager.authenticate(expectedAuthProperties)).thenReturn(securityPrincipal);
+
+ Properties properties = new Properties();
+ CacheFactory cacheFactory = new CacheFactory(properties);
+ cacheFactory.set("mcast-port", "0"); // sometimes it isn't due to other tests.
+
+ cacheFactory.setSecurityManager(mockSecurityManager);
+ cache = cacheFactory.create();
+
+ cacheServer = cache.addCacheServer();
+ cacheServerPort = AvailablePortHelper.getRandomAvailableTCPPort();
+ cacheServer.setPort(cacheServerPort);
+ cacheServer.start();
+
+ cache.createRegionFactory().create(TEST_REGION);
+
+ System.setProperty("geode.feature-protobuf-protocol", "true");
+ System.setProperty("geode.protocol-authentication-mode", "SIMPLE");
+ socket = new Socket("localhost", cacheServerPort);
+
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(socket::isConnected);
+ outputStream = socket.getOutputStream();
+ inputStream = socket.getInputStream();
+ outputStream.write(110);
+
+ serializationService = new ProtobufSerializationService();
+ protobufProtocolSerializer = new ProtobufProtocolSerializer();
+
+ when(mockSecurityManager.authorize(same(securityPrincipal), any())).thenReturn(false);
+ AuthenticationAPI.SimpleAuthenticationRequest authenticationRequest =
+ AuthenticationAPI.SimpleAuthenticationRequest.newBuilder().setUsername(TEST_USERNAME)
+ .setPassword(TEST_PASSWORD).build();
+ authenticationRequest.writeDelimitedTo(outputStream);
+
+ AuthenticationAPI.SimpleAuthenticationResponse authenticationResponse =
+ AuthenticationAPI.SimpleAuthenticationResponse.parseDelimitedFrom(inputStream);
+ assertTrue(authenticationResponse.getAuthenticated());
+ }
+
+ @After
+ public void shutDown() throws IOException {
+ cache.close();
+ socket.close();
+ }
+
+
+ @Test
+ public void validateNoPermissions() throws Exception {
+ when(mockSecurityManager.authorize(securityPrincipal, READ_PERMISSION)).thenReturn(false);
+ when(mockSecurityManager.authorize(securityPrincipal, WRITE_PERMISSION)).thenReturn(false);
+
+ verifyOperations(false, false);
+ }
+
+ @Test
+ public void validateWritePermission() throws Exception {
+ when(mockSecurityManager.authorize(securityPrincipal, READ_PERMISSION)).thenReturn(false);
+ when(mockSecurityManager.authorize(securityPrincipal, WRITE_PERMISSION)).thenReturn(true);
+
+ verifyOperations(false, true);
+ }
+
+ @Test
+ public void validateReadPermission() throws Exception {
+ when(mockSecurityManager.authorize(securityPrincipal, READ_PERMISSION)).thenReturn(true);
+ when(mockSecurityManager.authorize(securityPrincipal, WRITE_PERMISSION)).thenReturn(false);
+
+ verifyOperations(true, false);
+ }
+
+ @Test
+ public void validateReadAndWritePermission() throws Exception {
+ when(mockSecurityManager.authorize(securityPrincipal, READ_PERMISSION)).thenReturn(true);
+ when(mockSecurityManager.authorize(securityPrincipal, WRITE_PERMISSION)).thenReturn(true);
+
+ verifyOperations(true, true);
+ }
+
+ private void verifyOperations(boolean readAllowed, boolean writeAllowed) throws Exception {
+ ClientProtocol.Message getRegionsMessage =
+ ClientProtocol.Message.newBuilder().setRequest(ClientProtocol.Request.newBuilder()
+ .setGetRegionNamesRequest(RegionAPI.GetRegionNamesRequest.newBuilder())).build();
+ validateOperationAuthorized(getRegionsMessage, inputStream, outputStream,
+ readAllowed ? ClientProtocol.Response.ResponseAPICase.GETREGIONNAMESRESPONSE
+ : ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE);
+
+ ClientProtocol.Message putMessage = ClientProtocol.Message.newBuilder()
+ .setRequest(ClientProtocol.Request.newBuilder()
+ .setPutRequest(RegionAPI.PutRequest.newBuilder().setRegionName(TEST_REGION).setEntry(
+ ProtobufUtilities.createEntry(serializationService, "TEST_KEY", "TEST_VALUE"))))
+ .build();
+ validateOperationAuthorized(putMessage, inputStream, outputStream,
+ writeAllowed ? ClientProtocol.Response.ResponseAPICase.PUTRESPONSE
+ : ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE);
+
+ ClientProtocol.Message removeMessage = ClientProtocol.Message.newBuilder()
+ .setRequest(ClientProtocol.Request.newBuilder()
+ .setRemoveRequest(RegionAPI.RemoveRequest.newBuilder().setRegionName(TEST_REGION)
+ .setKey(ProtobufUtilities.createEncodedValue(serializationService, "TEST_KEY"))))
+ .build();
+ validateOperationAuthorized(removeMessage, inputStream, outputStream,
+ writeAllowed ? ClientProtocol.Response.ResponseAPICase.REMOVERESPONSE
+ : ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE);
+ }
+
+ private void validateOperationAuthorized(ClientProtocol.Message message, InputStream inputStream,
+ OutputStream outputStream, ClientProtocol.Response.ResponseAPICase expectedResponseType)
+ throws Exception {
+ protobufProtocolSerializer.serialize(message, outputStream);
+ ClientProtocol.Message response = protobufProtocolSerializer.deserialize(inputStream);
+ assertEquals(expectedResponseType, response.getResponse().getResponseAPICase());
+ if (expectedResponseType == ClientProtocol.Response.ResponseAPICase.ERRORRESPONSE) {
+ assertEquals(ProtocolErrorCode.AUTHORIZATION_FAILED.codeValue,
+ response.getResponse().getErrorResponse().getError().getErrorCode());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/7d6a0a3e/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessorTest.java
----------------------------------------------------------------------
diff --git a/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessorTest.java b/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessorTest.java
index 87bfd52..2ffdf5f 100644
--- a/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessorTest.java
+++ b/geode-protobuf/src/test/java/org/apache/geode/protocol/protobuf/ProtobufStreamProcessorTest.java
@@ -26,6 +26,7 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.security.StreamAuthorizer;
import org.apache.geode.test.junit.categories.UnitTest;
@Category(UnitTest.class)
@@ -37,6 +38,7 @@ public class ProtobufStreamProcessorTest {
ProtobufStreamProcessor protobufStreamProcessor = new ProtobufStreamProcessor();
InternalCache mockInternalCache = mock(InternalCache.class);
- protobufStreamProcessor.receiveMessage(inputStream, outputStream, mockInternalCache);
+ protobufStreamProcessor.receiveMessage(inputStream, outputStream, mockInternalCache,
+ mock(StreamAuthorizer.class));
}
}