diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..c2641fa
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,139 @@
+# This file is a template, and might need editing before it works on your project.
+# The following script will work for any project that can be built from command line by msbuild
+# It uses powershell shell executor, so you need to add the following line to your config.toml file
+# (located in gitlab-runner.exe directory):
+# shell = "powershell"
+#
+# The script is composed of 3 stages: build, test and deploy.
+#
+# The build stage restores NuGet packages and uses msbuild to build the exe and msi
+# One major issue you'll find is that you can't build msi projects from command line
+# if you use vdproj. There are workarounds building msi via devenv, but they rarely work
+# The best solution is migrating your vdproj projects to WiX, as it can be build directly
+# by msbuild.
+#
+# The test stage runs nunit from command line against Test project inside your solution
+# It also saves the resulting TestResult.xml file
+#
+# The deploy stage copies the exe and msi from build stage to a network drive
+# You need to have the network drive mapped as Local System user for gitlab-runner service to see it
+# The best way to persist the mapping is via a scheduled task (see: https://stackoverflow.com/a/7867064/1288473),
+# running the following batch command: net use P: \\x.x.x.x\Projects /u:your_user your_pass /persistent:yes
+
+
+# place project specific paths in variables to make the rest of the script more generic
+variables:
+ LIB_RELEASE_FOLDER: 'Borepin\Borepin\bin\Release'
+ UWP_RELEASE_FOLDER: 'Borepin\Borepin.UWP\bin\x86\Release'
+ TEST_FOLDER: 'Tests\bin\Release'
+# DEPLOY_FOLDER: 'P:\Projects\YourApp\Builds'
+ NUGET_PATH: 'C:\NuGet\nuget.exe'
+ DOTNET_PATH: 'C:\Program Files\dotnet\dotnet.exe'
+ MSBUILD_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin\msbuild.exe'
+ NUNIT_PATH: 'C:\Program Files (x86)\NUnit.org\nunit-console\nunit3-console.exe'
+
+stages:
+ - build
+ - test
+ - deploy
+
+build_base:
+ stage: build
+ tags:
+ - xamarin
+ - windows
+# only:
+# - tags # the build process will only be started by git tag commits
+ script:
+ - '& "$env:NUGET_PATH" restore' # restore Nuget dependencies
+ - '& "$env:MSBUILD_PATH" /p:Configuration=Release /target:Borepin' # build the project
+ artifacts:
+ expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on
+ paths:
+ - '$env:LIB_RELEASE_FOLDER' # saving exe to copy to deploy folder
+ - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests
+
+build_UWP:
+ stage: build
+ tags:
+ - xamarin
+ - windows
+# only:
+# - tags # the build process will only be started by git tag commits
+ script:
+ - '& "$env:NUGET_PATH" restore' # restore Nuget dependencies
+ - '& "$env:MSBUILD_PATH" /p:Configuration=Release /target:Borepin_UWP' # build the project
+ artifacts:
+ expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on
+ paths:
+ - '$env:UWP_RELEASE_FOLDER' # saving exe to copy to deploy folder
+ - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests
+
+build_Android:
+ stage: build
+ tags:
+ - xamarin
+ - windows
+# only:
+# - tags # the build process will only be started by git tag commits
+ script:
+ - '& "$env:NUGET_PATH" restore' # restore Nuget dependencies
+ - '& "$env:MSBUILD_PATH" /p:Configuration=Release /target:Borepin_Android:PackageForAndroid /target:Borepin_Android:SignAndroidPackage' # build the project /p:AndroidKeyStore=True
+ artifacts:
+ expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on
+ paths:
+ - Borepin/Borepin.Android/bin/Release/com.companyname.borepin-Signed.apk # saving apk to copy to deploy folder
+ - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests
+
+build_iOS:
+ stage: build
+ tags:
+ - macos
+# only:
+# - tags # the build process will only be started by git tag commits
+ script:
+ - 'nuget restore' # restore Nuget dependencies
+ - 'msbuild /t:Borepin_iOS /p:Configuration=Debug /p:Platform=iPhone /p:ArchiveOnBuild=true' # build the project /p:AndroidKeyStore=True
+ artifacts:
+ expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on
+ paths:
+ - Borepin/Borepin.iOS/bin/iPhone/Debug/Borepin.ipa
+ - Borepin/Borepin.iOS/bin/iPhone/Debug/Borepin.app.dSYM
+ - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests
+
+# test_job:
+# stage: test
+# tags:
+# - xamarin
+# - windows
+# # only:
+# # - tags
+# script:
+# - '& "$env:NUNIT_PATH" ".\$env:TEST_FOLDER\Tests.dll"' # running NUnit tests
+# artifacts:
+# when: always # save test results even when the task fails
+# expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on
+# paths:
+# - '.\TestResult.xml' # saving NUnit results to copy to deploy folder
+# dependencies:
+# - build_job
+
+# deploy_job:
+# stage: deploy
+# only:
+# - tags
+# script:
+# # Compose a folder for each release based on commit tag.
+# # Assuming your tag is Rev1.0.0.1, and your last commit message is 'First commit'
+# # the artifact files will be copied to:
+# # P:\Projects\YourApp\Builds\Rev1.0.0.1 - First commit\
+# - '$commitSubject = git log -1 --pretty=%s'
+# - '$deployFolder = $($env:DEPLOY_FOLDER) + "\" + $($env:CI_BUILD_TAG) + " - " + $commitSubject + "\"'
+
+# # xcopy takes care of recursively creating required folders
+# - 'xcopy /y ".\$env:EXE_RELEASE_FOLDER\YourApp.exe" "$deployFolder"'
+# - 'xcopy /y ".\$env:MSI_RELEASE_FOLDER\YourApp Setup.msi" "$deployFolder"'
+# - 'xcopy /y ".\TestResult.xml" "$deployFolder"'
+# dependencies:
+# - build_job
+# - test_job
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f04abcd
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "FabAccessAPI/schema"]
+ path = FabAccessAPI/schema
+ url = https://gitlab.com/fabinfra/fabaccess/fabaccess-api.git
+[submodule "external/SASL"]
+ path = external/SASL
+ url = https://github.com/kjkriegel/S22.Sasl.git
diff --git a/Borepin.sln b/Borepin.sln
index f2e2b0c..409eea6 100644
--- a/Borepin.sln
+++ b/Borepin.sln
@@ -15,9 +15,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Borepin.GTK", "Borepin\Bore
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Borepin.macOS", "Borepin\Borepin.macOS\Borepin.macOS.csproj", "{3EC23FE7-395E-4BBC-B56B-9455354BDA34}"
EndProject
-Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "NFC_Test", "NFC_Test\NFC_Test.csproj", "{41EC0C17-B456-42AE-89F2-79DDB8ED9858}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NFC_Test", "NFC_Test\NFC_Test.csproj", "{41EC0C17-B456-42AE-89F2-79DDB8ED9858}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NFC", "NFC\NFC.csproj", "{9C2ED2EB-D91C-4D80-9580-6A135C05AF11}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NFC", "NFC\NFC.csproj", "{9C2ED2EB-D91C-4D80-9580-6A135C05AF11}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FabAccessAPI", "FabAccessAPI\FabAccessAPI.csproj", "{3251FCE9-FEA3-4662-8BEB-636BE6732D48}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "S22.Sasl", "external\SASL\S22.Sasl.csproj", "{7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FabAccessAPI_Test", "FabAccessAPI_Test\FabAccessAPI_Test.csproj", "{1C85978A-9FC0-4064-8399-FA2455C5EC2A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -275,6 +281,78 @@ Global
{9C2ED2EB-D91C-4D80-9580-6A135C05AF11}.Release|x64.Build.0 = Release|Any CPU
{9C2ED2EB-D91C-4D80-9580-6A135C05AF11}.Release|x86.ActiveCfg = Release|Any CPU
{9C2ED2EB-D91C-4D80-9580-6A135C05AF11}.Release|x86.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|ARM.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|x64.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Debug|x86.Build.0 = Debug|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|ARM.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|ARM.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|iPhone.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|x64.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|x64.Build.0 = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|x86.ActiveCfg = Release|Any CPU
+ {3251FCE9-FEA3-4662-8BEB-636BE6732D48}.Release|x86.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|ARM.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|x64.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Debug|x86.Build.0 = Debug|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|ARM.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|ARM.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|iPhone.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|x64.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|x64.Build.0 = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|x86.ActiveCfg = Release|Any CPU
+ {7FEC3D5E-C240-41B6-BBFA-895C4F4D45CA}.Release|x86.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|ARM.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|x64.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Debug|x86.Build.0 = Debug|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|ARM.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|ARM.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|iPhone.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|x64.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|x64.Build.0 = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|x86.ActiveCfg = Release|Any CPU
+ {1C85978A-9FC0-4064-8399-FA2455C5EC2A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Borepin/Borepin.iOS/Borepin.iOS.csproj b/Borepin/Borepin.iOS/Borepin.iOS.csproj
index 65a106b..ee89ac0 100644
--- a/Borepin/Borepin.iOS/Borepin.iOS.csproj
+++ b/Borepin/Borepin.iOS/Borepin.iOS.csproj
@@ -15,7 +15,7 @@
true
NSUrlSessionHandler
automatic
- true
+ 13.2
true
@@ -51,9 +51,10 @@
iPhone Developer
true
Entitlements.plist
- Full
+ SdkOnly
-all
--optimize=experimental-xforms-product-type
+ iOS Team Provisioning Profile: org.fab-access.borepin
none
@@ -64,8 +65,8 @@
ARM64
iPhone Developer
Entitlements.plist
+ Automatic:AppStore
--optimize=experimental-xforms-product-type
- Full
diff --git a/Borepin/Borepin/Borepin.csproj b/Borepin/Borepin/Borepin.csproj
index 7c81734..477ce23 100644
--- a/Borepin/Borepin/Borepin.csproj
+++ b/Borepin/Borepin/Borepin.csproj
@@ -1,4 +1,4 @@
-
+
4.0
@@ -22,6 +22,7 @@
+
@@ -51,4 +52,4 @@
MSBuild:UpdateDesignTimeXaml
-
\ No newline at end of file
+
diff --git a/FabAccessAPI/Auth.cs b/FabAccessAPI/Auth.cs
new file mode 100644
index 0000000..ab83078
--- /dev/null
+++ b/FabAccessAPI/Auth.cs
@@ -0,0 +1,165 @@
+using Capnp;
+using FabAccessAPI.Schema;
+using S22.Sasl;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Exception = System.Exception;
+
+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 `@`
+ 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 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{}
+
+ ///
+ /// THIS IS VERY INCOMPLETE!
+ ///
+ public class Auth {
+ #region Log
+ private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+
+ private IAuthentication _authCap;
+ public Auth(IAuthentication authCap) {
+ _authCap = authCap;
+ }
+
+ public Task> GetMechanisms() {
+ return _authCap.Mechanisms();
+ }
+
+ public async Task Authenticate(string mech, Dictionary properties) {
+
+ var m = SaslFactory.Create(mech);
+ foreach (KeyValuePair entry in properties) {
+ m.Properties.Add(entry.Key, entry.Value);
+ }
+
+ var initialResponse = new Request.initialResponse();
+ if (m.HasInitial) {
+ initialResponse.Initial = m.GetResponse(new byte[0]);
+ }
+
+ var req = new Request {
+ Mechanism = m.Name,
+ InitialResponse = initialResponse
+ };
+
+ var resp = await _authCap.Start(req);
+ while (!m.IsCompleted) {
+ if (resp.which == Response.WHICH.Challence) {
+ var additional = m.GetResponse(resp.Challence.ToArray());
+ resp = await _authCap.Step(additional);
+ }
+ else {
+ break;
+ }
+ }
+
+ if (resp.which == Response.WHICH.Outcome) {
+ if (resp.Outcome.Result == Response.Result.successful) {
+ return true;
+ }
+ else {
+ //TODO: Provide meaningful info about auth failure
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ }
+
+}
diff --git a/FabAccessAPI/Connection.cs b/FabAccessAPI/Connection.cs
new file mode 100644
index 0000000..84386ab
--- /dev/null
+++ b/FabAccessAPI/Connection.cs
@@ -0,0 +1,64 @@
+using Capnp.Rpc;
+using FabAccessAPI.Schema;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI {
+ public class Connection {
+ #region private variables
+ private TcpRpcClient? _rpcClient = null;
+ private IBootstrap? _bootstrapCap = null;
+ private AuthUser? _authUser = null;
+ private Auth? _auth = null;
+ private Machines? _machines = null;
+ #endregion
+
+ public TcpRpcClient? RpcClient => _rpcClient;
+
+ #region Log
+ private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+
+ ///
+ ///
+ ///
+ /// Should be an already configured and connected TcpRpcClient
+ public Connection(TcpRpcClient rpcClient) {
+ _rpcClient = rpcClient;
+ _bootstrapCap = _rpcClient.GetMain();
+ _Log.Debug($"Done bootstraping API connection.");
+ }
+
+ ///
+ /// Authenticate this connection.
+ /// Calling this more then once is UB
+ ///
+ /// The desired authentication mechanism
+ /// Key-Value data specific to the mechanism
+ ///
+ public async Task Auth(string mech, Dictionary kvs) {
+ // _bootstrapCap = await _bootstrapCap.Unwrap();
+ var authCap = await _bootstrapCap.Auth();
+ _auth = new Auth(authCap);
+ var mechs = await _auth.GetMechanisms().ConfigureAwait(false);
+ _Log.Debug($"The Server supports the following auth mechs: {string.Join(", ", mechs)}");
+
+ if (!mechs.Contains(mech)) {
+ throw new UnsupportedMechanismException();
+ }
+
+ await _auth.Authenticate(mech, kvs).ConfigureAwait(false);
+ }
+
+ ///
+ /// Get a wrapped capability to interact with machines
+ ///
+ /// A wrapped capability to interact with machines
+ public async Task AccessMachines() {
+ _machines ??= new Machines((await _bootstrapCap.Machines().ConfigureAwait(false)));
+ return _machines;
+ }
+ }
+}
diff --git a/FabAccessAPI/FabAccessAPI.csproj b/FabAccessAPI/FabAccessAPI.csproj
new file mode 100644
index 0000000..b616b61
--- /dev/null
+++ b/FabAccessAPI/FabAccessAPI.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netstandard2.1
+ 8
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FabAccessAPI/Machines.cs b/FabAccessAPI/Machines.cs
new file mode 100644
index 0000000..1a3212c
--- /dev/null
+++ b/FabAccessAPI/Machines.cs
@@ -0,0 +1,204 @@
+using FabAccessAPI.Schema;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace FabAccessAPI {
+
+ public class MachineException : Exception { }
+
+ ///
+ /// Wraps a capability for accessing the Machines subsystem of BFFH
+ ///
+ public class Machines {
+ #region Log
+ private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+
+
+ private IMachines _machinesCap;
+
+ ///
+ /// Constructs the Wrapper Class from a given capability.
+ ///
+ /// The capability that should be wrapped.
+ public Machines(IMachines machinesCap) {
+ _machinesCap = machinesCap;
+ }
+
+ ///
+ /// List of all machines that BFFH knows about the user has been granted at least read access on
+ ///
+ /// ReadOnlyList of available Machines
+ public async Task?> ListMachines() {
+ return (await _machinesCap.ListMachines().ConfigureAwait(false)).Select(x => new Machine(x)) as IReadOnlyList;
+ }
+
+ ///
+ /// Access a particular machine by known name. This may fail for two reasons:
+ /// The user has not been granted access to know the machine exists or the machine does in fact not exist.
+ /// In both cases the `machine` result will be a NULL-pointer
+ ///
+ /// Name of the Machine
+ /// The Machine we requested
+ public async Task GetMachine(string name) {
+ var mach = (await _machinesCap.GetMachine(name).ConfigureAwait(false)).Item1;
+ if (mach == null) {
+ //TODO: Throw a more specific exception!
+ throw new MachineException();
+ }
+ return new Machine(mach);
+ }
+ }
+
+ ///
+ /// A machine. This represents a machine as BFFH thinks about it which may mean
+ ///several machines or just part of a machine in the real world.
+ ///By itself this struct is completely useless since it contains only the information
+ ///that the machine exists the user is allowed to know about that fact. For all further
+ ///information the user has to call the contained capabilities which depending on the
+ ///access level may not be set. For example an admin will have every capability here
+ ///set but a simple user may only have `read` and `write` set while some users may not
+ /// even have `read` set and are unable to even see if the machine is currently in use.
+ ///
+ public class Machine {
+ private Schema.Machine _machine;
+
+ ///
+ /// Constructs the Wrapper Class from a given capability
+ ///
+ /// The capability that should be wrapped.
+ public Machine(Schema.Machine machine) {
+ _machine = machine;
+ }
+
+ // read operations
+
+ ///
+ /// Get the MInfo Struct for the Machine.
+ /// This contains everything BFFH knows about the Machine.
+ ///
+ ///
+ /// The MInfo Struct describing the Machine
+ public async Task GetMInfo() {
+ var readCap = _machine.Read;
+ if (readCap == null) {
+ throw new UnauthorizedException();
+ }
+
+ return (await _machine.Read.Info().ConfigureAwait(false)).Item1;
+ }
+
+ //write operations
+
+ ///
+ /// Try to use a machine. Throws a UnauthorizedException if the user does not have the required
+ /// permissions to use this machine.
+ ///
+ /// Use the Ret() Method of the returned Object to return the machine
+ ///
+ ///
+ /// Capability to give back the machine
+ public Task Use() {
+ var writeCap = _machine.Write;
+ if (writeCap == null) {
+ throw new UnauthorizedException();
+ }
+
+ return writeCap.Use();
+ }
+
+ ///
+ /// Try to reserve a machine. Throws a UnauthorizedException if the user does not have the required
+ /// permissions to use this machine.
+ ///
+ /// Use the Ret() Method of the returned Object to return the machine
+ /// Use the Use() Nethod of the Machine to use your reserved machine.
+ ///
+ ///
+ /// Capability to give back the machine
+ public Task Reserve()
+ {
+ var writeCap = _machine.Write;
+ if (writeCap == null)
+ {
+ throw new UnauthorizedException();
+ }
+
+ return writeCap.Reserve();
+ }
+
+
+ // public void GiveBack(Schema.Machine.WriteInterface.IGiveBack cap) {
+ // cap.Ret();
+ // }
+
+ //manage operations
+
+ ///
+ /// After a machine has been used by an user with low enough permissions it's
+ /// in the 'toCheck' state. This call then allows more priviledged users to
+ /// "check" the machine and move it to the `free` state.
+ ///
+ /// Calling this method signifies that the machine was checked and in an acceptable state.
+ ///
+ public async void MarkOk() {
+ var manageCap = _machine.Manage;
+ if (manageCap == null) {
+ throw new UnauthorizedException();
+ }
+ // TODO: Do we really want to check this here?
+ if ((await GetMInfo().ConfigureAwait(false)).State == State.toCheck) {
+ await _machine.Manage.Ok().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// After a machine has been used by an user with low enough permissions it's
+ /// in the 'toCheck' state. This call then allows more priviledged users to
+ /// "check" the machine and move it to the `free` state.
+ ///
+ /// Calling this method signifies that the machine was checked and in an unacceptable state.
+ /// It will most likely be marked as `blocked` and the previous user will somehow be informed.
+ ///
+ public async void MarkNotOk() {
+ var manageCap = _machine.Manage;
+ if (manageCap == null) {
+ throw new UnauthorizedException();
+ }
+ // TODO: Do we really want to check this here?
+ if ((await GetMInfo().ConfigureAwait(false)).State == State.toCheck) {
+ await _machine.Manage.NotOk().ConfigureAwait(false);
+ }
+ }
+
+ //administrative operations
+
+ ///
+ /// Forcefully set a machine state.
+ ///
+ /// The desired machine state.
+ public async void ForceSetState(State state) {
+ var adminCap = _machine.Admin;
+ if (adminCap == null) {
+ throw new UnauthorizedException();
+ }
+
+ await adminCap.ForceSetState(state).ConfigureAwait(false);
+ }
+
+ ///
+ /// Set the given user as current responsible
+ ///
+ /// The user
+ public async void ForceSetUser(String user) {
+ var adminCap = _machine.Admin;
+ if (adminCap == null) {
+ throw new UnauthorizedException();
+ }
+
+ await adminCap.ForceSetUser(user).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/FabAccessAPI/Permissions.cs b/FabAccessAPI/Permissions.cs
new file mode 100644
index 0000000..121aa3e
--- /dev/null
+++ b/FabAccessAPI/Permissions.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+
+//This is where the permissions subsystem will live
+namespace FabAccessAPI {
+ public class Permissions {
+ #region Log
+ private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+ }
+}
diff --git a/FabAccessAPI/schema b/FabAccessAPI/schema
new file mode 160000
index 0000000..4adb053
--- /dev/null
+++ b/FabAccessAPI/schema
@@ -0,0 +1 @@
+Subproject commit 4adb05341763b96a43440a6a96e0d9959ba71e89
diff --git a/FabAccessAPI_Test/FabAccessAPITests.cs b/FabAccessAPI_Test/FabAccessAPITests.cs
new file mode 100644
index 0000000..a20f35a
--- /dev/null
+++ b/FabAccessAPI_Test/FabAccessAPITests.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using FabAccessAPI;
+using Capnp;
+using Capnp.Rpc;
+using log4net.Config;
+using Microsoft.Extensions.Logging;
+
+namespace FabAccessAPI_Test {
+ public class Tests {
+ #region Log
+ private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+
+ private static ILoggerFactory _loggerFactory;
+ private Connection _connection;
+
+ [OneTimeSetUp]
+ public void InitialSetup() {
+ XmlConfigurator.Configure(new System.IO.FileInfo("log4net.config"));
+ _loggerFactory = LoggerFactory.Create(builder => {
+ builder
+ .AddFilter("Microsoft", LogLevel.Warning)
+ .AddFilter("System", LogLevel.Warning);
+ });
+ _loggerFactory.AddLog4Net();
+ Logging.LoggerFactory = _loggerFactory;
+ }
+
+ [SetUp]
+ public void Setup() {
+ var rpcClient = new TcpRpcClient();
+ rpcClient.Connect("127.0.0.1", 59661);
+ _connection = new Connection(rpcClient);
+ }
+
+ [TearDown]
+ public void Teardown() {
+ _connection.RpcClient?.Dispose();
+ _connection = null;
+ }
+
+ [Test]
+ public void Connect() {
+ Assert.AreEqual(ConnectionState.Active, _connection.RpcClient.State);
+ }
+
+ [Test]
+ public async Task Authenticate() {
+ await _connection.Auth("PLAIN", new Dictionary{{"Username", "Testuser"}, {"Password", "secret"}});
+ }
+
+ [Test]
+ public async Task Machines() {
+ await _connection.Auth("PLAIN", new Dictionary{{"Username", "Testuser"}, {"Password", "secret"}});
+ var machines = await _connection.AccessMachines();
+
+ var testmachine = await machines.GetMachine("Testmachine");
+ Assert.NotNull(testmachine);
+ var minfo = await testmachine.GetMInfo();
+ Assert.NotNull(minfo);
+ _Log.Info($"Name: {minfo.Name}, Description: {minfo.Description}, State: {minfo.State.ToString()}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/FabAccessAPI_Test/FabAccessAPI_Test.csproj b/FabAccessAPI_Test/FabAccessAPI_Test.csproj
new file mode 100644
index 0000000..7682803
--- /dev/null
+++ b/FabAccessAPI_Test/FabAccessAPI_Test.csproj
@@ -0,0 +1,26 @@
+
+
+
+ netcoreapp3.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/FabAccessAPI_Test/log4net.config b/FabAccessAPI_Test/log4net.config
new file mode 100644
index 0000000..3781ff0
--- /dev/null
+++ b/FabAccessAPI_Test/log4net.config
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 972e79e..6a792c0 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,14 @@ Borepin
* with .NET Desktop
2. Install GTKSharp for Windows
[download GTKSharp](https://www.mono-project.com/download/stable/#download-win)
+3. Install capnproto
+
+ 3.1 If you have Chocolatey installed
+ ```shell
+ $ choco install capnproto
+ ```
+ 3.2 else you can download it from [here](https://capnproto.org/install.html) and add it to your PATH
+
4. Clone Borepin
[download Borepin](https://gitlab.com/fabinfra/fabaccess/client)
6. Load Borepin
@@ -22,11 +30,11 @@ If Step 5. Build Borepin is failing because of GTKSharp, it could help to restar
1. Install mono, gtk-sharp, msbuild, nuget
1.1 Debian based
```shell
- $ apt install mono-complete, gtk-sharp2, nuget
+ $ apt install mono-complete, gtk-sharp2, nuget, capnproto
```
1.2 ArchLinux based
```shell
- $ pacman -S mono, mono-msbuild, gtk-sharp-2, nuget
+ $ pacman -S mono, mono-msbuild, gtk-sharp-2, nuget, capnproto
```
2. Clone Borepin
```shell
diff --git a/external/SASL b/external/SASL
new file mode 160000
index 0000000..cb9a991
--- /dev/null
+++ b/external/SASL
@@ -0,0 +1 @@
+Subproject commit cb9a9919c971e00c52732fba983e82d795fbe4ad