#109 expose IPrincipal on connection when identify is established through SASL or transport
This commit is contained in:
Родитель
96d009ee96
Коммит
79af04dccc
1
amqp.sln
1
amqp.sln
|
@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
Amqp.Micro.nuspec = Amqp.Micro.nuspec
|
Amqp.Micro.nuspec = Amqp.Micro.nuspec
|
||||||
Amqp.Net.nuspec = Amqp.Net.nuspec
|
Amqp.Net.nuspec = Amqp.Net.nuspec
|
||||||
build.cmd = build.cmd
|
build.cmd = build.cmd
|
||||||
|
dnx\Amqp.DotNet\project.json = dnx\Amqp.DotNet\project.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amqp.Net", "src\Amqp.Net.csproj", "{92153715-1D99-43B1-B291-470CF91A156D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amqp.Net", "src\Amqp.Net.csproj", "{92153715-1D99-43B1-B291-470CF91A156D}"
|
||||||
|
|
|
@ -64,7 +64,9 @@
|
||||||
"System.Reflection.Emit.Lightweight": "4.0.1-beta-23409",
|
"System.Reflection.Emit.Lightweight": "4.0.1-beta-23409",
|
||||||
"System.Reflection.Extensions": "4.0.1-beta-23409",
|
"System.Reflection.Extensions": "4.0.1-beta-23409",
|
||||||
"System.Reflection.TypeExtensions": "4.0.1-beta-23409",
|
"System.Reflection.TypeExtensions": "4.0.1-beta-23409",
|
||||||
"System.Runtime.Serialization.Primitives": "4.0.11-beta-23409"
|
"System.Runtime.Serialization.Primitives": "4.0.11-beta-23409",
|
||||||
|
"System.Security.Claims": "4.0.1-beta-23509",
|
||||||
|
"System.Security.Principal": "4.0.1-beta-23509"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
<Compile Include="Listener\AttachContext.cs" />
|
<Compile Include="Listener\AttachContext.cs" />
|
||||||
<Compile Include="Listener\ContextState.cs" />
|
<Compile Include="Listener\ContextState.cs" />
|
||||||
<Compile Include="Listener\FlowContext.cs" />
|
<Compile Include="Listener\FlowContext.cs" />
|
||||||
|
<Compile Include="Listener\IAuthenticated.cs" />
|
||||||
|
<Compile Include="Listener\X509Identity.cs" />
|
||||||
<Compile Include="Listener\LinkCollection.cs" />
|
<Compile Include="Listener\LinkCollection.cs" />
|
||||||
<Compile Include="Listener\ILinkProcessor.cs" />
|
<Compile Include="Listener\ILinkProcessor.cs" />
|
||||||
<Compile Include="Listener\LinkEndpoint.cs" />
|
<Compile Include="Listener\LinkEndpoint.cs" />
|
||||||
|
@ -71,8 +73,8 @@
|
||||||
<Compile Include="Net\ConnectionFactoryBase.cs" />
|
<Compile Include="Net\ConnectionFactoryBase.cs" />
|
||||||
<Compile Include="Properties\Version.cs" />
|
<Compile Include="Properties\Version.cs" />
|
||||||
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
||||||
<Compile Include="Sasl\SaslPlainMechanism.cs" />
|
<Compile Include="Listener\SaslPlainMechanism.cs" />
|
||||||
<Compile Include="Sasl\SaslMechanism.cs" />
|
<Compile Include="Listener\SaslMechanism.cs" />
|
||||||
<Compile Include="Listener\IContainer.cs" />
|
<Compile Include="Listener\IContainer.cs" />
|
||||||
<Compile Include="Listener\ListenerSession.cs" />
|
<Compile Include="Listener\ListenerSession.cs" />
|
||||||
<Compile Include="Listener\ListenerConnection.cs" />
|
<Compile Include="Listener\ListenerConnection.cs" />
|
||||||
|
|
|
@ -51,8 +51,6 @@
|
||||||
<Compile Include="Net\TypeExtensions.cs" />
|
<Compile Include="Net\TypeExtensions.cs" />
|
||||||
<Compile Include="Properties\Version.cs" />
|
<Compile Include="Properties\Version.cs" />
|
||||||
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
||||||
<Compile Include="Sasl\SaslPlainMechanism.cs" />
|
|
||||||
<Compile Include="Sasl\SaslMechanism.cs" />
|
|
||||||
<Compile Include="Delivery.cs" />
|
<Compile Include="Delivery.cs" />
|
||||||
<Compile Include="Framing\Accepted.cs" />
|
<Compile Include="Framing\Accepted.cs" />
|
||||||
<Compile Include="Framing\ApplicationProperties.cs" />
|
<Compile Include="Framing\ApplicationProperties.cs" />
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
<Compile Include="Listener\AttachContext.cs" />
|
<Compile Include="Listener\AttachContext.cs" />
|
||||||
<Compile Include="Listener\ContextState.cs" />
|
<Compile Include="Listener\ContextState.cs" />
|
||||||
<Compile Include="Listener\FlowContext.cs" />
|
<Compile Include="Listener\FlowContext.cs" />
|
||||||
|
<Compile Include="Listener\IAuthenticated.cs" />
|
||||||
<Compile Include="Listener\LinkCollection.cs" />
|
<Compile Include="Listener\LinkCollection.cs" />
|
||||||
<Compile Include="Listener\ILinkProcessor.cs" />
|
<Compile Include="Listener\ILinkProcessor.cs" />
|
||||||
<Compile Include="Listener\LinkEndpoint.cs" />
|
<Compile Include="Listener\LinkEndpoint.cs" />
|
||||||
|
@ -58,6 +59,9 @@
|
||||||
<Compile Include="Listener\IMessageProcessor.cs" />
|
<Compile Include="Listener\IMessageProcessor.cs" />
|
||||||
<Compile Include="Listener\Context.cs" />
|
<Compile Include="Listener\Context.cs" />
|
||||||
<Compile Include="Listener\ListenerLink.cs" />
|
<Compile Include="Listener\ListenerLink.cs" />
|
||||||
|
<Compile Include="Listener\SaslMechanism.cs" />
|
||||||
|
<Compile Include="Listener\SaslPlainMechanism.cs" />
|
||||||
|
<Compile Include="Listener\X509Identity.cs" />
|
||||||
<Compile Include="Net\ResourceManager.cs" />
|
<Compile Include="Net\ResourceManager.cs" />
|
||||||
<Compile Include="Net\BufferManager.cs" />
|
<Compile Include="Net\BufferManager.cs" />
|
||||||
<Compile Include="Net\SocketExtensions.cs" />
|
<Compile Include="Net\SocketExtensions.cs" />
|
||||||
|
@ -70,8 +74,6 @@
|
||||||
<Compile Include="Net\ConnectionFactoryBase.cs" />
|
<Compile Include="Net\ConnectionFactoryBase.cs" />
|
||||||
<Compile Include="Properties\Version.cs" />
|
<Compile Include="Properties\Version.cs" />
|
||||||
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
<Compile Include="Sasl\SaslExternalProfile.cs" />
|
||||||
<Compile Include="Sasl\SaslPlainMechanism.cs" />
|
|
||||||
<Compile Include="Sasl\SaslMechanism.cs" />
|
|
||||||
<Compile Include="Listener\IContainer.cs" />
|
<Compile Include="Listener\IContainer.cs" />
|
||||||
<Compile Include="Listener\ListenerSession.cs" />
|
<Compile Include="Listener\ListenerSession.cs" />
|
||||||
<Compile Include="Listener\ListenerConnection.cs" />
|
<Compile Include="Listener\ListenerConnection.cs" />
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace Amqp.Listener
|
||||||
#endif
|
#endif
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Amqp.Framing;
|
using Amqp.Framing;
|
||||||
using Amqp.Sasl;
|
using Amqp.Sasl;
|
||||||
|
@ -190,13 +191,27 @@ namespace Amqp.Listener
|
||||||
|
|
||||||
async Task HandleTransportAsync(IAsyncTransport transport)
|
async Task HandleTransportAsync(IAsyncTransport transport)
|
||||||
{
|
{
|
||||||
|
IPrincipal principal = null;
|
||||||
if (this.saslSettings != null)
|
if (this.saslSettings != null)
|
||||||
{
|
{
|
||||||
ListenerSaslProfile profile = new ListenerSaslProfile(this);
|
ListenerSaslProfile profile = new ListenerSaslProfile(this);
|
||||||
transport = await profile.OpenAsync(null, this.BufferManager, transport);
|
transport = await profile.OpenAsync(null, this.BufferManager, transport);
|
||||||
|
principal = profile.GetPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection connection = new ListenerConnection(this, this.address, transport);
|
var connection = new ListenerConnection(this, this.address, transport);
|
||||||
|
if (principal == null)
|
||||||
|
{
|
||||||
|
// SASL principal preferred. If not present, check transport.
|
||||||
|
IAuthenticated authenticated = transport as IAuthenticated;
|
||||||
|
if (authenticated != null)
|
||||||
|
{
|
||||||
|
principal = authenticated.Principal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.Principal = principal;
|
||||||
|
|
||||||
bool shouldClose = false;
|
bool shouldClose = false;
|
||||||
lock (this.connections)
|
lock (this.connections)
|
||||||
{
|
{
|
||||||
|
@ -355,9 +370,15 @@ namespace Amqp.Listener
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaslProfile InnerProfile
|
public IPrincipal GetPrincipal()
|
||||||
{
|
{
|
||||||
get { return this.innerProfile; }
|
IAuthenticated authenticated = this.innerProfile as IAuthenticated;
|
||||||
|
if (authenticated != null)
|
||||||
|
{
|
||||||
|
return authenticated.Principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ITransport UpgradeTransport(ITransport transport)
|
protected override ITransport UpgradeTransport(ITransport transport)
|
||||||
|
@ -499,7 +520,7 @@ namespace Amqp.Listener
|
||||||
protected virtual Task<IAsyncTransport> CreateTransportAsync(Socket socket)
|
protected virtual Task<IAsyncTransport> CreateTransportAsync(Socket socket)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<IAsyncTransport>();
|
var tcs = new TaskCompletionSource<IAsyncTransport>();
|
||||||
tcs.SetResult(new TcpTransport(socket, this.Listener.BufferManager));
|
tcs.SetResult(new ListenerTcpTransport(socket, this.Listener.BufferManager));
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +579,36 @@ namespace Amqp.Listener
|
||||||
this.Listener.sslSettings.Protocols, this.Listener.sslSettings.CheckCertificateRevocation);
|
this.Listener.sslSettings.Protocols, this.Listener.sslSettings.CheckCertificateRevocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TcpTransport(sslStream, this.Listener.BufferManager);
|
return new ListenerTcpTransport(sslStream, this.Listener.BufferManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListenerTcpTransport : TcpTransport, IAuthenticated
|
||||||
|
{
|
||||||
|
public ListenerTcpTransport(Socket socket, IBufferManager bufferManager)
|
||||||
|
: base(bufferManager)
|
||||||
|
{
|
||||||
|
this.socketTransport = new TcpSocket(this, socket);
|
||||||
|
this.writer = new Writer(this, this.socketTransport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenerTcpTransport(SslStream sslStream, IBufferManager bufferManager)
|
||||||
|
: base(bufferManager)
|
||||||
|
{
|
||||||
|
this.socketTransport = new SslSocket(this, sslStream);
|
||||||
|
this.writer = new Writer(this, this.socketTransport);
|
||||||
|
if (sslStream.RemoteCertificate != null)
|
||||||
|
{
|
||||||
|
this.Principal = new GenericPrincipal(
|
||||||
|
new X509Identity(sslStream.RemoteCertificate),
|
||||||
|
new string[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPrincipal Principal
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +647,7 @@ namespace Amqp.Listener
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var wsContext = await context.AcceptWebSocketAsync(WebSocketTransport.WebSocketSubProtocol);
|
var wsContext = await context.AcceptWebSocketAsync(WebSocketTransport.WebSocketSubProtocol);
|
||||||
var wsTransport = new WebSocketTransport(wsContext.WebSocket);
|
var wsTransport = new ListenerWebSocketTransport(wsContext);
|
||||||
await this.listener.HandleTransportAsync(wsTransport);
|
await this.listener.HandleTransportAsync(wsTransport);
|
||||||
}
|
}
|
||||||
catch(Exception exception)
|
catch(Exception exception)
|
||||||
|
@ -627,6 +677,21 @@ namespace Amqp.Listener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ListenerWebSocketTransport : WebSocketTransport, IAuthenticated
|
||||||
|
{
|
||||||
|
public ListenerWebSocketTransport(HttpListenerWebSocketContext context)
|
||||||
|
: base(context.WebSocket)
|
||||||
|
{
|
||||||
|
this.Principal = context.User;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPrincipal Principal
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed 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
|
||||||
|
//
|
||||||
|
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR
|
||||||
|
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||||
|
// NON-INFRINGEMENT.
|
||||||
|
//
|
||||||
|
// See the Apache Version 2.0 License for specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Amqp.Listener
|
||||||
|
{
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
interface IAuthenticated
|
||||||
|
{
|
||||||
|
IPrincipal Principal { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
namespace Amqp.Listener
|
namespace Amqp.Listener
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Amqp.Framing;
|
using Amqp.Framing;
|
||||||
|
|
||||||
|
@ -35,6 +36,16 @@ namespace Amqp.Listener
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a IPrincipal object for the connection. If the value is null,
|
||||||
|
/// the connection is not authenticated.
|
||||||
|
/// </summary>
|
||||||
|
public IPrincipal Principal
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
internal set;
|
||||||
|
}
|
||||||
|
|
||||||
internal ConnectionListener Listener
|
internal ConnectionListener Listener
|
||||||
{
|
{
|
||||||
get { return this.listener; }
|
get { return this.listener; }
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// ------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace Amqp.Sasl
|
namespace Amqp.Listener
|
||||||
{
|
{
|
||||||
|
using Amqp.Sasl;
|
||||||
|
|
||||||
abstract class SaslMechanism
|
abstract class SaslMechanism
|
||||||
{
|
{
|
||||||
public abstract string Name { get; }
|
public abstract string Name { get; }
|
|
@ -15,12 +15,14 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
// ------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace Amqp.Sasl
|
namespace Amqp.Listener
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Amqp.Framing;
|
using Amqp.Framing;
|
||||||
using Amqp.Types;
|
using Amqp.Types;
|
||||||
|
using Amqp.Sasl;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
class SaslPlainMechanism : SaslMechanism
|
class SaslPlainMechanism : SaslMechanism
|
||||||
{
|
{
|
||||||
|
@ -43,7 +45,7 @@ namespace Amqp.Sasl
|
||||||
return new SaslPlainProfile(this);
|
return new SaslPlainProfile(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SaslPlainProfile : SaslProfile
|
class SaslPlainProfile : SaslProfile, IAuthenticated
|
||||||
{
|
{
|
||||||
readonly SaslPlainMechanism mechanism;
|
readonly SaslPlainMechanism mechanism;
|
||||||
|
|
||||||
|
@ -52,6 +54,12 @@ namespace Amqp.Sasl
|
||||||
this.mechanism = mechanism;
|
this.mechanism = mechanism;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IPrincipal Principal
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
protected override ITransport UpgradeTransport(ITransport transport)
|
protected override ITransport UpgradeTransport(ITransport transport)
|
||||||
{
|
{
|
||||||
return transport;
|
return transport;
|
||||||
|
@ -85,6 +93,10 @@ namespace Amqp.Sasl
|
||||||
string.Equals(this.mechanism.user, items[1], StringComparison.OrdinalIgnoreCase) &&
|
string.Equals(this.mechanism.user, items[1], StringComparison.OrdinalIgnoreCase) &&
|
||||||
string.Equals(this.mechanism.password, items[2], StringComparison.Ordinal))
|
string.Equals(this.mechanism.password, items[2], StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
this.Principal = new GenericPrincipal(
|
||||||
|
new GenericIdentity(string.IsNullOrEmpty(items[2]) ? items[0] : items[2], this.mechanism.Name),
|
||||||
|
new string[0]);
|
||||||
|
|
||||||
return SaslCode.Ok;
|
return SaslCode.Ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed 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
|
||||||
|
//
|
||||||
|
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||||
|
// EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR
|
||||||
|
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||||
|
// NON-INFRINGEMENT.
|
||||||
|
//
|
||||||
|
// See the Apache Version 2.0 License for specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Amqp.Listener
|
||||||
|
{
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an client identity established by SSL client certificate authentication.
|
||||||
|
/// </summary>
|
||||||
|
public class X509Identity : IIdentity
|
||||||
|
{
|
||||||
|
internal X509Identity(X509Certificate certificate)
|
||||||
|
{
|
||||||
|
this.Certificate = certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the client certificate.
|
||||||
|
/// </summary>
|
||||||
|
public X509Certificate Certificate
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of authentication used. ("X509").
|
||||||
|
/// </summary>
|
||||||
|
public string AuthenticationType
|
||||||
|
{
|
||||||
|
get { return "X509"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that indicates whether the user has been authenticated.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAuthenticated
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the identity.
|
||||||
|
/// </summary>
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return this.Certificate.Subject; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,13 +24,13 @@ namespace Amqp
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
sealed class TcpTransport : IAsyncTransport
|
class TcpTransport : IAsyncTransport
|
||||||
{
|
{
|
||||||
static readonly RemoteCertificateValidationCallback noneCertValidator = (a, b, c, d) => true;
|
static readonly RemoteCertificateValidationCallback noneCertValidator = (a, b, c, d) => true;
|
||||||
readonly IBufferManager bufferManager;
|
readonly IBufferManager bufferManager;
|
||||||
Connection connection;
|
Connection connection;
|
||||||
Writer writer;
|
protected Writer writer;
|
||||||
IAsyncTransport socketTransport;
|
protected IAsyncTransport socketTransport;
|
||||||
|
|
||||||
public TcpTransport()
|
public TcpTransport()
|
||||||
: this(null)
|
: this(null)
|
||||||
|
@ -42,22 +42,6 @@ namespace Amqp
|
||||||
this.bufferManager = bufferManager;
|
this.bufferManager = bufferManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// called by listener
|
|
||||||
public TcpTransport(Socket socket, IBufferManager bufferManager)
|
|
||||||
: this(bufferManager)
|
|
||||||
{
|
|
||||||
this.socketTransport = new TcpSocket(this, socket);
|
|
||||||
this.writer = new Writer(this, this.socketTransport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by listener
|
|
||||||
public TcpTransport(SslStream sslStream, IBufferManager bufferManager)
|
|
||||||
: this(bufferManager)
|
|
||||||
{
|
|
||||||
this.socketTransport = new SslSocket(this, sslStream);
|
|
||||||
this.writer = new Writer(this, this.socketTransport);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Connect(Connection connection, Address address, bool noVerification)
|
public void Connect(Connection connection, Address address, bool noVerification)
|
||||||
{
|
{
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
@ -203,7 +187,7 @@ namespace Amqp
|
||||||
this.writer.DisposeQueuedBuffers();
|
this.writer.DisposeQueuedBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
class TcpSocket : IAsyncTransport
|
protected class TcpSocket : IAsyncTransport
|
||||||
{
|
{
|
||||||
readonly static EventHandler<SocketAsyncEventArgs> onWriteComplete = OnWriteComplete;
|
readonly static EventHandler<SocketAsyncEventArgs> onWriteComplete = OnWriteComplete;
|
||||||
readonly TcpTransport transport;
|
readonly TcpTransport transport;
|
||||||
|
@ -353,7 +337,7 @@ namespace Amqp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SslSocket : IAsyncTransport
|
protected class SslSocket : IAsyncTransport
|
||||||
{
|
{
|
||||||
readonly TcpTransport transport;
|
readonly TcpTransport transport;
|
||||||
readonly SslStream sslStream;
|
readonly SslStream sslStream;
|
||||||
|
@ -439,7 +423,7 @@ namespace Amqp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Writer
|
protected class Writer
|
||||||
{
|
{
|
||||||
readonly TcpTransport owner;
|
readonly TcpTransport owner;
|
||||||
readonly IAsyncTransport transport;
|
readonly IAsyncTransport transport;
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace Amqp
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
sealed class WebSocketTransport : IAsyncTransport
|
class WebSocketTransport : IAsyncTransport
|
||||||
{
|
{
|
||||||
public const string WebSocketSubProtocol = "AMQPWSB10";
|
public const string WebSocketSubProtocol = "AMQPWSB10";
|
||||||
public const string WebSockets = "WS";
|
public const string WebSockets = "WS";
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Amqp;
|
using Amqp;
|
||||||
using Amqp.Framing;
|
using Amqp.Framing;
|
||||||
using Amqp.Listener;
|
using Amqp.Listener;
|
||||||
|
using Amqp.Sasl;
|
||||||
using Amqp.Types;
|
using Amqp.Types;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ namespace Test.Amqp
|
||||||
this.Uri = new Uri("amqp://guest:guest@localhost:5765");
|
this.Uri = new Uri("amqp://guest:guest@localhost:5765");
|
||||||
|
|
||||||
this.host = new ContainerHost(new List<Uri>() { this.Uri }, null, this.Uri.UserInfo);
|
this.host = new ContainerHost(new List<Uri>() { this.Uri }, null, this.Uri.UserInfo);
|
||||||
|
this.host.Listeners[0].SASL.EnableExternalMechanism = true;
|
||||||
this.host.Open();
|
this.host.Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,6 +479,70 @@ namespace Test.Amqp
|
||||||
connection.Close();
|
connection.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ContainerHostPlainPrincipalTest()
|
||||||
|
{
|
||||||
|
string name = MethodInfo.GetCurrentMethod().Name;
|
||||||
|
ListenerLink link = null;
|
||||||
|
var linkProcessor = new TestLinkProcessor();
|
||||||
|
linkProcessor.OnLinkAttached += a => link = a;
|
||||||
|
this.host.RegisterLinkProcessor(linkProcessor);
|
||||||
|
|
||||||
|
var connection = new Connection(Address);
|
||||||
|
var session = new Session(connection);
|
||||||
|
var sender = new SenderLink(session, name, name);
|
||||||
|
sender.Send(new Message("msg1"), SendTimeout);
|
||||||
|
connection.Close();
|
||||||
|
|
||||||
|
Assert.IsTrue(link != null, "link is null");
|
||||||
|
var listenerConnection = (ListenerConnection)link.Session.Connection;
|
||||||
|
Assert.IsTrue(listenerConnection.Principal != null, "principal is null");
|
||||||
|
Assert.IsTrue(listenerConnection.Principal.Identity.AuthenticationType == "PLAIN", "wrong auth type");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ContainerHostX509PrincipalTest()
|
||||||
|
{
|
||||||
|
string name = MethodInfo.GetCurrentMethod().Name;
|
||||||
|
string address = "amqps://localhost:5676";
|
||||||
|
X509Certificate2 cert = GetCertificate(StoreLocation.LocalMachine, StoreName.My, "localhost");
|
||||||
|
ContainerHost sslHost = new ContainerHost(new Uri(address));
|
||||||
|
sslHost.Listeners[0].SSL.Certificate = cert;
|
||||||
|
sslHost.Listeners[0].SSL.ClientCertificateRequired = true;
|
||||||
|
sslHost.Listeners[0].SSL.RemoteCertificateValidationCallback = (a, b, c, d) => true;
|
||||||
|
sslHost.Listeners[0].SASL.EnableExternalMechanism = true;
|
||||||
|
ListenerLink link = null;
|
||||||
|
var linkProcessor = new TestLinkProcessor();
|
||||||
|
linkProcessor.OnLinkAttached += a => link = a;
|
||||||
|
sslHost.RegisterLinkProcessor(linkProcessor);
|
||||||
|
sslHost.Open();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var factory = new ConnectionFactory();
|
||||||
|
factory.SSL.RemoteCertificateValidationCallback = (a, b, c, d) => true;
|
||||||
|
factory.SSL.ClientCertificates.Add(cert);
|
||||||
|
factory.SASL.Profile = SaslProfile.External;
|
||||||
|
var connection = factory.CreateAsync(new Address(address)).Result;
|
||||||
|
var session = new Session(connection);
|
||||||
|
var sender = new SenderLink(session, name, name);
|
||||||
|
sender.Send(new Message("msg1"), SendTimeout);
|
||||||
|
connection.Close();
|
||||||
|
|
||||||
|
Assert.IsTrue(link != null, "link is null");
|
||||||
|
var listenerConnection = (ListenerConnection)link.Session.Connection;
|
||||||
|
Assert.IsTrue(listenerConnection.Principal != null, "principal is null");
|
||||||
|
Assert.IsTrue(listenerConnection.Principal.Identity.AuthenticationType == "X509", "wrong auth type");
|
||||||
|
|
||||||
|
X509Identity identity = (X509Identity)listenerConnection.Principal.Identity;
|
||||||
|
Assert.IsTrue(identity.Certificate != null, "certificate is null");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sslHost.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void InvalidAddresses()
|
public void InvalidAddresses()
|
||||||
{
|
{
|
||||||
|
@ -509,6 +576,23 @@ namespace Test.Amqp
|
||||||
connection.Close();
|
connection.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static X509Certificate2 GetCertificate(StoreLocation storeLocation, StoreName storeName, string certFindValue)
|
||||||
|
{
|
||||||
|
X509Store store = new X509Store(storeName, storeLocation);
|
||||||
|
store.Open(OpenFlags.OpenExistingOnly);
|
||||||
|
X509Certificate2Collection collection = store.Certificates.Find(
|
||||||
|
X509FindType.FindBySubjectName,
|
||||||
|
certFindValue,
|
||||||
|
false);
|
||||||
|
if (collection.Count == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("No certificate can be found using the find value " + certFindValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.Close();
|
||||||
|
return collection[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestMessageProcessor : IMessageProcessor
|
class TestMessageProcessor : IMessageProcessor
|
||||||
|
@ -569,8 +653,15 @@ namespace Test.Amqp
|
||||||
|
|
||||||
class TestLinkProcessor : ILinkProcessor
|
class TestLinkProcessor : ILinkProcessor
|
||||||
{
|
{
|
||||||
|
public Action<ListenerLink> OnLinkAttached;
|
||||||
|
|
||||||
public void Process(AttachContext attachContext)
|
public void Process(AttachContext attachContext)
|
||||||
{
|
{
|
||||||
|
if (this.OnLinkAttached != null)
|
||||||
|
{
|
||||||
|
this.OnLinkAttached(attachContext.Link);
|
||||||
|
}
|
||||||
|
|
||||||
attachContext.Complete(new TestLinkEndpoint(), attachContext.Attach.Role ? 0 : 30);
|
attachContext.Complete(new TestLinkEndpoint(), attachContext.Attach.Role ? 0 : 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче