diff --git a/API.sln b/API.sln
new file mode 100644
index 0000000..30cc012
--- /dev/null
+++ b/API.sln
@@ -0,0 +1,54 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34607.119
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FabAccessAPI", "FabAccessAPI\FabAccessAPI.csproj", "{1349C60B-AEDC-4D87-BDE3-68A9E040C6B6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FabAccessAPI_Test", "FabAccessAPI_Test\FabAccessAPI_Test.csproj", "{784C376D-6F28-46F5-AFA7-71DC7F764609}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime", "external\capnproto-dotnetcore_Runtime\Capnp.Net.Runtime\Capnp.Net.Runtime.csproj", "{CA36D5C1-A477-469D-82F7-E70977708FE9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S22.Sasl", "external\S22.Sasl\S22.Sasl.csproj", "{63DF5EBE-236A-4586-A2DE-95A0A40D1E87}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{F74FFCEE-898F-4C38-BC49-395475147DED}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F3CE8F3-459B-44FD-A52B-76A55B1EB028}"
+ ProjectSection(SolutionItems) = preProject
+ ..\borepin_tmp\.editorconfig = ..\borepin_tmp\.editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1349C60B-AEDC-4D87-BDE3-68A9E040C6B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1349C60B-AEDC-4D87-BDE3-68A9E040C6B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1349C60B-AEDC-4D87-BDE3-68A9E040C6B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1349C60B-AEDC-4D87-BDE3-68A9E040C6B6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {784C376D-6F28-46F5-AFA7-71DC7F764609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {784C376D-6F28-46F5-AFA7-71DC7F764609}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {784C376D-6F28-46F5-AFA7-71DC7F764609}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {784C376D-6F28-46F5-AFA7-71DC7F764609}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CA36D5C1-A477-469D-82F7-E70977708FE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CA36D5C1-A477-469D-82F7-E70977708FE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CA36D5C1-A477-469D-82F7-E70977708FE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CA36D5C1-A477-469D-82F7-E70977708FE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {63DF5EBE-236A-4586-A2DE-95A0A40D1E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {63DF5EBE-236A-4586-A2DE-95A0A40D1E87}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {63DF5EBE-236A-4586-A2DE-95A0A40D1E87}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {63DF5EBE-236A-4586-A2DE-95A0A40D1E87}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {CA36D5C1-A477-469D-82F7-E70977708FE9} = {F74FFCEE-898F-4C38-BC49-395475147DED}
+ {63DF5EBE-236A-4586-A2DE-95A0A40D1E87} = {F74FFCEE-898F-4C38-BC49-395475147DED}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C60AAC74-8D10-421C-B051-908A1D5553BC}
+ EndGlobalSection
+EndGlobal
diff --git a/FabAccessAPI/API.cs b/FabAccessAPI/API.cs
new file mode 100644
index 0000000..63b15a5
--- /dev/null
+++ b/FabAccessAPI/API.cs
@@ -0,0 +1,506 @@
+using Capnp.Rpc;
+using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
+using FabAccessAPI.Schema;
+using NLog;
+using S22.Sasl;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI
+{
+ public class API : IAPI
+ {
+ #region Logger
+ private static readonly Logger Log = LogManager.GetCurrentClassLogger();
+ #endregion
+
+ #region Private Members
+ ///
+ /// Internal client to connect to a server with TCP and RPC
+ ///
+ private TcpRpcClient _TcpRpcClient;
+
+ ///
+ /// Private ConnectionData
+ ///
+ private ConnectionData _ConnectionData;
+
+ ///
+ /// Private ServerData
+ ///
+ private ServerData _ServerData;
+
+ ///
+ /// Private Session
+ ///
+ private Session _Session;
+
+ ///
+ /// Private Bootstrap
+ ///
+ private IBootstrap _Bootstrap;
+
+ ///
+ /// Timer to check connection status
+ ///
+ private readonly Timer _ConnectionHeatbeat;
+
+ ///
+ /// Semaphore to connect only once
+ ///
+ private static readonly SemaphoreSlim _ConnectSemaphore = new SemaphoreSlim(1, 1);
+ #endregion
+
+ #region Constructors
+
+ public API()
+ {
+ _ConnectionHeatbeat = new Timer(Heartbeat, null, 1000, 1000);
+ }
+ #endregion
+
+ #region Members
+ ///
+ /// State of the conneciton, can the API-Service connect to a server
+ ///
+ public bool CanConnect
+ {
+ get
+ {
+ return _ConnectionData != null;
+ }
+ }
+
+ ///
+ /// State of the conneciton, is the API-Service connecting to a server
+ ///
+ public bool IsConnecting
+ {
+ get
+ {
+ return _TcpRpcClient != null && _ConnectionData != null;
+ }
+ }
+
+ ///
+ /// State of the conneciton, is the API-Service connected to a server
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ return _TcpRpcClient != null && _TcpRpcClient.State == ConnectionState.Active;
+ }
+ }
+
+ ///
+ /// Information about the connection
+ ///
+ /// When API-Service is not connected or trying to connected to a server
+ public ConnectionData ConnectionData
+ {
+ get
+ {
+ if(_ConnectionData == null || !IsConnecting)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _ConnectionData;
+ }
+ }
+ private set
+ {
+ _ConnectionData = value;
+ }
+ }
+
+ ///
+ /// Information about the server
+ /// Is only avalible if the API-Service is connected
+ ///
+ /// When API-Service is not connected
+ public ServerData ServerData
+ {
+ get
+ {
+ if (_ServerData == null || !IsConnected)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _ServerData;
+ }
+ }
+ private set
+ {
+ _ServerData = value;
+ }
+ }
+ #endregion
+
+ #region Events
+ ///
+ /// Event on changes in connection status
+ ///
+ public event EventHandler ConnectionStatusChanged;
+
+ ///
+ /// Unbind all handlers from EventHandler
+ ///
+ public void UnbindEventHandler()
+ {
+ if (ConnectionStatusChanged != null)
+ {
+ Log.Trace("Eventhandlers unbinded");
+ foreach (Delegate d in ConnectionStatusChanged.GetInvocationList())
+ {
+ ConnectionStatusChanged -= (EventHandler)d;
+ }
+ }
+ }
+
+ ///
+ /// Eventhandler for TcpRpcConnectionChanged
+ /// Track connection loss and publish i in ConnectionStatusChanged
+ ///
+ public void OnTcpRpcConnectionChanged(object sender, ConnectionStateChange args)
+ {
+ if (args.LastState == ConnectionState.Active && args.NewState == ConnectionState.Down)
+ {
+ Log.Trace("TcpRpcClient Event ConnectionLoss");
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.ConnectionLoss);
+ }
+ }
+ #endregion
+
+ #region Session
+ ///
+ /// Get session after connection
+ ///
+ /// When API-Service is not connected
+ public Session Session
+ {
+ get
+ {
+ if (_Session == null || !IsConnected)
+ {
+ throw new InvalidOperationException();
+ }
+ else
+ {
+ return _Session;
+ }
+ }
+ private set
+ {
+ _Session = value;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+ ///
+ /// Connect to server with ConnectionData
+ /// If connection lost, the API-Server will try to reconnect
+ ///
+ /// Data to establish a connection to a server
+ /// When API-Service can not connect to a server
+ /// When API-Service can connect to a server but can not authenticate
+ /// When API-Service is allready connected
+ public async Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
+ {
+ await _ConnectSemaphore.WaitAsync();
+ try
+ {
+ if (IsConnected)
+ {
+ Log.Warn("API already connected");
+ throw new InvalidOperationException();
+ }
+
+ if (tcpRpcClient == null)
+ {
+ tcpRpcClient = new TcpRpcClient();
+ }
+
+ try
+ {
+ await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
+
+ _Bootstrap = tcpRpcClient.GetMain();
+ ServerData = await _GetServerData(_Bootstrap);
+
+ Session = await _Authenticate(connectionData).ConfigureAwait(false);
+ ConnectionData = connectionData;
+
+ _TcpRpcClient = tcpRpcClient;
+ tcpRpcClient.ConnectionStateChanged += OnTcpRpcConnectionChanged;
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Connected);
+ Log.Info("API connected");
+ }
+ catch (System.Exception ex)
+ {
+ Log.Warn(ex, "API connect failed");
+ throw ex;
+ }
+ }
+ finally
+ {
+ _ConnectSemaphore.Release();
+ }
+ }
+
+ ///
+ /// Disconnect from a server
+ ///
+ /// When API-Service is not connected or trying to connect
+ public Task Disconnect()
+ {
+ if (IsConnected)
+ {
+ _TcpRpcClient.Dispose();
+ }
+
+ _Bootstrap = null;
+ _TcpRpcClient = null;
+
+ Session = null;
+ ConnectionData = null;
+ ServerData = null;
+
+ ConnectionStatusChanged?.Invoke(this, FabAccessAPI.ConnectionStatusChanged.Disconnected);
+
+ Log.Info("API disconnected");
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Try to connect to a server and get ServerData
+ /// The connection is not maintained
+ ///
+ /// When API-Service can not connect to a server
+ public async Task TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null)
+ {
+ if (tcpRpcClient == null)
+ {
+ tcpRpcClient = new TcpRpcClient();
+ }
+
+ await _ConnectAsync(tcpRpcClient, connectionData).ConfigureAwait(false);
+ IBootstrap bootstrap = tcpRpcClient.GetMain();
+
+ ServerData serverData = await _GetServerData(bootstrap).ConfigureAwait(false);
+
+ tcpRpcClient.Dispose();
+
+ return serverData;
+ }
+
+ ///
+ /// Public Wrapper to run HeartbeatAsync
+ ///
+ public void Heartbeat(object state)
+ {
+ _ = _HeartbeatAsync();
+ }
+ #endregion
+
+ #region Private Methods
+
+
+ private async Task _HeartbeatAsync()
+ {
+ if(!IsConnected && CanConnect)
+ {
+ try
+ {
+ await Connect(ConnectionData).ConfigureAwait(false);
+ }
+ catch(AuthenticationException)
+ {
+ await Disconnect().ConfigureAwait(false);
+ }
+ }
+ }
+
+ ///
+ /// Validate Certificate
+ /// TODO: Do some validation
+ ///
+ private bool _RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+ // TODO Cert Check
+ return true;
+ }
+
+ ///
+ /// Injects SSL as Midlayer in TCPRPCConnection
+ ///
+ ///
+ private Stream _InjectSSL(Stream tcpstream)
+ {
+ SslStream sslStream = new SslStream(tcpstream, false, new RemoteCertificateValidationCallback(_RemoteCertificateValidationCallback));
+ try
+ {
+ sslStream.ReadTimeout = 5000;
+ sslStream.AuthenticateAsClient("bffhd");
+ sslStream.ReadTimeout = -1;
+
+ return sslStream;
+ }
+ catch (System.Security.Authentication.AuthenticationException exception)
+ {
+ sslStream.Close();
+ Log.Warn(exception);
+ throw new ConnectionException("TLS failed", exception);
+ }
+ catch(IOException exception)
+ {
+ sslStream.Close();
+ Log.Warn(exception);
+ throw new ConnectionException("TLS failed", new Exceptions.TimeoutException("TLS timeout", exception));
+ }
+ }
+
+ ///
+ /// Connect async to a server with ConnectionData
+ ///
+ /// Based on RPC Exception
+ private async Task _ConnectAsync(TcpRpcClient tcprpcClient, ConnectionData connectionData)
+ {
+ tcprpcClient.InjectMidlayer(_InjectSSL);
+
+ try
+ {
+ Task timeoutTask = Task.Delay(5000);
+ tcprpcClient.Connect(connectionData.Host.Host, connectionData.Host.Port);
+ await await Task.WhenAny(tcprpcClient.WhenConnected, timeoutTask);
+
+ if (timeoutTask.IsCompleted)
+ {
+ Exceptions.TimeoutException timeoutException = new Exceptions.TimeoutException();
+ Log.Warn(timeoutException);
+ throw new ConnectionException("Connection timeout", timeoutException);
+ }
+ }
+ catch (RpcException exception) when (string.Equals(exception.Message, "TcpRpcClient is unable to connect", StringComparison.Ordinal))
+ {
+ Log.Warn(exception);
+ throw new ConnectionException("RPC Connecting failed", exception);
+ }
+ }
+
+ ///
+ /// Authenticate connection with ConnectionData
+ ///
+ /// Data to establish a connection to a server
+ ///
+ private async Task _Authenticate(ConnectionData connectionData)
+ {
+ IAuthentication? authentication = await _Bootstrap.CreateSession(SASLMechanism.ToString(connectionData.Mechanism)).ConfigureAwait(false);
+
+ try
+ {
+ return await _SASLAuthenticate(authentication, SASLMechanism.ToString(connectionData.Mechanism), connectionData.Properties).ConfigureAwait(false);
+ }
+ catch (System.Exception exception)
+ {
+ Log.Warn(exception, "API authenticating failed");
+ AuthenticationException authenticationException = new AuthenticationException("Authentication failed", exception);
+
+ throw authenticationException;
+ }
+ }
+
+ ///
+ /// Authenticate with SASL
+ ///
+ ///
+ ///
+ ///
+ private async Task _SASLAuthenticate(IAuthentication authentication, string mech, Dictionary properties)
+ {
+ SaslMechanism? saslMechanism = SaslFactory.Create(mech);
+ foreach (KeyValuePair entry in properties)
+ {
+ saslMechanism.Properties.Add(entry.Key, entry.Value);
+ }
+
+ byte[] data = new byte[0];
+
+ if (saslMechanism.HasInitial)
+ {
+ data = saslMechanism.GetResponse(new byte[0]);
+ }
+
+ Response? response = await authentication.Step(data);
+ while (!saslMechanism.IsCompleted)
+ {
+ if (response.Failed != null)
+ {
+ break;
+ }
+ if (response.Challenge != null)
+ {
+ byte[]? additional = saslMechanism.GetResponse(response.Challenge.ToArray());
+ response = await authentication.Step(additional);
+ }
+ else
+ {
+ throw new AuthenticationFailedException();
+ }
+ }
+
+ if (response.Successful != null)
+ {
+ 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
+ {
+ throw new AuthenticationFailedException();
+ }
+ }
+
+ ///
+ /// Get ServerData from server with tcprpcconnection
+ ///
+ private async Task _GetServerData(IBootstrap bootstrap)
+ {
+ ServerData serverData = new ServerData()
+ {
+ APIVersion = await bootstrap.GetAPIVersion().ConfigureAwait(false),
+ Mechanisms = new List(await bootstrap.Mechanisms().ConfigureAwait(false)),
+ ServerName = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item1,
+ ServerRelease = (await bootstrap.GetServerRelease().ConfigureAwait(false)).Item2,
+ };
+
+ return serverData;
+ }
+ #endregion
+ }
+}
diff --git a/FabAccessAPI/ConnectionData.cs b/FabAccessAPI/ConnectionData.cs
new file mode 100644
index 0000000..f4c2bd8
--- /dev/null
+++ b/FabAccessAPI/ConnectionData.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+namespace FabAccessAPI
+{
+ ///
+ /// Data to establish a connection to a server
+ /// With Data for Authentication
+ ///
+ public class ConnectionData
+ {
+ public Uri Host;
+ public string Username;
+
+ public SASLMechanismEnum Mechanism;
+ public Dictionary Properties;
+
+ public DateTime LastTime;
+
+ public override bool Equals(object? obj)
+ {
+ if(obj is ConnectionData && obj != null)
+ {
+ ConnectionData? data = obj as ConnectionData;
+
+ return data.Host.Host == Host.Host &&
+ data.Host.Port == Host.Port &&
+ data.Mechanism == Mechanism &&
+ data.Username == Username;
+ }
+
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = -1151110446;
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Host);
+ hashCode = hashCode * -1521134295 + Mechanism.GetHashCode();
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Username);
+ hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(Properties);
+ return hashCode;
+ }
+ }
+}
diff --git a/FabAccessAPI/ConnectionStatusChanged.cs b/FabAccessAPI/ConnectionStatusChanged.cs
new file mode 100644
index 0000000..44cc80e
--- /dev/null
+++ b/FabAccessAPI/ConnectionStatusChanged.cs
@@ -0,0 +1,20 @@
+namespace FabAccessAPI
+{
+ public enum ConnectionStatusChanged
+ {
+ ///
+ /// API-Service has established connection to server
+ ///
+ Connected,
+
+ ///
+ /// API-Service has closed the connection to a server
+ ///
+ Disconnected,
+
+ ///
+ /// Connection was lost and the API-Service will try to reconnect automatically
+ ///
+ ConnectionLoss
+ }
+}
diff --git a/FabAccessAPI/Exceptions/APIIncompatibleException.cs b/FabAccessAPI/Exceptions/APIIncompatibleException.cs
new file mode 100644
index 0000000..24f91ff
--- /dev/null
+++ b/FabAccessAPI/Exceptions/APIIncompatibleException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ public class APIIncompatibleException : Exception
+ {
+ public APIIncompatibleException()
+ {
+
+ }
+
+ public APIIncompatibleException(string message) : base(message)
+ {
+
+ }
+
+ public APIIncompatibleException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/AuthenticationException.cs b/FabAccessAPI/Exceptions/AuthenticationException.cs
new file mode 100644
index 0000000..5b3eb33
--- /dev/null
+++ b/FabAccessAPI/Exceptions/AuthenticationException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Authenticating to a server has failed
+ /// InnerException will provide more information
+ ///
+ public class AuthenticationException : Exception
+ {
+ public AuthenticationException()
+ {
+
+ }
+
+ public AuthenticationException(string message) : base(message)
+ {
+
+ }
+
+ public AuthenticationException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/ConnectionException.cs b/FabAccessAPI/Exceptions/ConnectionException.cs
new file mode 100644
index 0000000..e018c2b
--- /dev/null
+++ b/FabAccessAPI/Exceptions/ConnectionException.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Connecting to a server has failed
+ /// InnerException will provide more information
+ ///
+ public class ConnectionException : Exception
+ {
+ public ConnectionException()
+ {
+
+ }
+
+ public ConnectionException(string message) : base(message)
+ {
+
+ }
+
+ public ConnectionException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/ReconnectingFailedException.cs b/FabAccessAPI/Exceptions/ReconnectingFailedException.cs
new file mode 100644
index 0000000..4d4b810
--- /dev/null
+++ b/FabAccessAPI/Exceptions/ReconnectingFailedException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ public class ReconnectingFailedException : Exception
+ {
+ public ReconnectingFailedException()
+ {
+
+ }
+
+ public ReconnectingFailedException(string message) : base(message)
+ {
+
+ }
+
+ public ReconnectingFailedException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs b/FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs
new file mode 100644
index 0000000..b31c304
--- /dev/null
+++ b/FabAccessAPI/Exceptions/SASL/AuthenticationFailedException.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace FabAccessAPI.Exceptions.SASL
+{
+ 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
+ }
+}
diff --git a/FabAccessAPI/Exceptions/SASL/BadMechanismException.cs b/FabAccessAPI/Exceptions/SASL/BadMechanismException.cs
new file mode 100644
index 0000000..0728be9
--- /dev/null
+++ b/FabAccessAPI/Exceptions/SASL/BadMechanismException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace FabAccessAPI.Exceptions.SASL
+{
+ public class BadMechanismException : Exception
+ {
+ public BadMechanismException()
+ {
+
+ }
+
+ public BadMechanismException(string message) : base(message)
+ {
+
+ }
+
+ public BadMechanismException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs b/FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs
new file mode 100644
index 0000000..060a731
--- /dev/null
+++ b/FabAccessAPI/Exceptions/SASL/InvalidCredentialsException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace FabAccessAPI.Exceptions.SASL
+{
+ public class InvalidCredentialsException : Exception
+ {
+ public InvalidCredentialsException()
+ {
+
+ }
+
+ public InvalidCredentialsException(string message) : base(message)
+ {
+
+ }
+
+ public InvalidCredentialsException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/TimeoutException.cs b/FabAccessAPI/Exceptions/TimeoutException.cs
new file mode 100644
index 0000000..2f65b1d
--- /dev/null
+++ b/FabAccessAPI/Exceptions/TimeoutException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace FabAccessAPI.Exceptions
+{
+ ///
+ /// Timeout on Connection
+ ///
+ public class TimeoutException : Exception
+ {
+ public TimeoutException()
+ {
+
+ }
+
+ public TimeoutException(string message) : base(message)
+ {
+
+ }
+
+ public TimeoutException(string message, Exception inner) : base(message, inner)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/Exceptions/UnsupportedMechanismException.cs b/FabAccessAPI/Exceptions/UnsupportedMechanismException.cs
new file mode 100644
index 0000000..1f62d09
--- /dev/null
+++ b/FabAccessAPI/Exceptions/UnsupportedMechanismException.cs
@@ -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)
+ {
+
+ }
+ }
+}
diff --git a/FabAccessAPI/FabAccessAPI.csproj b/FabAccessAPI/FabAccessAPI.csproj
new file mode 100644
index 0000000..39926f3
--- /dev/null
+++ b/FabAccessAPI/FabAccessAPI.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FabAccessAPI/IAPI.cs b/FabAccessAPI/IAPI.cs
new file mode 100644
index 0000000..56cc8a3
--- /dev/null
+++ b/FabAccessAPI/IAPI.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Threading.Tasks;
+using Capnp.Rpc;
+using FabAccessAPI.Exceptions;
+using FabAccessAPI.Schema;
+
+namespace FabAccessAPI
+{
+ ///
+ /// Service to connect to a server and maintain the connection
+ ///
+ public interface IAPI
+ {
+ #region Information about a connection and the server
+ ///
+ /// State of the conneciton, is the API-Service connecting to a server
+ ///
+ bool IsConnecting { get; }
+
+ ///
+ /// State of the conneciton, is the API-Service connected to a server
+ ///
+ bool IsConnected { get; }
+
+ ///
+ /// Information about the connection
+ ///
+ /// When API-Service is not connected or trying to connected to a server
+ ConnectionData ConnectionData { get; }
+
+ ///
+ /// Information about the server
+ /// Is only avalible if the API-Service is connected
+ ///
+ /// When API-Service is not connected
+ ServerData ServerData { get; }
+ #endregion
+
+ #region Methods to connect to server
+ ///
+ /// Connect to server with ConnectionData
+ /// If connection lost, the API-Server will try to reconnect
+ ///
+ /// Data to establish a connection to a server
+ /// When API-Service can not connect to a server
+ /// When API-Service can connect to a server but can not authenticate
+ /// When API-Service is allready connected
+ Task Connect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
+
+ ///
+ /// Disconnect from a server
+ ///
+ /// When API-Service is not connected or trying to connect
+ Task Disconnect();
+
+ ///
+ /// Try to connect to a server and get ServerData
+ /// The connection is not maintained
+ ///
+ /// When API-Service can not connect to a server
+ Task TryToConnect(ConnectionData connectionData, TcpRpcClient tcpRpcClient = null);
+ #endregion
+
+ #region Session
+ ///
+ /// Get session after connection
+ ///
+ /// When API-Service is not connected
+ Session Session { get; }
+ #endregion
+
+ #region Events
+ ///
+ /// Event on changes in connection status
+ ///
+ event EventHandler ConnectionStatusChanged;
+
+ ///
+ /// Unbind all handlers from EventHandler
+ ///
+ void UnbindEventHandler();
+ #endregion
+ }
+}
diff --git a/FabAccessAPI/SASLMechanismEnum.cs b/FabAccessAPI/SASLMechanismEnum.cs
new file mode 100644
index 0000000..cc12c7c
--- /dev/null
+++ b/FabAccessAPI/SASLMechanismEnum.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace FabAccessAPI
+{
+ public enum SASLMechanismEnum
+ {
+ PLAIN,
+ }
+
+ public static class SASLMechanism
+ {
+ public static string ToString(SASLMechanismEnum mechanism)
+ {
+ switch(mechanism)
+ {
+ case SASLMechanismEnum.PLAIN:
+ return "PLAIN";
+ default:
+ throw new ArgumentException("Mechanism unknown.");
+ }
+ }
+ }
+
+}
diff --git a/FabAccessAPI/ServerData.cs b/FabAccessAPI/ServerData.cs
new file mode 100644
index 0000000..9601820
--- /dev/null
+++ b/FabAccessAPI/ServerData.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+
+namespace FabAccessAPI
+{
+ public class ServerData
+ {
+ public Schema.Version APIVersion;
+ public string ServerName;
+ public string ServerRelease;
+ public List Mechanisms;
+ }
+}
diff --git a/FabAccessAPI_Test/API_Test.cs b/FabAccessAPI_Test/API_Test.cs
new file mode 100644
index 0000000..66e294c
--- /dev/null
+++ b/FabAccessAPI_Test/API_Test.cs
@@ -0,0 +1,174 @@
+using Capnp.Rpc;
+using FabAccessAPI;
+using FabAccessAPI.Exceptions;
+using FabAccessAPI.Exceptions.SASL;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test
+{
+ public class API_Test
+ {
+ [Test]
+ public async Task Connect_HostUnreachable()
+ {
+ API api = new API();
+
+ ConnectionData connectionData = new ConnectionData()
+ {
+ Host = new UriBuilder(TestEnv.SCHEMA, "UnkownHost" + TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
+ Mechanism = SASLMechanismEnum.PLAIN,
+ Username = "UnkownUser",
+ Properties = new Dictionary()
+ {
+ { "Username", "UnkownUser" },
+ { "Password", TestEnv.PASSWORD }
+ }
+ };
+
+ try
+ {
+ await api.Connect(connectionData);
+ }
+ catch (ConnectionException)
+ {
+ ClassicAssert.Pass();
+ }
+ ClassicAssert.Fail();
+ }
+
+ [Test]
+ public async Task Connect_InvalidCredentials()
+ {
+ API api = new API();
+
+ ConnectionData connectionData = TestEnv.CreateConnetionData("UnkownUser");
+
+ try
+ {
+ await api.Connect(connectionData);
+ }
+ catch(AuthenticationException exception) when (exception.InnerException is InvalidCredentialsException)
+ {
+ ClassicAssert.Pass();
+ }
+ ClassicAssert.Fail();
+ }
+
+ [TestCase("Admin1")]
+ public async Task ConnectDisconnect(string username)
+ {
+ API api = new API();
+
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+
+ bool event_Connected = false;
+ bool event_Disconnected = false;
+ api.ConnectionStatusChanged += (sender, eventArgs) =>
+ {
+ if (eventArgs == ConnectionStatusChanged.Connected)
+ {
+ event_Connected = true;
+ }
+ if(eventArgs == ConnectionStatusChanged.Disconnected)
+ {
+ event_Disconnected = true;
+ }
+ };
+
+ await api.Connect(connectionData);
+
+ bool HasSesion = api.Session != null;
+ bool HasConnectionData = api.ConnectionData != null;
+ bool HasServerData = api.ServerData != null;
+ bool IsConnected = api.IsConnected;
+
+ await api.Disconnect();
+
+ Thread.Sleep(3000);
+ ClassicAssert.Multiple(() =>
+ {
+ ClassicAssert.IsTrue(event_Connected, "event_Connected");
+ ClassicAssert.IsTrue(event_Disconnected, "event_Disconnected");
+
+ ClassicAssert.IsTrue(HasSesion, "HasSesion");
+ ClassicAssert.IsTrue(HasConnectionData, "HasConnectionData");
+ ClassicAssert.IsTrue(HasServerData, "HasServerData");
+ ClassicAssert.IsTrue(IsConnected, "IsConnected");
+
+ ClassicAssert.IsFalse(api.IsConnected, "api.IsConnected");
+ });
+ }
+
+ [TestCase("Admin1")]
+ public async Task TestConnectíon(string username)
+ {
+ API api = new API();
+
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+
+ ServerData serverData = await api.TryToConnect(connectionData);
+
+ ClassicAssert.IsNotNull(serverData);
+ }
+
+ [TestCase("Admin1"), Explicit]
+ public async Task Reconnect(string username)
+ {
+ API api = new API();
+
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+
+ int event_Connected = 0;
+ int event_ConnectionLoss = 0;
+ int event_Disconnected = 0;
+
+ api.ConnectionStatusChanged += (sender, eventArgs) =>
+ {
+ if (eventArgs == ConnectionStatusChanged.Connected)
+ {
+ event_Connected++;
+ }
+ if (eventArgs == ConnectionStatusChanged.ConnectionLoss)
+ {
+ event_ConnectionLoss++;
+ }
+ if (eventArgs == ConnectionStatusChanged.Disconnected)
+ {
+ event_Disconnected++;
+ }
+ };
+
+ TcpRpcClient tcpRpcClient = new TcpRpcClient();
+ await api.Connect(connectionData, tcpRpcClient);
+
+ try
+ {
+ // Stop here and cut internet connection
+ await api.Session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);
+ }
+ catch
+ {
+
+ }
+ Thread.Sleep(3000);
+
+ // Stop here and connect with internet again
+ await api.Disconnect();
+
+ Thread.Sleep(1000);
+ ClassicAssert.Multiple(() =>
+ {
+ ClassicAssert.AreEqual(2, event_Connected, "event_Connected");
+ ClassicAssert.AreEqual(1, event_ConnectionLoss, "event_ConnectionLoss");
+ ClassicAssert.AreEqual(1, event_Disconnected, "event_Disconnected");
+ });
+ }
+
+
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/MachineSystem_Test_Stateless.cs b/FabAccessAPI_Test/API_TestEnv/MachineSystem_Test_Stateless.cs
new file mode 100644
index 0000000..b16be5c
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/MachineSystem_Test_Stateless.cs
@@ -0,0 +1,199 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture, Parallelizable(ParallelScope.Children)]
+ [Order(1)]
+ public class MachineSystem_Test_Stateless
+ {
+ [TestCase("Admin1", true)]
+ [TestCase("Admin2", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("ManagerA2", true)]
+ [TestCase("ManagerB1", true)]
+ [TestCase("ManagerB2", true)]
+ [TestCase("ManagerC1", true)]
+ [TestCase("ManagerC2", true)]
+ [TestCase("ManagerABC1", true)]
+ [TestCase("ManagerABC2", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("MakerA2", true)]
+ [TestCase("MakerB1", true)]
+ [TestCase("MakerB2", true)]
+ [TestCase("MakerC1", true)]
+ [TestCase("MakerC2", true)]
+ [TestCase("MakerABC1", true)]
+ [TestCase("MakerABC2", true)]
+ [TestCase("GuestA1", true)]
+ [TestCase("GuestA2", true)]
+ [TestCase("GuestB1", true)]
+ [TestCase("GuestB2", true)]
+ [TestCase("GuestC1", true)]
+ [TestCase("GuestC2", true)]
+ [TestCase("GuestABC1", true)]
+ [TestCase("GuestABC2", true)]
+ [TestCase("MakerQRA", true)]
+ [TestCase("MakerQRB", true)]
+ [TestCase("MakerQRC", true)]
+ [Order(2)]
+ public async Task AccessMachineSystem_Info(string username, bool expectAllow)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ ClassicAssert.AreEqual(expectAllow, !((MachineSystem.InfoInterface_Proxy)api.Session.MachineSystem.Info).IsNull);
+ }
+
+ [TestCase("Admin1", 15)]
+ [TestCase("ManagerA1", 5)]
+ [TestCase("ManagerB1", 5)]
+ [TestCase("ManagerC1", 5)]
+ [TestCase("ManagerABC1", 15)]
+ [TestCase("MakerA1", 5)]
+ [TestCase("MakerB1", 5)]
+ [TestCase("MakerC1", 5)]
+ [TestCase("MakerABC1", 15)]
+ [TestCase("GuestA1", 5)]
+ [TestCase("GuestB1", 5)]
+ [TestCase("GuestC1", 5)]
+ [TestCase("GuestABC1", 15)]
+ [TestCase("MakerQRA", 0)]
+ [TestCase("MakerQRB", 0)]
+ [TestCase("MakerQRC", 0)]
+ [Order(3)]
+ public async Task ListMachines(string username, int expectedMachineCount)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ IReadOnlyList machine_list = await api.Session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);
+
+ int result = machine_list.Count;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectedMachineCount, result);
+ }
+
+ [TestCase("Admin1", "MachineA1", true)]
+ [TestCase("Admin1", "MachineB1", true)]
+ [TestCase("Admin1", "MachineC1", true)]
+ [TestCase("ManagerA1", "MachineA1", true)]
+ [TestCase("ManagerA1", "MachineB1", false)]
+ [TestCase("ManagerA1", "MachineC1", false)]
+ [TestCase("ManagerB1", "MachineA1", false)]
+ [TestCase("ManagerB1", "MachineB1", true)]
+ [TestCase("ManagerB1", "MachineC1", false)]
+ [TestCase("ManagerC1", "MachineA1", false)]
+ [TestCase("ManagerC1", "MachineB1", false)]
+ [TestCase("ManagerC1", "MachineC1", true)]
+ [TestCase("ManagerABC1", "MachineA1", true)]
+ [TestCase("ManagerABC1", "MachineB1", true)]
+ [TestCase("ManagerABC1", "MachineC1", true)]
+ [TestCase("MakerA1", "MachineA1", true)]
+ [TestCase("MakerB1", "MachineB1", true)]
+ [TestCase("MakerC1", "MachineC1", true)]
+ [TestCase("GuestA1", "MachineA1", true)]
+ [TestCase("GuestB1", "MachineB1", true)]
+ [TestCase("GuestC1", "MachineC1", true)]
+ [TestCase("MakerQRA", "MachineA1", true)]
+ [TestCase("MakerQRB", "MachineB1", true)]
+ [TestCase("MakerQRC", "MachineC1", true)]
+ [Order(4)]
+ public async Task GetMachineByName(string username, string machineName, bool expectedAllow)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Optional optional = await api.Session.MachineSystem.Info.GetMachine(machineName).ConfigureAwait(false);
+
+ bool result = optional.Just != null;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectedAllow, result);
+ }
+
+ [TestCase("Admin1", "MachineX")]
+ [TestCase("Admin1", "urn:fabaccess:resource:MachineA1")]
+ [Order(5)]
+ public async Task GetMachineByName_WrongName(string username, string machineName)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Optional optional = await api.Session.MachineSystem.Info.GetMachine(machineName).ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNull(optional.Just);
+ }
+
+ [TestCase("Admin1", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("Admin1", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("Admin1", "urn:fabaccess:resource:MachineC1", true)]
+ [TestCase("ManagerA1", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("ManagerA1", "urn:fabaccess:resource:MachineB1", false)]
+ [TestCase("ManagerA1", "urn:fabaccess:resource:MachineC1", false)]
+ [TestCase("ManagerB1", "urn:fabaccess:resource:MachineA1", false)]
+ [TestCase("ManagerB1", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("ManagerB1", "urn:fabaccess:resource:MachineC1", false)]
+ [TestCase("ManagerC1", "urn:fabaccess:resource:MachineA1", false)]
+ [TestCase("ManagerC1", "urn:fabaccess:resource:MachineB1", false)]
+ [TestCase("ManagerC1", "urn:fabaccess:resource:MachineC1", true)]
+ [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineC1", true)]
+ [TestCase("MakerA1", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("MakerB1", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("MakerC1", "urn:fabaccess:resource:MachineC1", true)]
+ [TestCase("GuestA1", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("GuestB1", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("GuestC1", "urn:fabaccess:resource:MachineC1", true)]
+ [TestCase("MakerQRA", "urn:fabaccess:resource:MachineA1", true)]
+ [TestCase("MakerQRB", "urn:fabaccess:resource:MachineB1", true)]
+ [TestCase("MakerQRC", "urn:fabaccess:resource:MachineC1", true)]
+
+ [Order(6)]
+ public async Task GetMachineByURN(string username, string urn, bool expectedAllow)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Optional optional = await api.Session.MachineSystem.Info.GetMachineURN(urn).ConfigureAwait(false);
+ bool result = optional.Just != null;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectedAllow, result);
+ }
+
+ [TestCase("Admin1", "urn:fabaccess:resource:MachineX")]
+ [TestCase("Admin1", "MachineA1")]
+ [TestCase("Admin1", "something")]
+ [Order(7)]
+ public async Task GetMachineByURN_WrongURN(string username, string urn)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Optional optional = await api.Session.MachineSystem.Info.GetMachineURN(urn).ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNull(optional.Just);
+ }
+ }
+
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/Machine_Test.cs b/FabAccessAPI_Test/API_TestEnv/Machine_Test.cs
new file mode 100644
index 0000000..65a5746
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/Machine_Test.cs
@@ -0,0 +1,301 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture]
+ public class Machine_Test
+ {
+ #region SetUp
+ [SetUp]
+ public async Task SetUp()
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData("Admin1");
+ await api.Connect(connectionData);
+
+ IReadOnlyList machine_list = await api.Session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);
+
+ List tasks = new List();
+ foreach (Machine m in machine_list)
+ {
+ tasks.Add(m.Manage.ForceFree());
+ }
+
+ await Task.WhenAll(tasks);
+ }
+ #endregion
+
+ [TestCase("Admin1", "MachineA1")]
+ [TestCase("ManagerA1", "MachineA1")]
+ [TestCase("MakerA1", "MachineA1")]
+ [Order(1)]
+ public async Task UseGiveBack(string username, string machineID)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine.State != Machine.MachineState.free)
+ {
+ await api.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine.Use.Use().ConfigureAwait(false);
+
+ machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine.Inuse.GiveBack().ConfigureAwait(false);
+
+ machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(Machine.MachineState.free, machine.State);
+ }
+
+ [TestCase("ManagerA1", "MakerA1", "MachineA1")]
+ [TestCase("MakerA1", "Admin1", "MachineA1")]
+ [TestCase("ManagerA1", "GuestA1", "MachineA1")]
+ [Order(2), Ignore("Not Implemented")]
+ public async Task TransferMachine(string username1, string username2, string machineID)
+ {
+ API api1 = new API();
+ ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
+ await api1.Connect(connectionData1);
+
+ API api2 = new API();
+ ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
+ await api2.Connect(connectionData2);
+
+ Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine1.State != Machine.MachineState.free)
+ {
+ await api1.Disconnect();
+ await api2.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine1.Use.Use().ConfigureAwait(false);
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);
+
+ Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Takeover.Accept().ConfigureAwait(false);
+
+ machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Inuse.GiveBack().ConfigureAwait(false);
+
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api1.Disconnect();
+ await api2.Disconnect();
+
+ ClassicAssert.AreEqual(Machine.MachineState.free, machine1.State);
+ }
+
+ [TestCase("ManagerA1", "MakerA1", "MachineA1")]
+ [TestCase("MakerA1", "Admin1", "MachineA1")]
+ [TestCase("ManagerA1", "GuestA1", "MachineA1")]
+ [Order(3), Ignore("Not Implemented")]
+ public async Task TransferMachine_Reject(string username1, string username2, string machineID)
+ {
+ API api1 = new API();
+ ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
+ await api1.Connect(connectionData1);
+
+ API api2 = new API();
+ ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
+ await api2.Connect(connectionData2);
+
+ Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine1.State != Machine.MachineState.free)
+ {
+ await api1.Disconnect();
+ await api2.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine1.Use.Use().ConfigureAwait(false);
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);
+
+ Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Takeover.Reject().ConfigureAwait(false);
+
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.GiveBack().ConfigureAwait(false);
+
+ machine2 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api1.Disconnect();
+ await api2.Disconnect();
+
+ ClassicAssert.AreEqual(Machine.MachineState.free, machine2.State);
+ }
+
+ [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
+ [TestCase("ManagerA1", "Admin1", "MachineA1")]
+ [TestCase("MakerA1", "Admin1", "MachineA1")]
+ [Order(4), Ignore("Not Implemented")]
+ public async Task CheckMachine(string username1, string username2, string machineID)
+ {
+ API api1 = new API();
+ ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
+ await api1.Connect(connectionData1);
+
+ API api2 = new API();
+ ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
+ await api2.Connect(connectionData2);
+
+ Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine1.State != Machine.MachineState.free)
+ {
+ await api1.Disconnect();
+ await api2.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine1.Use.Use().ConfigureAwait(false);
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.GiveBack().ConfigureAwait(false);
+
+ Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Check.Check().ConfigureAwait(false);
+
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api1.Disconnect();
+ await api2.Disconnect();
+
+ ClassicAssert.AreEqual(Machine.MachineState.free, machine1.State);
+ }
+
+ [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
+ [TestCase("ManagerA1", "Admin1", "MachineA1")]
+ [TestCase("MakerA1", "Admin1", "MachineA1")]
+ [Order(5), Ignore("Not Implemented")]
+ public async Task CheckMachine_Reject(string username1, string username2, string machineID)
+ {
+ API api1 = new API();
+ ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
+ await api1.Connect(connectionData1);
+
+ API api2 = new API();
+ ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
+ await api2.Connect(connectionData2);
+
+
+ Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine1.State != Machine.MachineState.free)
+ {
+ await api1.Disconnect();
+ await api2.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine1.Use.Use().ConfigureAwait(false);
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.GiveBack().ConfigureAwait(false);
+
+ Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Check.Reject().ConfigureAwait(false);
+
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.GiveBack().ConfigureAwait(false);
+
+ machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine2.Check.Check().ConfigureAwait(false);
+
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api1.Disconnect();
+ await api2.Disconnect();
+
+ ClassicAssert.AreEqual(Machine.MachineState.free, machine1.State);
+ }
+
+ [TestCase("MakerA1", "GuestA1", "ManagerA1", "MachineA1")]
+ [Order(4), Ignore("Not Implemented")]
+ public async Task CheckMachine_NoPermission(string username1, string username2, string username3, string machineID)
+ {
+ API api1 = new API();
+ ConnectionData connectionData1 = TestEnv.CreateConnetionData(username1);
+ await api1.Connect(connectionData1);
+
+ API api2 = new API();
+ ConnectionData connectionData2 = TestEnv.CreateConnetionData(username2);
+ await api2.Connect(connectionData2);
+
+ API api3 = new API();
+ ConnectionData connectionData3 = TestEnv.CreateConnetionData(username3);
+ await api3.Connect(connectionData3);
+
+ Machine machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine1.State != Machine.MachineState.free)
+ {
+ await api1.Disconnect();
+ await api2.Disconnect();
+ await api3.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ await machine1.Use.Use().ConfigureAwait(false);
+ machine1 = (await api1.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine1.Inuse.GiveBack().ConfigureAwait(false);
+
+ Machine machine2 = (await api2.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ bool result = ((Machine.CheckInterface_Proxy)machine2.Check).IsNull;
+
+ Machine machine3 = (await api3.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+ await machine3.Check.Check().ConfigureAwait(false);
+
+ await api1.Disconnect();
+ await api2.Disconnect();
+ await api3.Disconnect();
+
+ ClassicAssert.IsTrue(result);
+ }
+
+ [TestCase("ManagerA1", "MachineA1")]
+ [Order(5)]
+ public async Task CurrentUser(string username, string machineID)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ // Check State before run Test
+ if (machine.State != Machine.MachineState.free)
+ {
+ await api.Disconnect();
+ ClassicAssert.Inconclusive("State is not 'free'");
+ }
+
+ Machine.MachineInfoExtended machineInfoExtended = await machine.Manage.GetMachineInfoExtended().ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNull(machineInfoExtended.CurrentUser.Just);
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/Machine_Test_Stateless.cs b/FabAccessAPI_Test/API_TestEnv/Machine_Test_Stateless.cs
new file mode 100644
index 0000000..99f054d
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/Machine_Test_Stateless.cs
@@ -0,0 +1,139 @@
+using FabAccessAPI;
+using NUnit.Framework;
+using System.Threading.Tasks;
+using FabAccessAPI.Schema;
+using NUnit.Framework.Legacy;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture, Parallelizable(ParallelScope.Children)]
+ [Order(2)]
+ public class Machine_Test_Stateless
+ {
+ [TestCase("Admin1", "MachineA1", true)]
+ [TestCase("Admin1", "MachineB1", true)]
+ [TestCase("Admin1", "MachineC1", true)]
+ [TestCase("ManagerA1", "MachineA1", true)]
+ [TestCase("ManagerB1", "MachineB1", true)]
+ [TestCase("ManagerC1", "MachineC1", true)]
+ [TestCase("ManagerABC1", "MachineA1", true)]
+ [TestCase("ManagerABC1", "MachineB1", true)]
+ [TestCase("ManagerABC1", "MachineC1", true)]
+ [TestCase("MakerA1", "MachineA1", true)]
+ [TestCase("MakerB1", "MachineB1", true)]
+ [TestCase("MakerC1", "MachineC1", true)]
+ [TestCase("GuestA1", "MachineA1", true)]
+ [TestCase("GuestB1", "MachineB1", true)]
+ [TestCase("GuestC1", "MachineC1", true)]
+ [TestCase("MakerQRA", "MachineA1", true)]
+ [TestCase("MakerQRB", "MachineB1", true)]
+ [TestCase("MakerQRC", "MachineC1", true)]
+ [Order(1)]
+ public async Task InfoInterface(string username, string machineID, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ bool result = !((Machine.InfoInterface_Proxy)machine.Info).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", "MachineA1", true)]
+ [TestCase("Admin1", "MachineB1", true)]
+ [TestCase("Admin1", "MachineC1", true)]
+ [TestCase("ManagerA1", "MachineA1", true)]
+ [TestCase("ManagerB1", "MachineB1", true)]
+ [TestCase("ManagerC1", "MachineC1", true)]
+ [TestCase("ManagerABC1", "MachineA1", true)]
+ [TestCase("ManagerABC1", "MachineB1", true)]
+ [TestCase("ManagerABC1", "MachineC1", true)]
+ [TestCase("MakerA1", "MachineA1", false)]
+ [TestCase("MakerB1", "MachineB1", false)]
+ [TestCase("MakerC1", "MachineC1", false)]
+ [TestCase("GuestA1", "MachineA1", false)]
+ [TestCase("GuestB1", "MachineB1", false)]
+ [TestCase("GuestC1", "MachineC1", false)]
+ [TestCase("MakerQRA", "MachineA1", false)]
+ [TestCase("MakerQRB", "MachineB1", false)]
+ [TestCase("MakerQRC", "MachineC1", false)]
+ [Order(2)]
+ public async Task ManageInterface(string username, string machineID, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ bool result = !((Machine.ManageInterface_Proxy)machine.Manage).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", "MachineA1", true)]
+ [TestCase("Admin1", "MachineB1", true)]
+ [TestCase("Admin1", "MachineC1", true)]
+ [TestCase("ManagerA1", "MachineA1", false)]
+ [TestCase("ManagerB1", "MachineB1", false)]
+ [TestCase("ManagerC1", "MachineC1", false)]
+ [TestCase("ManagerABC1", "MachineA1", false)]
+ [TestCase("ManagerABC1", "MachineB1", false)]
+ [TestCase("ManagerABC1", "MachineC1", false)]
+ [TestCase("MakerA1", "MachineA1", false)]
+ [TestCase("MakerB1", "MachineB1", false)]
+ [TestCase("MakerC1", "MachineC1", false)]
+ [TestCase("GuestA1", "MachineA1", false)]
+ [TestCase("GuestB1", "MachineB1", false)]
+ [TestCase("GuestC1", "MachineC1", false)]
+ [TestCase("MakerQRA", "MachineA1", false)]
+ [TestCase("MakerQRB", "MachineB1", false)]
+ [TestCase("MakerQRC", "MachineC1", false)]
+ [Order(3), Ignore("Not Implemented")]
+ public async Task AdminInterface(string username, string machineID, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ bool result = !((Machine.AdminInterface_Proxy)machine.Admin).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", "MachineA1", "Description of MachineA1", @"https://fab-access.readthedocs.io", "CategoryA")]
+ [TestCase("Admin1", "MachineB2", "Description of MachineB2", @"https://fab-access.readthedocs.io", "CategoryB")]
+ [TestCase("Admin1", "MachineC3", "Description of MachineC3", @"https://fab-access.readthedocs.io", "CategoryC")]
+ [Order(4)]
+ public async Task ReadMachineData(string username, string machineID, string description, string wiki, string category)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ Machine machine = (await api.Session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false)).Just;
+
+ await api.Disconnect();
+
+ ClassicAssert.Multiple(() =>
+ {
+ ClassicAssert.AreEqual(machineID, machine.Id);
+ ClassicAssert.AreEqual(description, machine.Description);
+ ClassicAssert.AreEqual(wiki, machine.Wiki);
+ ClassicAssert.AreEqual(category, machine.Category);
+ });
+ }
+ }
+
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/PermissionSystem_Test_Stateless.cs b/FabAccessAPI_Test/API_TestEnv/PermissionSystem_Test_Stateless.cs
new file mode 100644
index 0000000..9c2d635
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/PermissionSystem_Test_Stateless.cs
@@ -0,0 +1,70 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture, Parallelizable(ParallelScope.Children)]
+ [Order(1)]
+ public class PermissionSystem_Test_Stateless
+ {
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("GuestA1", true)]
+ [Order(1)]
+ public async Task AccessPermissionSystem(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ bool result = api.Session.PermissionSystem != null;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("GuestA1", true)]
+ [Order(2)]
+ public async Task InfoInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ PermissionSystem.InfoInterface_Proxy infoInterface = (PermissionSystem.InfoInterface_Proxy)api.Session.PermissionSystem.Info;
+
+ bool result = !infoInterface.IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", 13)]
+ [TestCase("ManagerA1", 13)]
+ [TestCase("MakerA1", 13)]
+ [TestCase("GuestA1", 13)]
+ [Order(3), Ignore("Not implemented")]
+ public async Task ListRoles(string username, int expectRolesCount)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ IReadOnlyList roles_list = await api.Session.PermissionSystem.Info.GetRoleList().ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectRolesCount, roles_list.Count);
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/UserSystem_Test.cs b/FabAccessAPI_Test/API_TestEnv/UserSystem_Test.cs
new file mode 100644
index 0000000..99536e5
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/UserSystem_Test.cs
@@ -0,0 +1,143 @@
+using Capnp.Rpc;
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture]
+ public class UserSystem_Test
+ {
+ #region SetUp
+ [SetUp]
+ public async Task SetUp()
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData("Admin1");
+ await api.Connect(connectionData);
+
+ IReadOnlyList user_list = await api.Session.UserSystem.Manage.GetUserList().ConfigureAwait(false);
+
+ List tasks = new List();
+ foreach (User u in user_list)
+ {
+ if(u.Username.StartsWith("New"))
+ {
+ tasks.Add(api.Session.UserSystem.Manage.RemoveUser(u));
+ }
+ }
+
+ await Task.WhenAll(tasks);
+ }
+ #endregion
+
+ [TestCase("Admin1", "NewUserA1")]
+ [Order(1)]
+ public async Task AddUser_DEPRECATED(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ bool methodNotImplemented = false;
+
+ try
+ {
+ User user = (await api.Session.UserSystem.Manage.AddUser(username2, TestEnv.PASSWORD).ConfigureAwait(false));
+ }
+ catch (RpcException exception) when (string.Equals(exception.Message, "method not implemented", StringComparison.Ordinal))
+ {
+ methodNotImplemented = true;
+ }
+
+ await api.Disconnect();
+
+ ClassicAssert.IsTrue(methodNotImplemented);
+ }
+
+ [TestCase("Admin1", "NewUserA1")]
+ [TestCase("Admin1", "NewUserB1")]
+ [TestCase("Admin1", "NewUserC1")]
+ [Order(1)]
+ public async Task AddUser(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = (await api.Session.UserSystem.Manage.AddUserFallible(username2, TestEnv.PASSWORD).ConfigureAwait(false)).Successful;
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNotNull(user);
+ }
+
+ [TestCase("Admin1", "Admin1")]
+ [TestCase("Admin1", "ManagerA1")]
+ [TestCase("Admin1", "MakerA1")]
+ [TestCase("Admin1", "GuestA1")]
+ [Order(2)]
+ public async Task AddUser_AllreadyExists(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.ManageInterface.AddUserError.AddUserErrorEnum error = (await api.Session.UserSystem.Manage.AddUserFallible(username2, TestEnv.PASSWORD).ConfigureAwait(false)).Failed.Error;
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(UserSystem.ManageInterface.AddUserError.AddUserErrorEnum.alreadyExists, error);
+ }
+
+ [TestCase("Admin1", "")]
+ [Order(2)]
+ public async Task AddUser_InvalidUsername(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.ManageInterface.AddUserError.AddUserErrorEnum error = (await api.Session.UserSystem.Manage.AddUserFallible(username2, TestEnv.PASSWORD).ConfigureAwait(false)).Failed.Error;
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(UserSystem.ManageInterface.AddUserError.AddUserErrorEnum.usernameInvalid, error);
+ }
+
+ [TestCase("Admin1", "NewUserC1", "")]
+ [Order(2)]
+ public async Task AddUser_InvalidPassword(string username, string username2, string password)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.ManageInterface.AddUserError.AddUserErrorEnum error = (await api.Session.UserSystem.Manage.AddUserFallible(username2, password).ConfigureAwait(false)).Failed.Error;
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(UserSystem.ManageInterface.AddUserError.AddUserErrorEnum.passwordInvalid, error);
+ }
+
+ [TestCase("Admin1", "NewUserA1")]
+ [TestCase("Admin1", "NewUserB1")]
+ [TestCase("Admin1", "NewUserC1")]
+ [Order(3)]
+ public async Task AddRemoveUser(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = (await api.Session.UserSystem.Manage.AddUserFallible(username2, TestEnv.PASSWORD).ConfigureAwait(false)).Successful;
+
+ await api.Session.UserSystem.Manage.RemoveUser(user);
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNotNull(user);
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/UserSystem_Test_Stateless.cs b/FabAccessAPI_Test/API_TestEnv/UserSystem_Test_Stateless.cs
new file mode 100644
index 0000000..2f058b5
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/UserSystem_Test_Stateless.cs
@@ -0,0 +1,123 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture, Parallelizable(ParallelScope.Children)]
+ [Order(1)]
+ public class UserSystem_Test_Stateless
+ {
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("GuestA1", true)]
+ [Order(2)]
+ public async Task InfoInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.InfoInterface_Proxy infoInterface = (UserSystem.InfoInterface_Proxy)api.Session.UserSystem.Info;
+
+ bool result = !infoInterface.IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", false)]
+ [TestCase("GuestA1", false)]
+ [Order(3)]
+ public async Task ManageInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.ManageInterface_Proxy manageInterface = (UserSystem.ManageInterface_Proxy)api.Session.UserSystem.Manage;
+
+ bool result = !manageInterface.IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", false)]
+ [TestCase("GuestA1", false)]
+ [Order(3)]
+ public async Task SearchInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ UserSystem.SearchInterface_Proxy searchInterface = (UserSystem.SearchInterface_Proxy)api.Session.UserSystem.Search;
+
+ bool result = !searchInterface.IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1")]
+ [TestCase("ManagerA1")]
+ [TestCase("MakerA1")]
+ [TestCase("GuestA1")]
+ [Order(4)]
+ public async Task GetUserSelf(string username)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNotNull(user);
+ }
+
+ [TestCase("Admin1", "Admin1")]
+ [TestCase("Admin1", "MakerA1")]
+ [TestCase("Admin1", "GuestA1")]
+ [Order(4)]
+ public async Task GetUserByUsername(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = (await api.Session.UserSystem.Search.GetUserByName(username2).ConfigureAwait(false)).Just;
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNotNull(user);
+ }
+
+ [TestCase("Admin1", "UnknownUser")]
+ [Order(5)]
+ public async Task GetUserByUsername_NotExist(string username, string username2)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = (await api.Session.UserSystem.Search.GetUserByName(username2).ConfigureAwait(false)).Just;
+
+ await api.Disconnect();
+
+ ClassicAssert.IsNull(user);
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/User_Test.cs b/FabAccessAPI_Test/API_TestEnv/User_Test.cs
new file mode 100644
index 0000000..8633d7c
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/User_Test.cs
@@ -0,0 +1,69 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture]
+ public class User_Test
+ {
+ #region SetUp
+ [SetUp]
+ public async Task SetUp()
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData("Admin1");
+ await api.Connect(connectionData);
+
+ IReadOnlyList user_list = await api.Session.UserSystem.Manage.GetUserList().ConfigureAwait(false);
+
+ List tasks = new List();
+ foreach (User u in user_list)
+ {
+ if (u.Username.StartsWith("New"))
+ {
+ tasks.Add(api.Session.UserSystem.Manage.RemoveUser(u));
+ }
+ }
+
+ await Task.WhenAll(tasks);
+ }
+ #endregion
+
+ [TestCase("Admin1", "NewMakerA1", "UseA", "ReadA", "DiscloseA")]
+ [Order(1)]
+ [Ignore("Deprecated")]
+ public async Task AddRoles(string username, string username2, params string[] roles)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ await api.Session.UserSystem.Manage.AddUser(username2, TestEnv.PASSWORD);
+
+ User user = (await api.Session.UserSystem.Search.GetUserByName(username2).ConfigureAwait(false)).Just;
+
+ foreach(string s in roles)
+ {
+ await user.Admin.AddRole(new Role() { Name = s }).ConfigureAwait(false);
+ }
+
+ user = (await api.Session.UserSystem.Search.GetUserByName(username2).ConfigureAwait(false)).Just;
+ List user_roles = new List(await user.Info.ListRoles().ConfigureAwait(false));
+
+ await api.Disconnect();
+
+ ClassicAssert.Multiple(() =>
+ {
+ ClassicAssert.AreEqual(3, user_roles.Count);
+ foreach (string s in roles)
+ {
+ ClassicAssert.IsTrue(user_roles.Exists(x => x.Name == s));
+ }
+ });
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/API_TestEnv/User_Test_Stateless.cs b/FabAccessAPI_Test/API_TestEnv/User_Test_Stateless.cs
new file mode 100644
index 0000000..8cb6c91
--- /dev/null
+++ b/FabAccessAPI_Test/API_TestEnv/User_Test_Stateless.cs
@@ -0,0 +1,127 @@
+using FabAccessAPI;
+using FabAccessAPI.Schema;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI_Test.API_TestEnv
+{
+ [TestFixture, Parallelizable(ParallelScope.Children)]
+ [Order(2)]
+ public class User_Test_Stateless
+ {
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("GuestA1", true)]
+ [Order(1)]
+ public async Task InfoInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ bool result = !((User.InfoInterface_Proxy)user.Info).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", true)]
+ [TestCase("GuestA1", true)]
+ [Order(2)]
+ public async Task ManageInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ bool result = !((User.ManageInterface_Proxy)user.Manage).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1", true)]
+ [TestCase("ManagerA1", true)]
+ [TestCase("MakerA1", false)]
+ [TestCase("GuestA1", false)]
+ [Order(3)]
+ public async Task AdminInterface(string username, bool expectInterface)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ bool result = !((User.AdminInterface_Proxy)user.Admin).IsNull;
+
+ await api.Disconnect();
+
+ ClassicAssert.AreEqual(expectInterface, result);
+ }
+
+ [TestCase("Admin1")]
+ [TestCase("ManagerA1")]
+ [TestCase("MakerA1")]
+ [TestCase("GuestA1")]
+ [Order(4)]
+ public async Task ReadUserData(string username)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ await api.Disconnect();
+
+ ClassicAssert.Multiple(() =>
+ {
+ ClassicAssert.AreEqual(username, user.Username);
+ });
+ }
+
+ [TestCase("Admin1", "Admin", "ManageUsers")]
+ [TestCase("ManagerA1", "ManageA", "UseA", "ReadA", "DiscloseA", "ManageUsers")]
+ [TestCase("MakerA1", "UseA", "ReadA", "DiscloseA")]
+ [TestCase("GuestA1", "ReadA", "DiscloseA")]
+ [Order(5)]
+ public async Task ListUserRoles(string username, params string[] expect_roles)
+ {
+ API api = new API();
+ ConnectionData connectionData = TestEnv.CreateConnetionData(username);
+ await api.Connect(connectionData);
+
+ User user = await api.Session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);
+
+ List roles_user = new List(await user.Info.ListRoles().ConfigureAwait(false));
+ List expect_roles_list = new List(expect_roles);
+
+ await api.Disconnect();
+
+ if (roles_user.Count != expect_roles_list.Count)
+ {
+ ClassicAssert.Fail("Roles Count is different");
+ }
+
+ foreach (Role role_user in roles_user)
+ {
+ if (!expect_roles_list.Exists(x => x == role_user.Name))
+ {
+ ClassicAssert.Fail("Roles are different");
+ }
+ }
+ }
+ }
+}
diff --git a/FabAccessAPI_Test/FabAccessAPI_Test.csproj b/FabAccessAPI_Test/FabAccessAPI_Test.csproj
new file mode 100644
index 0000000..bf6f070
--- /dev/null
+++ b/FabAccessAPI_Test/FabAccessAPI_Test.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FabAccessAPI_Test/TestEnv.cs b/FabAccessAPI_Test/TestEnv.cs
new file mode 100644
index 0000000..356e208
--- /dev/null
+++ b/FabAccessAPI_Test/TestEnv.cs
@@ -0,0 +1,33 @@
+using FabAccessAPI;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+
+namespace FabAccessAPI_Test
+{
+ public static class TestEnv
+ {
+ public const string SCHEMA = "fabaccess";
+ public const string TESTSERVER = "127.0.0.1";//"test.fab-access.org";
+ public const int TESTSERVER_PORT = 59661;
+ public const string PASSWORD = "secret";
+
+ [TestCase("Testuser")]
+ public static ConnectionData CreateConnetionData(string username)
+ {
+ ConnectionData connectionData = new ConnectionData()
+ {
+ Host = new UriBuilder(TestEnv.SCHEMA, TestEnv.TESTSERVER, TestEnv.TESTSERVER_PORT).Uri,
+ Mechanism = SASLMechanismEnum.PLAIN,
+ Username = username,
+ Properties = new Dictionary()
+ {
+ { "Username", username },
+ { "Password", TestEnv.PASSWORD },
+ },
+ };
+
+ return connectionData;
+ }
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..261d975
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+FabAccess API
+===
+
+FabAccess API in C# with Tests
\ No newline at end of file