Merge branch 'fix/machinepagestability' into 'main'

API v0.3

See merge request fabinfra/fabaccess/borepin!45
This commit is contained in:
TheJoKlLa 2022-03-15 23:43:49 +00:00
commit beb295db4e
16 changed files with 253 additions and 228 deletions

View File

@ -2,3 +2,4 @@
# MA0003: Add argument name to improve readability # MA0003: Add argument name to improve readability
dotnet_diagnostic.MA0003.severity = none dotnet_diagnostic.MA0003.severity = none
csharp_style_prefer_switch_expression=false:suggestion

View File

@ -31,6 +31,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
.gitlab-ci.yml = .gitlab-ci.yml .gitlab-ci.yml = .gitlab-ci.yml
.gitmodules = .gitmodules
EndProjectSection EndProjectSection
EndProject EndProject
Global Global

View File

@ -30,7 +30,7 @@ namespace Borepin.Model
Manager = new UserVisualize(_Machine.Manager); Manager = new UserVisualize(_Machine.Manager);
Manager.LoadData(); Manager.LoadData();
MachineInfoExtended machineInfoExtended = (await _Machine.Info.GetMachineInfoExtended().ConfigureAwait(false)).Item1; MachineInfoExtended machineInfoExtended = await _Machine.Manage.GetMachineInfoExtended().ConfigureAwait(false);
if(machineInfoExtended != null) if(machineInfoExtended != null)
{ {
if (machineInfoExtended.CurrentUser == null || machineInfoExtended.CurrentUser.Username == null) if (machineInfoExtended.CurrentUser == null || machineInfoExtended.CurrentUser.Username == null)
@ -43,13 +43,13 @@ namespace Borepin.Model
CurrentUser.LoadData(); CurrentUser.LoadData();
} }
if (machineInfoExtended.TransferUser == null || machineInfoExtended.TransferUser.Username == null) if (machineInfoExtended.LastUser == null || machineInfoExtended.LastUser.Username == null)
{ {
LastUser = null; LastUser = null;
} }
else else
{ {
LastUser = new UserVisualize(machineInfoExtended.TransferUser); LastUser = new UserVisualize(machineInfoExtended.LastUser);
LastUser.LoadData(); LastUser.LoadData();
} }
} }
@ -61,7 +61,7 @@ namespace Borepin.Model
CanUse = !((UseInterface_Proxy)_Machine.Use).IsNull; CanUse = !((UseInterface_Proxy)_Machine.Use).IsNull;
CanInUse = !((InUseInterface_Proxy) _Machine.Inuse).IsNull; CanInUse = !((InUseInterface_Proxy) _Machine.Inuse).IsNull;
CanTransfer = !((TransferInterface_Proxy) _Machine.Transfer).IsNull; CanTakeOver = !((TakeoverInterface_Proxy) _Machine.Takeover).IsNull;
CanCheck = !((CheckInterface_Proxy) _Machine.Check).IsNull; CanCheck = !((CheckInterface_Proxy) _Machine.Check).IsNull;
CanManage = !((ManageInterface_Proxy) _Machine.Manage).IsNull; CanManage = !((ManageInterface_Proxy) _Machine.Manage).IsNull;
CanAdmin = !((AdminInterface_Proxy) _Machine.Admin).IsNull; CanAdmin = !((AdminInterface_Proxy) _Machine.Admin).IsNull;
@ -140,7 +140,7 @@ namespace Borepin.Model
} }
private bool _CanTransfer; private bool _CanTransfer;
public bool CanTransfer public bool CanTakeOver
{ {
get => _CanTransfer; get => _CanTransfer;
set => SetProperty(ref _CanTransfer, value); set => SetProperty(ref _CanTransfer, value);

View File

@ -49,10 +49,10 @@ namespace Borepin.PageModel
IsConnected = true; IsConnected = true;
IMachineSystem machineSystem = await _BFFHService.GetMachineSystemInterface().ConfigureAwait(false); IMachineSystem machineSystem = (await _BFFHService.GetSession().ConfigureAwait(false)).MachineSystem;
MachineSystem.IInfoInterface machine_infoInterface = await machineSystem.Info().ConfigureAwait(false); MachineSystem.IInfoInterface machine_infoInterface = await machineSystem.Info().ConfigureAwait(false);
IUserSystem userSystem = await _BFFHService.GetUserSystemInterface().ConfigureAwait(false); IUserSystem userSystem = (await _BFFHService.GetSession().ConfigureAwait(false)).UserSystem;
UserSystem.IInfoInterface user_infoInterface = await userSystem.Info().ConfigureAwait(false); UserSystem.IInfoInterface user_infoInterface = await userSystem.Info().ConfigureAwait(false);
User user_self = (await user_infoInterface.GetUserSelf().ConfigureAwait(false)).Item1; User user_self = (await user_infoInterface.GetUserSelf().ConfigureAwait(false)).Item1;
@ -66,7 +66,7 @@ namespace Borepin.PageModel
{ {
MachineListItemViewModel new_viewmodel = new MachineListItemViewModel(machine) MachineListItemViewModel new_viewmodel = new MachineListItemViewModel(machine)
{ {
IsUserAssigned = true IsUserAssigned = true,
}; };
viewmodel_list_user_assigned.Add(new_viewmodel); viewmodel_list_user_assigned.Add(new_viewmodel);
} }
@ -156,10 +156,10 @@ namespace Borepin.PageModel
return; return;
} }
IMachineSystem machineInterface = await _BFFHService.GetMachineSystemInterface().ConfigureAwait(false); IMachineSystem machineInterface = (await _BFFHService.GetSession().ConfigureAwait(false)).MachineSystem;
MachineSystem.IInfoInterface infoInterface = await machineInterface.Info().ConfigureAwait(false); MachineSystem.IInfoInterface infoInterface = await machineInterface.Info().ConfigureAwait(false);
Machine machine = (await infoInterface.GetMachineURN(result.Parameters.GetValue<string>("value")).ConfigureAwait(false)).Item1; Machine machine = await infoInterface.GetMachineURN(result.Parameters.GetValue<string>("value")).ConfigureAwait(false);
if(machine == null) if(machine == null)
{ {

View File

@ -43,7 +43,7 @@ namespace Borepin.PageModel
IMachineSystem machineSystem; IMachineSystem machineSystem;
try try
{ {
machineSystem = await _BFFHService.GetMachineSystemInterface().ConfigureAwait(false); machineSystem = (await _BFFHService.GetSession().ConfigureAwait(false)).MachineSystem;
} }
catch(ReconnectingFailedException) catch(ReconnectingFailedException)
{ {
@ -54,7 +54,7 @@ namespace Borepin.PageModel
IInfoInterface info = await machineSystem.Info().ConfigureAwait(false); IInfoInterface info = await machineSystem.Info().ConfigureAwait(false);
_Machine = (await info.GetMachine(_ID).ConfigureAwait(false)).Item1; _Machine = await info.GetMachine(_ID).ConfigureAwait(false);
MachineItem = new MachineVisualize(_Machine); MachineItem = new MachineVisualize(_Machine);
MachineItem.LoadData(); MachineItem.LoadData();

View File

@ -8,6 +8,11 @@ using Borepin.Service.Storage;
using Borepin.Model.Storage; using Borepin.Model.Storage;
using Borepin.Service.BFFH.Exceptions; using Borepin.Service.BFFH.Exceptions;
using Borepin.Service.Storage.Exceptions; using Borepin.Service.Storage.Exceptions;
using Capnp.Rpc;
using FabAccessAPI.Exceptions;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Security.Authentication;
namespace Borepin.Service.BFFH namespace Borepin.Service.BFFH
{ {
@ -59,7 +64,7 @@ namespace Borepin.Service.BFFH
} }
#endregion #endregion
#region Methods #region Method
/// <summary> /// <summary>
/// Get all known Connections from Storage /// Get all known Connections from Storage
/// </summary> /// </summary>
@ -104,17 +109,16 @@ namespace Borepin.Service.BFFH
{ {
try try
{ {
Capnp.Rpc.TcpRpcClient rpcClient = new Capnp.Rpc.TcpRpcClient();
rpcClient.Connect(connection.Address.Host, connection.Address.Port); TcpRpcClient rpcClient = await _ConnectAsync(connection.Address.Host, connection.Address.Port).ConfigureAwait(false);
await rpcClient.WhenConnected.ConfigureAwait(false);
rpcClient.Dispose(); rpcClient.Dispose();
return true;
} }
catch catch
{ {
return false; return false;
} }
return true;
} }
/// <summary> /// <summary>
@ -146,11 +150,11 @@ namespace Borepin.Service.BFFH
try try
{ {
Capnp.Rpc.TcpRpcClient rpcClient = await _ConnectAsync(connection.Address.Host, connection.Address.Port).ConfigureAwait(false); TcpRpcClient rpcClient = await _ConnectAsync(connection.Address.Host, connection.Address.Port).ConfigureAwait(false);
_APIConnection = new FabAccessAPI.Connection(rpcClient); _APIConnection = new FabAccessAPI.Connection(rpcClient);
} }
catch (Capnp.Rpc.RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal)) catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
{ {
throw new ConnectingFailedException("Connecting failed", exception); throw new ConnectingFailedException("Connecting failed", exception);
} }
@ -185,11 +189,11 @@ namespace Borepin.Service.BFFH
try try
{ {
Capnp.Rpc.TcpRpcClient rpcClient = await _ConnectAsync(connection.Address.Host, connection.Address.Port).ConfigureAwait(false); TcpRpcClient rpcClient = await _ConnectAsync(connection.Address.Host, connection.Address.Port).ConfigureAwait(false);
_APIConnection = new FabAccessAPI.Connection(rpcClient); _APIConnection = new FabAccessAPI.Connection(rpcClient);
} }
catch (Capnp.Rpc.RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal)) catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
{ {
throw new ConnectingFailedException("Connecting failed", exception); throw new ConnectingFailedException("Connecting failed", exception);
} }
@ -231,11 +235,11 @@ namespace Borepin.Service.BFFH
try try
{ {
Capnp.Rpc.TcpRpcClient rpcClient = await _ConnectAsync(_CurrentConnection.Address.Host, _CurrentConnection.Address.Port).ConfigureAwait(false); TcpRpcClient rpcClient = await _ConnectAsync(_CurrentConnection.Address.Host, _CurrentConnection.Address.Port).ConfigureAwait(false);
_APIConnection = new FabAccessAPI.Connection(rpcClient); _APIConnection = new FabAccessAPI.Connection(rpcClient);
} }
catch (Capnp.Rpc.RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal)) 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)); throw new ReconnectingFailedException("Connecting failed", new ConnectingFailedException("Connecting failed", exception));
} }
@ -264,51 +268,62 @@ namespace Borepin.Service.BFFH
} }
#region FabAccess API Systems #region FabAccess API Systems
public async Task<IMachineSystem> GetMachineSystemInterface() public async Task<Session> GetSession()
{ {
if (!IsConnected) if (!IsConnected)
{ {
await Reconnect().ConfigureAwait(false); await Reconnect().ConfigureAwait(false);
} }
return await _APIConnection.AccessMachineSystem().ConfigureAwait(false); return _APIConnection.Session;
}
public async Task<IUserSystem> GetUserSystemInterface()
{
if (!IsConnected)
{
await Reconnect().ConfigureAwait(false);
}
return await _APIConnection.AccessUserSystem().ConfigureAwait(false);
}
public async Task<IPermissionSystem> GetPermissionSystemInterface()
{
if (!IsConnected)
{
await Reconnect().ConfigureAwait(false);
}
return await _APIConnection.AccessPermissionSystem().ConfigureAwait(false);
} }
#endregion #endregion
#endregion #endregion
#region Private Methods #region Private Methods
private static async Task<Capnp.Rpc.TcpRpcClient> _ConnectAsync(string host, int port) private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{ {
Capnp.Rpc.TcpRpcClient rpcClient = new Capnp.Rpc.TcpRpcClient(); return true;
}
private async Task<TcpRpcClient> _ConnectAsync(string host, int port)
{
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;
}
});
rpcClient.Connect(host, port); rpcClient.Connect(host, port);
await rpcClient.WhenConnected.ConfigureAwait(false); await rpcClient.WhenConnected.ConfigureAwait(false);
return rpcClient; return rpcClient;
} }
private Task<bool> _AuthenticatePlainAsync(string username, string password) private async Task<bool> _AuthenticatePlainAsync(string username, string password)
{ {
return _APIConnection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", password } }); try
{
await _APIConnection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", password } }).ConfigureAwait(false);
return await Task.FromResult(true).ConfigureAwait(false);
}
catch(InvalidCredentialsException)
{
return await Task.FromResult(true).ConfigureAwait(false);
}
catch (AuthenticatingFailedException)
{
return await Task.FromResult(true).ConfigureAwait(false);
}
} }
#endregion #endregion
} }

View File

@ -22,8 +22,6 @@ namespace Borepin.Service.BFFH
Task Disconnect(); Task Disconnect();
Task<IMachineSystem> GetMachineSystemInterface(); Task<Session> GetSession();
Task<IPermissionSystem> GetPermissionSystemInterface();
Task<IUserSystem> GetUserSystemInterface();
} }
} }

View File

@ -1,175 +1,81 @@
using FabAccessAPI.Schema; using FabAccessAPI.Schema;
using S22.Sasl; using S22.Sasl;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Exception = System.Exception; using FabAccessAPI.Exceptions;
using System.Linq;
namespace FabAccessAPI namespace FabAccessAPI
{ {
/// Authentication Identity
///
/// Under the hood a string because the form depends heavily on the method
public struct AuthCId
{
public string Id { get; private set; }
public AuthCId(string id) : this() { Id = id; }
}
/// Authorization Identity
///
/// This identity is internal to FabAccess and completely independent from the authentication
/// method or source
public struct AuthZId
{
/// Main User ID. Generally an user name or similar
public string Uid;
/// Sub user ID.
///
/// Can change scopes for permissions, e.g. having a +admin account with more permissions than
/// the default account and +dashboard et.al. accounts that have restricted permissions for
/// their applications
public string Subuid;
/// Realm this account originates.
///
/// The Realm is usually described by a domain name but local policy may dictate an unrelated
/// mapping
public string Realm;
}
/// Authentication/Authorization user object.
///
/// This struct contains the user as is passed to the actual authentication/authorization
/// subsystems
///
public struct AuthUser
{
/// Contains the Authentication ID used
///
/// The authentication ID is an identifier for the authentication exchange. This is different
/// than the ID of the user to be authenticated; for example when using x509 the authcid is
/// the dn of the certificate, when using GSSAPI the authcid is of form `<userid>@<REALM>`
public AuthCId Authcid;
/// Contains the Authorization ID
///
/// This is the identifier of the user to *authenticate as*. This in several cases is different
/// to the `authcid`:
/// If somebody wants to authenticate as somebody else, su-style.
/// If a person wants to authenticate as a higher-permissions account, e.g. foo may set authzid foo+admin
/// to split normal user and "admin" accounts.
/// If a method requires a specific authcid that is different from the identifier of the user
/// to authenticate as, e.g. GSSAPI, x509 client certificates, API TOKEN authentication.
public AuthZId Authzid;
/// Contains the authentication method used
///
/// For the most part this is the SASL method
public string AuthMethod;
/// Method-specific key-value pairs
///
/// Each method can use their own key-value pairs.
/// E.g. EXTERNAL encodes the actual method used (x509 client certs, UID/GID for unix sockets,
/// ...)
public Dictionary<string, string> Kvs;
}
// Authentication has two parts: Granting the authentication itself and then performing the
// authentication.
// Granting the authentication checks if
// a) the given authcid fits with the given (authMethod, kvs). In general a failure here indicates
// a programming failure — the authcid come from the same source as that tuple
// b) the given authcid may authenticate as the given authzid. E.g. if a given client certificate
// has been configured for that user, if a GSSAPI user maps to a given user,
public enum AuthError
{
/// Authentication ID is bad/unknown/..
BadAuthcid,
/// Authorization ID is unknown/..
BadAuthzid,
/// Authorization ID is not of form user+uid@realm
MalformedAuthzid,
/// User may not use that authorization id
NotAllowedAuthzid,
}
public class UnauthorizedException : Exception { }
public class UnsupportedMechanismException : Exception { }
/// <summary> /// <summary>
/// THIS IS VERY INCOMPLETE! /// Authenticate with SASL
/// </summary> /// </summary>
public class Auth public class Auth
{ {
#region Log #region Private Fields
//private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private readonly IAuthentication _AuthCap;
#endregion #endregion
private readonly IAuthenticationSystem _AuthCap; #region Constructors
public Auth(IAuthenticationSystem authCap) public Auth(IAuthentication authCap)
{ {
_AuthCap = authCap; _AuthCap = authCap;
} }
#endregion
public Task<IReadOnlyList<string>> GetMechanisms() #region Methods
public async Task<Session> Authenticate(string mech, Dictionary<string, object> properties)
{ {
return _AuthCap.Mechanisms(); SaslMechanism? saslMechanism = SaslFactory.Create(mech);
}
public async Task<bool> Authenticate(string mech, Dictionary<string, object> properties)
{
SaslMechanism? m = SaslFactory.Create(mech);
foreach (KeyValuePair<string, object> entry in properties) foreach (KeyValuePair<string, object> entry in properties)
{ {
m.Properties.Add(entry.Key, entry.Value); saslMechanism.Properties.Add(entry.Key, entry.Value);
} }
Request.initialResponse? initialResponse = new Request.initialResponse(); byte[] data = new byte[0];
if (m.HasInitial)
if (saslMechanism.HasInitial)
{ {
initialResponse.Initial = m.GetResponse(new byte[0]); data = saslMechanism.GetResponse(new byte[0]);
} }
Request? req = new Request Response? response = await _AuthCap.Step(data);
while (!saslMechanism.IsCompleted)
{ {
Mechanism = m.Name, if(response.Failed != null)
InitialResponse = initialResponse
};
Response? resp = await _AuthCap.Start(req);
while (!m.IsCompleted)
{
if (resp.which == Response.WHICH.Challence)
{ {
byte[]? additional = m.GetResponse(resp.Challence.ToArray()); switch (response.Failed.Code)
resp = await _AuthCap.Step(additional); {
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)
{
byte[]? additional = saslMechanism.GetResponse(response.Challenge.ToArray());
response = await _AuthCap.Step(additional);
} }
else else
{ {
break; throw new AuthenticationFailedException();
} }
} }
if (resp.which == Response.WHICH.Outcome) if (response.Successful != null)
{ {
if (resp.Outcome.Result == Response.Result.successful) return response.Successful.Session;
{ }
return true; else
} {
else throw new AuthenticationFailedException();
{
//TODO: Provide meaningful info about auth failure
return false;
}
} }
return false;
} }
#endregion
} }
} }

View File

@ -1,4 +1,5 @@
using Capnp.Rpc; using Capnp.Rpc;
using FabAccessAPI.Exceptions;
using FabAccessAPI.Schema; using FabAccessAPI.Schema;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -24,12 +25,13 @@ namespace FabAccessAPI
{ {
RpcClient = rpcClient; RpcClient = rpcClient;
_BootstrapCap = RpcClient.GetMain<IBootstrap>(); _BootstrapCap = RpcClient.GetMain<IBootstrap>();
//_Log.Debug($"Done bootstraping API connection.");
} }
#endregion #endregion
#region Fields #region Fields
public TcpRpcClient? RpcClient { get; } = null; public TcpRpcClient? RpcClient { get; } = null;
public Session? Session { get; private set; } = null;
#endregion #endregion
#region Methods #region Methods
@ -40,48 +42,21 @@ namespace FabAccessAPI
/// <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> /// <returns></returns>
public async Task<bool> Auth(string mech, Dictionary<string, object> kvs, CancellationToken cancellationToken_ = default) public async Task Auth(string mech, Dictionary<string, object> kvs, CancellationToken cancellationToken_ = default)
{ {
if(_Auth == null) IReadOnlyList<string>? mechs = await _BootstrapCap.Mechanisms();
{
IAuthenticationSystem? authCap = await _BootstrapCap.AuthenticationSystem(cancellationToken_).ConfigureAwait(false);
_Auth = new Auth(authCap);
}
IReadOnlyList<string>? mechs = await _Auth.GetMechanisms();
if (!mechs.Contains(mech)) if (!mechs.Contains(mech))
{ {
throw new UnsupportedMechanismException(); throw new UnsupportedMechanismException();
} }
return await _Auth.Authenticate(mech, kvs); if (_Auth == null)
} {
IAuthentication? authCap = await _BootstrapCap.CreateSession(mech, cancellationToken_).ConfigureAwait(false);
_Auth = new Auth(authCap);
}
/// <summary> Session = await _Auth.Authenticate(mech, kvs);
/// Get a wrapped capability to interact with machines
/// </summary>
/// <returns>A wrapped capability to interact with machines</returns>
public async Task<IMachineSystem> AccessMachineSystem()
{
return await _BootstrapCap.MachineSystem();
}
/// <summary>
/// Get a wrapped capability to interact with users
/// </summary>
/// <returns>A wrapped capability to interact with users</returns>
public async Task<IUserSystem> AccessUserSystem()
{
return await _BootstrapCap.UserSystem();
}
/// <summary>
/// Get a wrapped capability to interact with permissions
/// </summary>
/// <returns>A wrapped capability to interact with permissions</returns>
public async Task<IPermissionSystem> AccessPermissionSystem()
{
return await _BootstrapCap.PermissionSystem();
} }
#endregion #endregion
} }

View File

@ -0,0 +1,28 @@
using System;
namespace FabAccessAPI.Exceptions
{
public class AuthenticationFailedException : Exception
{
#region Constructors
public AuthenticationFailedException(byte[] additionalData = null)
{
AdditionalData = additionalData;
}
public AuthenticationFailedException(byte[] additionalData, string message) : base(message)
{
AdditionalData = additionalData;
}
public AuthenticationFailedException(byte[] additionalData, string message, Exception inner) : base(message, inner)
{
AdditionalData = additionalData;
}
#endregion
#region Fields
public byte[]? AdditionalData { get; }
#endregion
}
}

View File

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

View File

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

View File

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

@ -1 +1 @@
Subproject commit c855646a90958ae575d58be074d187acb9f8f4fa Subproject commit c9283ebd696ed6dd428a7c3d24820889f7ab4bf3

View File

@ -0,0 +1,35 @@
using Capnp.Rpc;
using FabAccessAPI;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FabAccessAPI_Test
{
public class Connection_Test
{
[TestCase("test.fab-access.org", 59661)]
public async Task Connect(string host, int port)
{
TcpRpcClient tcpRpcClient = new TcpRpcClient();
tcpRpcClient.Connect(host, port);
await tcpRpcClient.WhenConnected;
Connection connection = new Connection(tcpRpcClient);
}
[TestCase("test.fab-access.org", 59661, "Testuser", "secret")]
public async Task Authenticate_PLAIN(string host, int port, string username, string password)
{
TcpRpcClient tcpRpcClient = new TcpRpcClient();
tcpRpcClient.Connect(host, port);
await tcpRpcClient.WhenConnected;
Connection connection = new Connection(tcpRpcClient);
await connection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", password } });
Assert.IsNotNull(connection.Session);
}
}
}

@ -1 +1 @@
Subproject commit 1ba1b6fbe0d9a278445ef7fde7624f218171156b Subproject commit 086bbc2497785d2cc63e9252df6f6d3ee7599579