From 39cb42ddde538a33bd7b3b3a0b27428aae2a2276 Mon Sep 17 00:00:00 2001 From: Dylan Millikin Date: Wed, 30 Sep 2015 10:52:56 +0200 Subject: [PATCH] Made correction to fix TINKERPOP3-855. Added Test and changed documentation. --- docs/src/gremlin-applications.asciidoc | 2 +- .../handler/SaslAuthenticationHandler.java | 18 +++++++++++++++++- .../server/GremlinServerAuthIntegrateTest.java | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/src/gremlin-applications.asciidoc b/docs/src/gremlin-applications.asciidoc index 88a1c0d41ef..2388f9d7235 100644 --- a/docs/src/gremlin-applications.asciidoc +++ b/docs/src/gremlin-applications.asciidoc @@ -1029,7 +1029,7 @@ Authentication Gremlin Server supports link:https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer[SASL-based] authentication. A SASL implementation provides a series of challenges and responses that a driver must comply with in order to authenticate. By default, Gremlin Server only supports the "PLAIN" SASL mechanism, which is a cleartext password system. When authentication is enabled, an incoming request is intercepted before it is evaluated by the `ScriptEngine`. The request is saved on the server and a `AUTHENTICATE` challenge response (status code `407`) is returned to the client. -The client will detect the `AUTHENTICATE` and respond with an `authentication` for the `op` and an `arg` named `sasl` that contains the password. The password should be an encoded sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL), where the form is : `usernamepassword`. Should Gremlin Server be able to authenticate with the provided credentials, the server will return the results of the original request as it normally does without authentication. If it cannot authenticate given the challenge response from the client, it will return `UNAUTHORIZED` (status code `401`). +The client will detect the `AUTHENTICATE` and respond with an `authentication` for the `op` and an `arg` named `sasl` that contains the password. The password should be either, an encoded sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL), where the form is : `usernamepassword`, or a Base64 encoded string of the former (which in this instance would be `AHVzZXJuYW1lAHBhc3N3b3Jk`). Should Gremlin Server be able to authenticate with the provided credentials, the server will return the results of the original request as it normally does without authentication. If it cannot authenticate given the challenge response from the client, it will return `UNAUTHORIZED` (status code `401`). NOTE: Gremlin Server does not support the "authorization identity" as described in link:https://tools.ietf.org/html/rfc4616[RFC4616]. 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 813cd6c0067..6dee0e855ab 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 @@ -23,6 +23,7 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.Attribute; import io.netty.util.AttributeKey; +import java.util.Base64; import org.apache.tinkerpop.gremlin.driver.Tokens; import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; @@ -71,7 +72,22 @@ public void channelRead(final ChannelHandlerContext ctx, final Object msg) throw ctx.writeAndFlush(authenticate); } else { if (requestMessage.getOp().equals(Tokens.OPS_AUTHENTICATION) && requestMessage.getArgs().containsKey(Tokens.ARGS_SASL)) { - final byte[] saslResponse = (byte[]) requestMessage.getArgs().get(Tokens.ARGS_SASL); + + final Object saslObject = requestMessage.getArgs().get(Tokens.ARGS_SASL); + final byte[] saslResponse; + + if (saslObject instanceof byte[]) { + saslResponse = (byte[]) saslObject; + } else if(saslObject instanceof String) { + saslResponse = Base64.getDecoder().decode((String) saslObject); + } else { + final ResponseMessage error = ResponseMessage.build(request.get()) + .statusMessage("Incorrect type for : " + Tokens.ARGS_SASL + ". byte[] or String is expected") + .code(ResponseStatusCode.REQUEST_ERROR_MALFORMED_REQUEST).create(); + ctx.writeAndFlush(error); + return; + } + try { final byte[] saslMessage = negotiator.get().evaluateResponse(saslResponse); if (negotiator.get().isComplete()) { diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java index f9d845d31e1..f0ff50caadf 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerAuthIntegrateTest.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; import static org.junit.Assert.assertEquals; @@ -141,4 +142,18 @@ public void shouldFailAuthenticateWithPlainTextBadUsername() throws Exception { cluster.close(); } } + + @Test + public void shouldAuthenticateWithPlainTextOverJSONSerialization() throws Exception { + final Cluster cluster = Cluster.build().serializer(Serializers.GRAPHSON).credentials("stephen", "password").create(); + final Client client = cluster.connect(); + + try { + assertEquals(2, client.submit("1+1").all().get().get(0).getInt()); + assertEquals(3, client.submit("1+2").all().get().get(0).getInt()); + assertEquals(4, client.submit("1+3").all().get().get(0).getInt()); + } finally { + cluster.close(); + } + } }