Added more API Service Methods

This commit is contained in:
TheJoKlLa 2022-05-11 15:02:17 +02:00
parent a9a3f9c545
commit bb4a74f8c4
11 changed files with 202 additions and 150 deletions

View File

@ -130,11 +130,6 @@ namespace Borepin.Service.BFFH
/// <exception cref="AuthenticatingFailedException"></exception> /// <exception cref="AuthenticatingFailedException"></exception>
public async Task Connect(Connection connection) public async Task Connect(Connection connection)
{ {
if (IsConnected)
{
throw new AllreadyConnectedException();
}
string password; string password;
try try
{ {
@ -162,7 +157,6 @@ namespace Borepin.Service.BFFH
if (! await _AuthenticatePlainAsync(connection.Username, password).ConfigureAwait(false)) if (! await _AuthenticatePlainAsync(connection.Username, password).ConfigureAwait(false))
{ {
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);
throw new AuthenticatingFailedException();
} }
_CurrentConnection = new Connection_Plain(connection) _CurrentConnection = new Connection_Plain(connection)
@ -182,10 +176,6 @@ namespace Borepin.Service.BFFH
/// <exception cref="AuthenticatingFailedException"></exception> /// <exception cref="AuthenticatingFailedException"></exception>
public async Task Connect(Connection connection, string password) public async Task Connect(Connection connection, string password)
{ {
if (IsConnected)
{
throw new AllreadyConnectedException();
}
try try
{ {
@ -201,7 +191,7 @@ namespace Borepin.Service.BFFH
if (!await _AuthenticatePlainAsync(connection.Username, password).ConfigureAwait(false)) if (!await _AuthenticatePlainAsync(connection.Username, password).ConfigureAwait(false))
{ {
await Disconnect().ConfigureAwait(false); await Disconnect().ConfigureAwait(false);
throw new AuthenticatingFailedException();
} }
_CurrentConnection = new Connection_Plain(connection) _CurrentConnection = new Connection_Plain(connection)
@ -239,15 +229,6 @@ namespace Borepin.Service.BFFH
_APIConnection = new FabAccessAPI.Connection(rpcClient); _APIConnection = new FabAccessAPI.Connection(rpcClient);
} }
catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
{
throw new ReconnectingFailedException("Connecting failed", new ConnectingFailedException("Connecting failed", exception));
}
if (! await _AuthenticatePlainAsync(_CurrentConnection.Username, _CurrentConnection.Password).ConfigureAwait(false))
{
throw new ReconnectingFailedException("Authentication failed", new AuthenticatingFailedException());
}
} }
/// <summary> /// <summary>
@ -316,11 +297,7 @@ namespace Borepin.Service.BFFH
await _APIConnection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", password } }).ConfigureAwait(false); await _APIConnection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", password } }).ConfigureAwait(false);
return await Task.FromResult(true).ConfigureAwait(false); return await Task.FromResult(true).ConfigureAwait(false);
} }
catch(InvalidCredentialsException) catch(UnsupportedMechanismException)
{
return await Task.FromResult(true).ConfigureAwait(false);
}
catch (AuthenticatingFailedException)
{ {
return await Task.FromResult(true).ConfigureAwait(false); return await Task.FromResult(true).ConfigureAwait(false);
} }

View File

@ -1,22 +0,0 @@
using System;
namespace Borepin.Service.BFFH.Exceptions
{
public class APIIncompatibleException : Exception
{
public APIIncompatibleException()
{
}
public APIIncompatibleException(string message) : base(message)
{
}
public APIIncompatibleException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -1,22 +0,0 @@
using System;
namespace Borepin.Service.BFFH.Exceptions
{
public class AllreadyConnectedException : Exception
{
public AllreadyConnectedException()
{
}
public AllreadyConnectedException(string message) : base(message)
{
}
public AllreadyConnectedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -1,22 +0,0 @@
using System;
namespace Borepin.Service.BFFH.Exceptions
{
public class AuthenticatingFailedException : Exception
{
public AuthenticatingFailedException()
{
}
public AuthenticatingFailedException(string message) : base(message)
{
}
public AuthenticatingFailedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -1,22 +0,0 @@
using System;
namespace Borepin.Service.BFFH.Exceptions
{
public class ConnectingFailedException : Exception
{
public ConnectingFailedException()
{
}
public ConnectingFailedException(string message) : base(message)
{
}
public ConnectingFailedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -1,11 +1,19 @@
using FabAccessAPI.Schema; using Capnp.Rpc;
using FabAccessAPI.Exceptions;
using FabAccessAPI.Schema;
using System; using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace FabAccessAPI namespace FabAccessAPI
{ {
public class API : IAPI public class API : IAPI
{ {
#region Private Members #region Private Members
private Connection _APIConnection;
#endregion #endregion
#region Constructors #region Constructors
@ -28,7 +36,7 @@ namespace FabAccessAPI
{ {
get get
{ {
throw new NotImplementedException(); return _APIConnection != null && _APIConnection.RpcClient.State == ConnectionState.Active;
} }
} }
@ -42,24 +50,138 @@ namespace FabAccessAPI
#endregion #endregion
#region Methods #region Methods
public void Connect(ConnectionData connectionData) /// <summary>
/// Connect to server with ConnectionData
/// </summary>
/// <exception cref="AuthenticationException"></exception>
/// <exception cref="ConnectingFailedException"></exception>
public async Task Connect(ConnectionData connectionData)
{
if (IsConnected)
{
await Disconnect();
}
TcpRpcClient rpcClient = await _ConnectAsync(connectionData).ConfigureAwait(false);
_APIConnection = new Connection(rpcClient);
try
{
await _Authenticate(connectionData).ConfigureAwait(false);
}
catch(System.Exception)
{
await Disconnect().ConfigureAwait(false);
throw;
}
}
public Task Disconnect()
{
if (IsConnected)
{
_APIConnection.RpcClient?.Dispose();
}
_APIConnection = null;
ConnectionData = null;
ConnectionInfo = null;
return Task.CompletedTask;
}
public Task Reconnect()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Disconnect() public async Task<ConnectionInfo> TestConnection(ConnectionData connectionData)
{ {
throw new NotImplementedException(); try
{
TcpRpcClient rpcClient = await _ConnectAsync(connectionData).ConfigureAwait(false);
Connection testConnection = new Connection(rpcClient);
rpcClient.Dispose();
ConnectionInfo connectionInfo = new ConnectionInfo()
{
APIVersion = testConnection.
} }
public void Reconnect() return true;
}
catch
{ {
throw new NotImplementedException(); return null;
}
}
#endregion
#region Private Methods
private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// TODO Cert Check
return true;
} }
public ConnectionInfo TestConnection(ConnectionData connectionData) /// <summary>
/// Connect to server async with ConnectionData
/// </summary>
/// <exception cref="AuthenticationException">TLS Error</exception>
/// <exception cref="ConnectingFailedException">Based on RPC Exception</exception>
///
private async Task<TcpRpcClient> _ConnectAsync(ConnectionData connectionData)
{ {
throw new NotImplementedException(); TcpRpcClient rpcClient = new TcpRpcClient();
rpcClient.InjectMidlayer((tcpstream) =>
{
var sslStream = new SslStream(tcpstream, false, new RemoteCertificateValidationCallback(RemoteCertificateValidationCallback));
try
{
sslStream.AuthenticateAsClient("bffhd");
return sslStream;
}
catch (AuthenticationException)
{
sslStream.Close();
throw;
}
});
try
{
rpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
await rpcClient.WhenConnected.ConfigureAwait(false);
return rpcClient;
}
catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
{
throw new ConnectingFailedException("Connecting failed", exception);
}
}
/// <summary>
/// Authenticate connection with ConnectionData
/// </summary>
/// <exception cref="UnsupportedMechanismException"></exception>
/// <exception cref="InvalidCredentialsException"></exception>
/// <exception cref="AuthenticationFailedException"></exception>
private async Task _Authenticate(ConnectionData connectionData)
{
Dictionary<string, object> joinedProperties = new Dictionary<string, object>();
foreach(KeyValuePair<string, object> keyValuePair in connectionData.Properties)
{
joinedProperties.Add(keyValuePair.Key, keyValuePair.Value);
}
foreach (KeyValuePair<string, object> keyValuePair in connectionData.SecretProperties)
{
joinedProperties.Add(keyValuePair.Key, keyValuePair.Value);
}
await _APIConnection.Auth(MechanismString.ToString(connectionData.Mechanism), joinedProperties).ConfigureAwait(false);
} }
#endregion #endregion
} }

View File

@ -24,6 +24,12 @@ namespace FabAccessAPI
#endregion #endregion
#region Methods #region Methods
/// <summary>
/// Authenticate Connection to get Session
/// </summary>
/// <exception cref="BadMechanismException"></exception>
/// <exception cref="InvalidCredentialsException"></exception>
/// <exception cref="AuthenticationFailedException"></exception>
public async Task<Session> Authenticate(string mech, Dictionary<string, object> properties) public async Task<Session> Authenticate(string mech, Dictionary<string, object> properties)
{ {
SaslMechanism? saslMechanism = SaslFactory.Create(mech); SaslMechanism? saslMechanism = SaslFactory.Create(mech);
@ -44,17 +50,7 @@ namespace FabAccessAPI
{ {
if(response.Failed != null) if(response.Failed != null)
{ {
switch (response.Failed.Code) break;
{
case Response.Error.badMechanism:
throw new BadMechanismException();
case Response.Error.invalidCredentials:
throw new InvalidCredentialsException();
case Response.Error.aborted:
case Response.Error.failed:
default:
throw new AuthenticationFailedException(response.Failed.AdditionalData.ToArray());
}
} }
if(response.Challenge != null) if(response.Challenge != null)
{ {
@ -71,6 +67,20 @@ namespace FabAccessAPI
{ {
return response.Successful.Session; return response.Successful.Session;
} }
else if (response.Failed != null)
{
switch (response.Failed.Code)
{
case Response.Error.badMechanism:
throw new BadMechanismException();
case Response.Error.invalidCredentials:
throw new InvalidCredentialsException();
case Response.Error.aborted:
case Response.Error.failed:
default:
throw new AuthenticationFailedException(response.Failed.AdditionalData.ToArray());
}
}
else else
{ {
throw new AuthenticationFailedException(); throw new AuthenticationFailedException();

View File

@ -41,7 +41,9 @@ namespace FabAccessAPI
/// </summary> /// </summary>
/// <param name="mech">The desired authentication mechanism</param> /// <param name="mech">The desired authentication mechanism</param>
/// <param name="kvs">Key-Value data specific to the mechanism</param> /// <param name="kvs">Key-Value data specific to the mechanism</param>
/// <returns></returns> /// <exception cref="UnsupportedMechanismException"></exception>
/// <exception cref="InvalidCredentialsException"></exception>
/// <exception cref="AuthenticationFailedException"></exception>
public async Task Auth(string mech, Dictionary<string, object> kvs, CancellationToken cancellationToken_ = default) public async Task Auth(string mech, Dictionary<string, object> kvs, CancellationToken cancellationToken_ = default)
{ {
IReadOnlyList<string>? mechs = await _BootstrapCap.Mechanisms(); IReadOnlyList<string>? mechs = await _BootstrapCap.Mechanisms();

View File

@ -1,5 +1,6 @@
using FabAccessAPI.Schema; using FabAccessAPI.Schema;
using System; using System;
using System.Threading.Tasks;
namespace FabAccessAPI namespace FabAccessAPI
{ {
@ -35,17 +36,17 @@ namespace FabAccessAPI
/// Connect to BFFH Server /// Connect to BFFH Server
/// </summary> /// </summary>
/// <param name="connectionData"></param> /// <param name="connectionData"></param>
void Connect(ConnectionData connectionData); Task Connect(ConnectionData connectionData);
/// <summary> /// <summary>
/// Disconnect from BFFH Server /// Disconnect from BFFH Server
/// </summary> /// </summary>
void Disconnect(); Task Disconnect();
/// <summary> /// <summary>
/// Reconnect after connection loss with the last ConnectionData /// Reconnect after connection loss with the last ConnectionData
/// </summary> /// </summary>
void Reconnect(); Task Reconnect();
/// <summary> /// <summary>
/// Connect to Server and get ConnectionInfo. /// Connect to Server and get ConnectionInfo.

View File

@ -1,7 +1,24 @@
namespace FabAccessAPI using System;
namespace FabAccessAPI
{ {
public enum Mechanism public enum Mechanism
{ {
PLAIN PLAIN,
} }
public static class MechanismString
{
public static string ToString(Mechanism mechanism)
{
switch(mechanism)
{
case Mechanism.PLAIN:
return "PLAIN";
default:
throw new ArgumentException("Mechanism not known.");
}
}
}
} }

View File

@ -4,13 +4,14 @@ using FabAccessAPI.Exceptions;
using NUnit.Framework; using NUnit.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace FabAccessAPI_Test namespace FabAccessAPI_Test
{ {
public class API_Test public class API_Test
{ {
[TestCase("Admin1")] [TestCase("Admin1")]
public void ConnectDisconnect(string username) public async Task ConnectDisconnect(string username)
{ {
API api = new API(); API api = new API();
@ -29,12 +30,12 @@ namespace FabAccessAPI_Test
} }
}; };
api.Connect(connectionData); await api.Connect(connectionData);
api.Disconnect(); await api.Disconnect();
} }
[Test] [Test]
public void Connect_HostUnreachable() public async Task Connect_HostUnreachable()
{ {
API api = new API(); API api = new API();
@ -53,14 +54,19 @@ namespace FabAccessAPI_Test
} }
}; };
Assert.Throws<ConnectingFailedException>(() => try
{ {
api.Connect(connectionData); await api.Connect(connectionData);
}); }
catch (ConnectingFailedException)
{
Assert.Pass();
}
Assert.Fail();
} }
[Test] [Test]
public void Connect_InvalidCredentials() public async Task Connect_InvalidCredentials()
{ {
API api = new API(); API api = new API();
@ -79,10 +85,15 @@ namespace FabAccessAPI_Test
} }
}; };
Assert.Throws<InvalidCredentialsException>(() => try
{ {
api.Connect(connectionData); await api.Connect(connectionData);
}); }
catch(InvalidCredentialsException)
{
Assert.Pass();
}
Assert.Fail();
} }
} }
} }