From f5a82c46642cdffdfffcbd01cd0152bfc20bc7a4 Mon Sep 17 00:00:00 2001 From: sanych-sun Date: Fri, 1 Dec 2023 00:46:35 -0800 Subject: [PATCH] Prepare to SASL implementation --- .../Auth/PlainAuthMechanism.cs | 14 +++++++++++++- src/RabbitMQ.Next.Abstractions/IAuthMechanism.cs | 5 ++++- src/RabbitMQ.Next/Connection.cs | 9 ++++----- .../Transport/Methods/Connection/StartOkMethod.cs | 5 +++-- .../Methods/Connection/StartOkMethodFormatter.cs | 2 +- .../Abstractions/Auth/PlainAuthMechanismTests.cs | 11 ++++++++--- .../Tasks/TaskExtensionsTests.cs | 2 +- .../Transport/Methods/Connection/ModelTests.cs | 2 +- .../Methods/Connection/SerializationTests.cs | 2 +- 9 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/RabbitMQ.Next.Abstractions/Auth/PlainAuthMechanism.cs b/src/RabbitMQ.Next.Abstractions/Auth/PlainAuthMechanism.cs index 4044737d..c5be956f 100644 --- a/src/RabbitMQ.Next.Abstractions/Auth/PlainAuthMechanism.cs +++ b/src/RabbitMQ.Next.Abstractions/Auth/PlainAuthMechanism.cs @@ -1,3 +1,7 @@ +using System; +using System.Text; +using System.Threading.Tasks; + namespace RabbitMQ.Next.Auth; public class PlainAuthMechanism : IAuthMechanism @@ -9,8 +13,16 @@ public PlainAuthMechanism(string userName, string password) } public string Type => "PLAIN"; + public ValueTask> HandleChallengeAsync(ReadOnlySpan challenge) + { + if (!challenge.IsEmpty) + { + throw new NotSupportedException("PlainAuthMechanism does not support challenges."); + } - public string ToResponse() => $"\0{this.UserName}\0{this.Password}"; + ReadOnlyMemory response = Encoding.UTF8.GetBytes($"\0{this.UserName}\0{this.Password}"); + return ValueTask.FromResult(response); + } public string UserName { get; } diff --git a/src/RabbitMQ.Next.Abstractions/IAuthMechanism.cs b/src/RabbitMQ.Next.Abstractions/IAuthMechanism.cs index 7c21d2b1..4eb2b572 100644 --- a/src/RabbitMQ.Next.Abstractions/IAuthMechanism.cs +++ b/src/RabbitMQ.Next.Abstractions/IAuthMechanism.cs @@ -1,8 +1,11 @@ +using System; +using System.Threading.Tasks; + namespace RabbitMQ.Next; public interface IAuthMechanism { string Type { get; } - string ToResponse(); + ValueTask> HandleChallengeAsync(ReadOnlySpan challenge); } \ No newline at end of file diff --git a/src/RabbitMQ.Next/Connection.cs b/src/RabbitMQ.Next/Connection.cs index a06817c8..efe67fd7 100644 --- a/src/RabbitMQ.Next/Connection.cs +++ b/src/RabbitMQ.Next/Connection.cs @@ -270,17 +270,16 @@ private static async Task NegotiateConnectionAsync(IChannel // connection should be forcibly closed if negotiation phase take more then 10s. cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token.Combine(cancellation); - var startMethodTask = channel.WaitAsync(cancellation); - - var startMethod = await startMethodTask; - + var startMethod = await channel.WaitAsync(cancellation); if (!startMethod.Mechanisms.Contains(settings.Auth.Type)) { throw new NotSupportedException("Provided auth mechanism does not supported by the server"); } + var initialChallengeResponse = await settings.Auth.HandleChallengeAsync(Span.Empty); + var tuneMethodTask = channel.WaitAsync(cancellation); - await channel.SendAsync(new StartOkMethod(settings.Auth.Type, settings.Auth.ToResponse(), settings.Locale, settings.ClientProperties), cancellation); + await channel.SendAsync(new StartOkMethod(settings.Auth.Type, initialChallengeResponse, settings.Locale, settings.ClientProperties), cancellation); var tuneMethod = await tuneMethodTask; var negotiationResult = new NegotiationResults( diff --git a/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethod.cs b/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethod.cs index 50d45234..3f236ef1 100644 --- a/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethod.cs +++ b/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethod.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using RabbitMQ.Next.Methods; @@ -5,7 +6,7 @@ namespace RabbitMQ.Next.Transport.Methods.Connection; public readonly struct StartOkMethod : IOutgoingMethod { - public StartOkMethod(string mechanism, string response, string locale, IReadOnlyDictionary clientProperties) + public StartOkMethod(string mechanism, ReadOnlyMemory response, string locale, IReadOnlyDictionary clientProperties) { this.Mechanism = mechanism; this.Response = response; @@ -19,7 +20,7 @@ public StartOkMethod(string mechanism, string response, string locale, IReadOnly public string Mechanism { get; } - public string Response { get; } + public ReadOnlyMemory Response { get; } public string Locale { get; } } \ No newline at end of file diff --git a/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethodFormatter.cs b/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethodFormatter.cs index c840572a..daf0f874 100644 --- a/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethodFormatter.cs +++ b/src/RabbitMQ.Next/Transport/Methods/Connection/StartOkMethodFormatter.cs @@ -8,6 +8,6 @@ public Span Write(Span destination, StartOkMethod method) => destination .Write(method.ClientProperties) .Write(method.Mechanism) - .Write(method.Response, true) + .Write(method.Response.Span) .Write(method.Locale); } \ No newline at end of file diff --git a/tests/RabbitMQ.Next.Tests/Abstractions/Auth/PlainAuthMechanismTests.cs b/tests/RabbitMQ.Next.Tests/Abstractions/Auth/PlainAuthMechanismTests.cs index 799f0c79..d7abd4e9 100644 --- a/tests/RabbitMQ.Next.Tests/Abstractions/Auth/PlainAuthMechanismTests.cs +++ b/tests/RabbitMQ.Next.Tests/Abstractions/Auth/PlainAuthMechanismTests.cs @@ -1,3 +1,6 @@ +using System; +using System.Text; +using System.Threading.Tasks; using NSubstitute; using RabbitMQ.Next.Auth; using Xunit; @@ -20,14 +23,16 @@ public void CtorTests() } [Fact] - public void ToResponse() + public async Task HandleChallengeAsync() { var user = "test"; var password = "pwd"; + var expected = "\0test\0pwd"u8.ToArray(); var auth = new PlainAuthMechanism(user, password); - - Assert.Equal($"\0{user}\0{password}", auth.ToResponse()); + var response = await auth.HandleChallengeAsync(ReadOnlySpan.Empty); + + Assert.Equal(expected, response.ToArray()); } [Fact] diff --git a/tests/RabbitMQ.Next.Tests/Tasks/TaskExtensionsTests.cs b/tests/RabbitMQ.Next.Tests/Tasks/TaskExtensionsTests.cs index 0c81d8b6..a3ac390b 100644 --- a/tests/RabbitMQ.Next.Tests/Tasks/TaskExtensionsTests.cs +++ b/tests/RabbitMQ.Next.Tests/Tasks/TaskExtensionsTests.cs @@ -60,7 +60,7 @@ public async Task WithCancellationReturnsResult() var wrapped = task.WithCancellation(cancellation.Token); tcs.SetResult(5); - await Task.Yield(); + await Task.Delay(10); Assert.True(wrapped.IsCompleted); Assert.Equal(5, await wrapped); diff --git a/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/ModelTests.cs b/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/ModelTests.cs index a41b9a40..d934e888 100644 --- a/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/ModelTests.cs +++ b/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/ModelTests.cs @@ -32,7 +32,7 @@ public void StartMethod() public void StartOkMethod() { var mechanism = "PLAIN"; - var response = "test"; + var response = "ab"u8.ToArray(); var locale = "en_US"; var clientProperties = new Dictionary() { diff --git a/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/SerializationTests.cs b/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/SerializationTests.cs index 5ac3040b..d023c962 100644 --- a/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/SerializationTests.cs +++ b/tests/RabbitMQ.Next.Tests/Transport/Methods/Connection/SerializationTests.cs @@ -49,7 +49,7 @@ public void StartOkMethodFormatter() ["exchange_exchange_bindings"] = true } }; - var method = new StartOkMethod("PLAIN", "\0test1\0test1", "en_US", clientProperties); + var method = new StartOkMethod("PLAIN", "\0test1\0test1"u8.ToArray(), "en_US", clientProperties); this.TestFormatter(method); }