TINKERPOP-1600 Added base64 encoded string to sasl challenge

Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/e80a4cd1
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/e80a4cd1
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/e80a4cd1

Branch: refs/heads/TINKERPOP-1602
Commit: e80a4cd172746dc393711cc095e229a4d5e78629
Parents: 802ff9f
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Jan 16 15:22:07 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Jan 18 06:56:11 2017 -0500

----------------------------------------------------------------------
 CHANGELOG.asciidoc                                  |  2 +-
 docs/src/dev/io/graphson.asciidoc                   |  5 ++---
 docs/src/upgrade/release-3.2.x-incubating.asciidoc  | 16 ++++++++++++++++
 .../apache/tinkerpop/gremlin/driver/Handler.java    | 15 +++++++++++++--
 .../server/handler/SaslAuthenticationHandler.java   | 14 ++++++++++++--
 5 files changed, 44 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e80a4cd1/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9471b9f..4f3f9ce 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* SASL negotiation supports both a byte array and Base64 encoded bytes as a 
string for authentication to Gremlin Server.
 * Deprecated `TinkerIoRegistry` replacing it with the more consistently named 
`TinkerIoRegistryV1d0`.
 * Made error messaging more consistent during result iteration timeouts in 
Gremlin Server.
 * `PathRetractionStrategy` does not add a `NoOpBarrierStep` to the end of 
local children as its wasted computation in 99% of traversals.
@@ -580,7 +581,6 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.1.6 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* Bumped to Groovy 2.4.8.
 * Fixed bug in `IncidentToAdjacentStrategy`, it was missing some invalidating 
steps.
 * Returned a confirmation on session close from Gremlin Server.
 * Use non-default port for running tests on Gremlin Server.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e80a4cd1/docs/src/dev/io/graphson.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/dev/io/graphson.asciidoc 
b/docs/src/dev/io/graphson.asciidoc
index cdc9880..fe56c27 100644
--- a/docs/src/dev/io/graphson.asciidoc
+++ b/docs/src/dev/io/graphson.asciidoc
@@ -1299,7 +1299,7 @@ ResponseMessage
 Authentication Challenge
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-When authentication is enabled, an initial request to the server will result 
in an authentication challenge. The typical response message will appear as 
follows, but handling it could be different dependending on the SASL 
implementation (e.g. multiple challenges maybe requested in some cases, but no 
in the default provided by Gremlin Server).
+When authentication is enabled, an initial request to the server will result 
in an authentication challenge. The typical response message will appear as 
follows, but handling it could be different dependending on the SASL 
implementation (e.g. multiple challenges maybe requested in some cases, but not 
in the default provided by Gremlin Server).
 
 [source,json]
 ----
@@ -4262,14 +4262,13 @@ The following `RequestMessage` is an example of a 
sessionless request for a scri
 }
 ----
 
-
 ResponseMessage
 ~~~~~~~~~~~~~~~
 
 Authentication Challenge
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-When authentication is enabled, an initial request to the server will result 
in an authentication challenge. The typical response message will appear as 
follows, but handling it could be different dependending on the SASL 
implementation (e.g. multiple challenges maybe requested in some cases, but no 
in the default provided by Gremlin Server).
+When authentication is enabled, an initial request to the server will result 
in an authentication challenge. The typical response message will appear as 
follows, but handling it could be different dependending on the SASL 
implementation (e.g. multiple challenges maybe requested in some cases, but not 
in the default provided by Gremlin Server).
 
 [source,json]
 ----

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e80a4cd1/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc 
b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 8d73baed..b478b96 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -240,6 +240,22 @@ and defaults to `false`.
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-932[TINKERPOP-932],
 
link:http://tinkerpop.apache.org/docs/current/dev/provider/#_session_opprocessor[Provider
 Documentation - Session OpProcessor]
 
+SASL Authentication
++++++++++++++++++++
+
+Gremlin Supports SASL based authentication. The server accepts either a byte 
array or Base64 encoded String as the in
+the `sasl` argument on the `RequestMessage`, however it sends back a byte 
array only. Some serializers or serializer
+configurations don't work well with that approach (specifically the "toString" 
configuration on the Gryo serializer) as
+the byte array is returned in the `ResponseMessage` result. In the case of the 
"toString" serializer the byte array
+gets "toString'd" and the can't be read by the client.
+
+In 3.2.4, the byte array is still returned in the `ResponseMessage` result, 
but is also returned in the status
+attributes under a `sasl` key as a Base64 encoded string. In this way, the 
client has options on how it chooses to
+process the authentication response and the change remains backward 
compatible. Drivers should upgrade to using the
+Base64 encoded string however as the old approach will likely be removed in 
the future.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1600[TINKERPOP-1600]
+
 TinkerPop 3.2.3
 ---------------
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e80a4cd1/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
----------------------------------------------------------------------
diff --git 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
index 681354c..650bc2f 100644
--- 
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
+++ 
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Handler.java
@@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory;
 import java.net.InetSocketAddress;
 import java.security.PrivilegedExceptionAction;
 import java.security.PrivilegedActionException;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -70,6 +71,9 @@ final class Handler {
         private static final Map<String, String> SASL_PROPERTIES = new 
HashMap<String, String>() {{ put(Sasl.SERVER_AUTH, "true"); }};
         private static final byte[] NULL_CHALLENGE = new byte[0];
 
+        private static final Base64.Encoder BASE64_ENCODER = 
Base64.getEncoder();
+        private static final Base64.Decoder BASE64_DECODER = 
Base64.getDecoder();
+
         private final AuthProperties authProps;
 
         public GremlinSaslAuthenticationHandler(final AuthProperties 
authProps) {
@@ -103,9 +107,16 @@ final class Handler {
 
                     messageBuilder.addArg(Tokens.ARGS_SASL_MECHANISM, 
getMechanism());
                     messageBuilder.addArg(Tokens.ARGS_SASL, 
saslClient.get().hasInitialResponse() ?
-                                                                
evaluateChallenge(subject, saslClient, NULL_CHALLENGE) : null);
+                            
BASE64_ENCODER.encodeToString(evaluateChallenge(subject, saslClient, 
NULL_CHALLENGE)) : null);
                 } else {
-                    messageBuilder.addArg(Tokens.ARGS_SASL, 
evaluateChallenge(subject, saslClient, (byte[])response.getResult().getData()));
+                    // the server sends base64 encoded sasl as well as the 
byte array. the byte array will eventually be
+                    // phased out, but is present now for backward 
compatibility in 3.2.x
+                    final String base64sasl = 
response.getStatus().getAttributes().containsKey(Tokens.ARGS_SASL) ?
+                        
response.getStatus().getAttributes().get(Tokens.ARGS_SASL).toString() :
+                        BASE64_ENCODER.encodeToString((byte[]) 
response.getResult().getData());
+
+                    messageBuilder.addArg(Tokens.ARGS_SASL, 
BASE64_ENCODER.encodeToString(
+                        evaluateChallenge(subject, saslClient, 
BASE64_DECODER.decode(base64sasl))));
                 }
                 channelHandlerContext.writeAndFlush(messageBuilder.create());
             } else {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e80a4cd1/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
----------------------------------------------------------------------
diff --git 
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
 
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
index 4bb7879..6cb0ddb 100644
--- 
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
+++ 
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/SaslAuthenticationHandler.java
@@ -28,6 +28,9 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -50,6 +53,8 @@ import org.slf4j.LoggerFactory;
 @ChannelHandler.Sharable
 public class SaslAuthenticationHandler extends ChannelInboundHandlerAdapter {
     private static final Logger logger = 
LoggerFactory.getLogger(SaslAuthenticationHandler.class);
+    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
+    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
 
     private final Authenticator authenticator;
 
@@ -80,7 +85,7 @@ public class SaslAuthenticationHandler extends 
ChannelInboundHandlerAdapter {
                     if (saslObject instanceof byte[]) {
                         saslResponse = (byte[]) saslObject;
                     } else if(saslObject instanceof String) {
-                        saslResponse = Base64.getDecoder().decode((String) 
saslObject);
+                        saslResponse = BASE64_DECODER.decode((String) 
saslObject);
                     } else {
                         final ResponseMessage error = 
ResponseMessage.build(request.get())
                                 .statusMessage("Incorrect type for : " + 
Tokens.ARGS_SASL + " - byte[] or base64 encoded String is expected")
@@ -101,8 +106,13 @@ public class SaslAuthenticationHandler extends 
ChannelInboundHandlerAdapter {
                             final RequestMessage original = request.get();
                             ctx.fireChannelRead(original);
                         } else {
-                            // not done here - send back the sasl message for 
next challenge
+                            // not done here - send back the sasl message for 
next challenge. note that we send back
+                            // the base64 encoded sasl as well as the byte 
array. the byte array will eventually be
+                            // phased out, but is present now for backward 
compatibility in 3.2.x
+                            final Map<String,Object> metadata = new 
HashMap<>();
+                            metadata.put(Tokens.ARGS_SASL, 
BASE64_ENCODER.encodeToString(saslMessage));
                             final ResponseMessage authenticate = 
ResponseMessage.build(requestMessage)
+                                    .statusAttributes(metadata)
                                     
.code(ResponseStatusCode.AUTHENTICATE).result(saslMessage).create();
                             ctx.writeAndFlush(authenticate);
                         }

Reply via email to