You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2017/07/13 17:48:21 UTC
[33/52] [abbrv] tinkerpop git commit: Add support for authentication
(Plain SASL) to Gremlin.Net
Add support for authentication (Plain SASL) to Gremlin.Net
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/88415ee3
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/88415ee3
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/88415ee3
Branch: refs/heads/TINKERPOP-1552-master
Commit: 88415ee35819d017652dfcf8c1e5fde5004b2816
Parents: e02ddcb
Author: Florian Hockmann <fh...@florian-hockmann.de>
Authored: Mon Jun 12 22:09:24 2017 +0200
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jul 13 13:46:48 2017 -0400
----------------------------------------------------------------------
.../src/Gremlin.Net/Driver/Connection.cs | 35 +++++++-
.../src/Gremlin.Net/Driver/ConnectionFactory.cs | 11 +--
.../src/Gremlin.Net/Driver/GremlinClient.cs | 2 +-
.../src/Gremlin.Net/Driver/GremlinServer.cs | 16 +++-
.../Driver/Messages/ResponseStatusCode.cs | 2 +-
gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs | 11 +++
.../Driver/GremlinClientAuthenticationTests.cs | 86 ++++++++++++++++++++
.../appsettings.json | 3 +-
gremlin-dotnet/test/pom.xml | 17 ++++
9 files changed, 171 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
index 2315ed4..126b461 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
@@ -23,6 +23,7 @@
using System;
using System.Collections.Generic;
+using System.Text;
using System.Threading.Tasks;
using Gremlin.Net.Driver.Messages;
using Gremlin.Net.Driver.ResultsAggregation;
@@ -38,10 +39,15 @@ namespace Gremlin.Net.Driver
private readonly JsonMessageSerializer _messageSerializer = new JsonMessageSerializer();
private readonly Uri _uri;
private readonly WebSocketConnection _webSocketConnection = new WebSocketConnection();
+ private readonly string _username;
+ private readonly string _password;
- public Connection(Uri uri, GraphSONReader graphSONReader, GraphSONWriter graphSONWriter)
+ public Connection(Uri uri, string username, string password, GraphSONReader graphSONReader,
+ GraphSONWriter graphSONWriter)
{
_uri = uri;
+ _username = username;
+ _password = password;
_graphSONReader = graphSONReader;
_graphSONWriter = graphSONWriter;
}
@@ -83,7 +89,11 @@ namespace Gremlin.Net.Driver
status = receivedMsg.Status;
status.ThrowIfStatusIndicatesError();
- if (status.Code != ResponseStatusCode.NoContent)
+ if (status.Code == ResponseStatusCode.Authenticate)
+ {
+ await AuthenticateAsync().ConfigureAwait(false);
+ }
+ else if (status.Code != ResponseStatusCode.NoContent)
{
var receivedData = _graphSONReader.ToObject(receivedMsg.Result.Data);
foreach (var d in receivedData)
@@ -101,13 +111,32 @@ namespace Gremlin.Net.Driver
result.Add(d);
}
}
- } while (status.Code == ResponseStatusCode.PartialContent);
+ } while (status.Code == ResponseStatusCode.PartialContent || status.Code == ResponseStatusCode.Authenticate);
if (isAggregatingSideEffects)
return new List<T> {(T) aggregator.GetAggregatedResult()};
return result;
}
+ private async Task AuthenticateAsync()
+ {
+ if (string.IsNullOrEmpty(_username) || string.IsNullOrEmpty(_password))
+ throw new InvalidOperationException(
+ $"The Gremlin Server requires authentication, but no credentials are specified - username: {_username}, password: {_password}.");
+
+ var message = RequestMessage.Build(Tokens.OpsAuthentication).Processor(Tokens.ProcessorTraversal)
+ .AddArgument(Tokens.ArgsSasl, SaslArgument()).Create();
+
+ await SendAsync(message).ConfigureAwait(false);
+ }
+
+ private string SaslArgument()
+ {
+ var auth = $"\0{_username}\0{_password}";
+ var authBytes = Encoding.UTF8.GetBytes(auth);
+ return Convert.ToBase64String(authBytes);
+ }
+
#region IDisposable Support
private bool _disposed;
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
index d31817c..0041a67 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
@@ -21,7 +21,6 @@
#endregion
-using System;
using Gremlin.Net.Structure.IO.GraphSON;
namespace Gremlin.Net.Driver
@@ -30,18 +29,20 @@ namespace Gremlin.Net.Driver
{
private readonly GraphSONReader _graphSONReader;
private readonly GraphSONWriter _graphSONWriter;
- private readonly Uri _uri;
+ private readonly GremlinServer _gremlinServer;
- public ConnectionFactory(Uri uri, GraphSONReader graphSONReader, GraphSONWriter graphSONWriter)
+ public ConnectionFactory(GremlinServer gremlinServer, GraphSONReader graphSONReader,
+ GraphSONWriter graphSONWriter)
{
- _uri = uri;
+ _gremlinServer = gremlinServer;
_graphSONReader = graphSONReader;
_graphSONWriter = graphSONWriter;
}
public Connection CreateConnection()
{
- return new Connection(_uri, _graphSONReader, _graphSONWriter);
+ return new Connection(_gremlinServer.Uri, _gremlinServer.Username, _gremlinServer.Password, _graphSONReader,
+ _graphSONWriter);
}
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
index 7833088..46dd8a6 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
@@ -47,7 +47,7 @@ namespace Gremlin.Net.Driver
{
var reader = graphSONReader ?? new GraphSONReader();
var writer = graphSONWriter ?? new GraphSONWriter();
- var connectionFactory = new ConnectionFactory(gremlinServer.Uri, reader, writer);
+ var connectionFactory = new ConnectionFactory(gremlinServer, reader, writer);
_connectionPool = new ConnectionPool(connectionFactory);
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinServer.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinServer.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinServer.cs
index 8da6d0b..601bbae 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinServer.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinServer.cs
@@ -36,9 +36,13 @@ namespace Gremlin.Net.Driver
/// <param name="hostname">The hostname of the server.</param>
/// <param name="port">The port on which Gremlin Server can be reached.</param>
/// <param name="enableSsl">Specifies whether SSL should be enabled.</param>
- public GremlinServer(string hostname, int port = 8182, bool enableSsl = false)
+ /// <param name="username">The username to submit on requests that require authentication.</param>
+ /// <param name="password">The password to submit on requests that require authentication.</param>
+ public GremlinServer(string hostname, int port = 8182, bool enableSsl = false, string username = null, string password = null)
{
Uri = CreateUri(hostname, port, enableSsl);
+ Username = username;
+ Password = password;
}
/// <summary>
@@ -47,6 +51,16 @@ namespace Gremlin.Net.Driver
/// <value>The WebSocket <see cref="System.Uri" /> that the Gremlin Server responds to.</value>
public Uri Uri { get; }
+ /// <summary>
+ /// Gets the username to submit on requests that require authentication.
+ /// </summary>
+ public string Username { get; }
+
+ /// <summary>
+ /// Gets the password to submit on requests that require authentication.
+ /// </summary>
+ public string Password { get; }
+
private Uri CreateUri(string hostname, int port, bool enableSsl)
{
var scheme = enableSsl ? "wss" : "ws";
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatusCode.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatusCode.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatusCode.cs
index 7b0bc94..558e4f6 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatusCode.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatusCode.cs
@@ -49,9 +49,9 @@ namespace Gremlin.Net.Driver.Messages
case ResponseStatusCode.Success:
case ResponseStatusCode.NoContent:
case ResponseStatusCode.PartialContent:
+ case ResponseStatusCode.Authenticate:
return false;
case ResponseStatusCode.Unauthorized:
- case ResponseStatusCode.Authenticate:
case ResponseStatusCode.MalformedRequest:
case ResponseStatusCode.InvalidRequestArguments:
case ResponseStatusCode.ServerError:
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs
index 5a940cd..c9dc0fb 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs
@@ -31,6 +31,11 @@ namespace Gremlin.Net.Driver
public class Tokens
{
/// <summary>
+ /// Operation used by the client to authenticate itself.
+ /// </summary>
+ public static string OpsAuthentication = "authentication";
+
+ /// <summary>
/// Operation used for a request that contains the Bytecode representation of a Traversal.
/// </summary>
public static string OpsBytecode = "bytecode";
@@ -108,6 +113,12 @@ namespace Gremlin.Net.Driver
/// </summary>
public static string ArgsEvalTimeout = "scriptEvaluationTimeout";
+ /// <summary>
+ /// Argument name for the response to the server authentication challenge. This value is dependent on the SASL
+ /// authentication mechanism required by the server.
+ /// </summary>
+ public static string ArgsSasl = "sasl";
+
internal static string ValAggregateToMap = "map";
internal static string ValAggregateToBulkSet = "bulkset";
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientAuthenticationTests.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientAuthenticationTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientAuthenticationTests.cs
new file mode 100644
index 0000000..5045f3c
--- /dev/null
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientAuthenticationTests.cs
@@ -0,0 +1,86 @@
+#region License
+
+/*
+ * 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.
+ */
+
+#endregion
+
+using System;
+using System.Threading.Tasks;
+using Gremlin.Net.Driver;
+using Gremlin.Net.Driver.Exceptions;
+using Gremlin.Net.IntegrationTest.Util;
+using Xunit;
+
+namespace Gremlin.Net.IntegrationTest.Driver
+{
+ public class GremlinClientAuthenticationTests
+ {
+ private static readonly string TestHost = ConfigProvider.Configuration["TestServerIpAddress"];
+ private static readonly int TestPort = Convert.ToInt32(ConfigProvider.Configuration["TestSecureServerPort"]);
+ private readonly RequestMessageProvider _requestMessageProvider = new RequestMessageProvider();
+
+ [Fact]
+ public async Task ShouldThrowForMissingCredentials()
+ {
+ var gremlinServer = new GremlinServer(TestHost, TestPort);
+ using (var gremlinClient = new GremlinClient(gremlinServer))
+ {
+ var exception = await Assert.ThrowsAsync<InvalidOperationException>(
+ async () => await gremlinClient.SubmitWithSingleResultAsync<string>(_requestMessageProvider
+ .GetDummyMessage()));
+
+ Assert.Contains("authentication", exception.Message);
+ Assert.Contains("credentials", exception.Message);
+ }
+ }
+
+ [Theory]
+ [InlineData("unknownUser", "passwordDoesntMatter")]
+ [InlineData("stephen", "wrongPassword")]
+ public async Task ShouldThrowForWrongCredentials(string username, string password)
+ {
+ var gremlinServer = new GremlinServer(TestHost, TestPort, username: username, password: password);
+ using (var gremlinClient = new GremlinClient(gremlinServer))
+ {
+ var exception = await Assert.ThrowsAsync<ResponseException>(
+ async () => await gremlinClient.SubmitWithSingleResultAsync<string>(_requestMessageProvider
+ .GetDummyMessage()));
+
+ Assert.Contains("Unauthorized", exception.Message);
+ }
+ }
+
+ [Theory]
+ [InlineData("'Hello' + 'World'", "HelloWorld")]
+ public async Task ScriptShouldBeEvaluatedAndResultReturnedForCorrectCredentials(string requestMsg,
+ string expectedResponse)
+ {
+ const string username = "stephen";
+ const string password = "password";
+ var gremlinServer = new GremlinServer(TestHost, TestPort, username: username, password: password);
+ using (var gremlinClient = new GremlinClient(gremlinServer))
+ {
+ var response = await gremlinClient.SubmitWithSingleResultAsync<string>(requestMsg);
+
+ Assert.Equal(expectedResponse, response);
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/appsettings.json
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/appsettings.json b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/appsettings.json
index 38007ec..5788e50 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/appsettings.json
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/appsettings.json
@@ -1,4 +1,5 @@
{
"TestServerIpAddress": "localhost",
- "TestServerPort": 45950
+ "TestServerPort": 45950,
+ "TestSecureServerPort": 45951
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88415ee3/gremlin-dotnet/test/pom.xml
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/test/pom.xml b/gremlin-dotnet/test/pom.xml
index 488a772..d062620 100644
--- a/gremlin-dotnet/test/pom.xml
+++ b/gremlin-dotnet/test/pom.xml
@@ -122,6 +122,19 @@ server.start().join()
project.setContextValue("gremlin.dotnet.server", server)
log.info("Gremlin Server with no authentication started on port 45950")
+
+def settingsSecure = Settings.read("${gremlin.server.dir}/conf/gremlin-server-modern.yaml")
+settingsSecure.graphs.graph = "${gremlin.server.dir}/conf/tinkergraph-empty.properties"
+settingsSecure.scriptEngines["gremlin-groovy"].scripts = ["${gremlin.server.dir}/scripts/generate-modern.groovy"]
+settingsSecure.port = 45951
+settingsSecure.authentication.className = "org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator"
+settingsSecure.authentication.config = [credentialsDb: "${gremlin.server.dir}/conf/tinkergraph-credentials.properties", credentialsDbLocation: "${gremlin.server.dir}/data/credentials.kryo"]
+
+def serverSecure = new GremlinServer(settingsSecure)
+serverSecure.start().join()
+
+project.setContextValue("gremlin.dotnet.server.secure", serverSecure)
+log.info("Gremlin Server with authentication started on port 45951")
]]>
</script>
</scripts>
@@ -147,6 +160,10 @@ def server = project.getContextValue("gremlin.dotnet.server")
log.info("Shutting down $server")
server.stop().join()
+def serverSecure = project.getContextValue("gremlin.dotnet.server.secure")
+log.info("Shutting down $serverSecure")
+serverSecure.stop().join()
+
log.info("Gremlin Server instance shutdown for gremlin-dotnet")
]]>
</script>