Merged: master

This commit is contained in:
TheJoKlLa 2021-01-04 23:07:49 +01:00
commit 1ad79b7c03
16 changed files with 819 additions and 9 deletions

139
.gitlab-ci.yml Normal file
View File

@ -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

6
.gitmodules vendored Normal file
View File

@ -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

View File

@ -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

View File

@ -15,7 +15,7 @@
<MtouchEnableSGenConc>true</MtouchEnableSGenConc>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<ProvisioningType>automatic</ProvisioningType>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<MtouchSdkVersion>13.2</MtouchSdkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
@ -51,9 +51,10 @@
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>Full</MtouchLink>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchInterpreter>-all</MtouchInterpreter>
<MtouchExtraArgs>--optimize=experimental-xforms-product-type</MtouchExtraArgs>
<CodesignProvision>iOS Team Provisioning Profile: org.fab-access.borepin</CodesignProvision>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>none</DebugType>
@ -64,8 +65,8 @@
<MtouchArch>ARM64</MtouchArch>
<CodesignKey>iPhone Developer</CodesignKey>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignProvision>Automatic:AppStore</CodesignProvision>
<MtouchExtraArgs>--optimize=experimental-xforms-product-type</MtouchExtraArgs>
<MtouchLink>Full</MtouchLink>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Default' ">
<AppExtensionDebugBundleId />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="MultilingualAppToolkit">
<MultilingualAppToolkitVersion>4.0</MultilingualAppToolkitVersion>
@ -23,6 +23,7 @@
<PackageReference Include="Prism.DryIoc.Forms" Version="8.0.0.1909" />
<PackageReference Include="Xamarin.Forms" Version="4.8.0.1687" />
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Page\MachinesPage.xaml.cs">
@ -83,4 +84,4 @@
<Folder Include="Base\" />
<Folder Include="Behaviour\" />
</ItemGroup>
</Project>
</Project>

165
FabAccessAPI/Auth.cs Normal file
View File

@ -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 `<userid>@<REALM>`
public AuthCId Authcid;
/// Contains the Authorization ID
///
/// This is the identifier of the user to *authenticate as*. This in several cases is different
/// to the `authcid`:
/// If somebody wants to authenticate as somebody else, su-style.
/// If a person wants to authenticate as a higher-permissions account, e.g. foo may set authzid foo+admin
/// to split normal user and "admin" accounts.
/// If a method requires a specific authcid that is different from the identifier of the user
/// to authenticate as, e.g. GSSAPI, x509 client certificates, API TOKEN authentication.
public AuthZId Authzid;
/// Contains the authentication method used
///
/// For the most part this is the SASL method
public string AuthMethod;
/// Method-specific key-value pairs
///
/// Each method can use their own key-value pairs.
/// E.g. EXTERNAL encodes the actual method used (x509 client certs, UID/GID for unix sockets,
/// ...)
public Dictionary<string, string> Kvs;
}
// Authentication has two parts: Granting the authentication itself and then performing the
// authentication.
// Granting the authentication checks if
// a) the given authcid fits with the given (authMethod, kvs). In general a failure here indicates
// a programming failure — the authcid come from the same source as that tuple
// b) the given authcid may authenticate as the given authzid. E.g. if a given client certificate
// has been configured for that user, if a GSSAPI user maps to a given user,
public enum AuthError {
/// Authentication ID is bad/unknown/..
BadAuthcid,
/// Authorization ID is unknown/..
BadAuthzid,
/// Authorization ID is not of form user+uid@realm
MalformedAuthzid,
/// User may not use that authorization id
NotAllowedAuthzid,
}
public class UnauthorizedException : Exception{}
public class UnsupportedMechanismException : Exception{}
/// <summary>
/// THIS IS VERY INCOMPLETE!
/// </summary>
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<IReadOnlyList<string>> GetMechanisms() {
return _authCap.Mechanisms();
}
public async Task<bool> Authenticate(string mech, Dictionary<string, object> properties) {
var m = SaslFactory.Create(mech);
foreach (KeyValuePair<string, object> 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;
}
}
}

View File

@ -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
/// <summary>
///
/// </summary>
/// <param name="rpcClient">Should be an already configured and connected TcpRpcClient</param>
public Connection(TcpRpcClient rpcClient) {
_rpcClient = rpcClient;
_bootstrapCap = _rpcClient.GetMain<IBootstrap>();
_Log.Debug($"Done bootstraping API connection.");
}
/// <summary>
/// Authenticate this connection.
/// Calling this more then once is UB
/// </summary>
/// <param name="mech">The desired authentication mechanism</param>
/// <param name="kvs">Key-Value data specific to the mechanism</param>
/// <returns></returns>
public async Task Auth(string mech, Dictionary<string, object> 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);
}
/// <summary>
/// Get a wrapped capability to interact with machines
/// </summary>
/// <returns>A wrapped capability to interact with machines</returns>
public async Task<Machines> AccessMachines() {
_machines ??= new Machines((await _bootstrapCap.Machines().ConfigureAwait(false)));
return _machines;
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Capnp.Net.Runtime" Version="1.3.118" />
<PackageReference Include="CapnpC.CSharp.MsBuild.Generation" Version="1.3.118" />
<PackageReference Include="log4net" Version="2.0.12" />
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\external\SASL\S22.Sasl.csproj" />
</ItemGroup>
</Project>

204
FabAccessAPI/Machines.cs Normal file
View File

@ -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 { }
/// <summary>
/// Wraps a capability for accessing the Machines subsystem of BFFH
/// </summary>
public class Machines {
#region Log
private static readonly log4net.ILog _Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
#endregion
private IMachines _machinesCap;
/// <summary>
/// Constructs the Wrapper Class from a given capability.
/// </summary>
/// <param name="machinesCap">The capability that should be wrapped.</param>
public Machines(IMachines machinesCap) {
_machinesCap = machinesCap;
}
/// <summary>
/// List of all machines that BFFH knows about the user has been granted at least read access on
/// </summary>
/// <returns>ReadOnlyList of available Machines</returns>
public async Task<IReadOnlyList<Machine>?> ListMachines() {
return (await _machinesCap.ListMachines().ConfigureAwait(false)).Select(x => new Machine(x)) as IReadOnlyList<Machine>;
}
/// <summary>
/// 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
/// </summary>
/// <param name="name">Name of the Machine</param>
/// <returns>The Machine we requested</returns>
public async Task<Machine> 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);
}
}
/// <summary>
/// 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.
/// </summary>
public class Machine {
private Schema.Machine _machine;
/// <summary>
/// Constructs the Wrapper Class from a given capability
/// </summary>
/// <param name="machine">The capability that should be wrapped.</param>
public Machine(Schema.Machine machine) {
_machine = machine;
}
// read operations
/// <summary>
/// Get the MInfo Struct for the Machine.
/// This contains everything BFFH knows about the Machine.
/// </summary>
/// <exception cref="UnauthorizedException"></exception>
/// <returns>The MInfo Struct describing the Machine</returns>
public async Task<Schema.Machine.MInfo> GetMInfo() {
var readCap = _machine.Read;
if (readCap == null) {
throw new UnauthorizedException();
}
return (await _machine.Read.Info().ConfigureAwait(false)).Item1;
}
//write operations
/// <summary>
/// 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
/// </summary>
/// <exception cref="UnauthorizedException"></exception>
/// <returns>Capability to give back the machine</returns>
public Task<Schema.Machine.WriteInterface.IGiveBack> Use() {
var writeCap = _machine.Write;
if (writeCap == null) {
throw new UnauthorizedException();
}
return writeCap.Use();
}
/// <summary>
/// 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.
/// </summary>
/// <exception cref="UnauthorizedException"></exception>
/// <returns>Capability to give back the machine</returns>
public Task<Schema.Machine.WriteInterface.IGiveBack> 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
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// Forcefully set a machine state.
/// </summary>
/// <param name="state">The desired machine state.</param>
public async void ForceSetState(State state) {
var adminCap = _machine.Admin;
if (adminCap == null) {
throw new UnauthorizedException();
}
await adminCap.ForceSetState(state).ConfigureAwait(false);
}
/// <summary>
/// Set the given user as current responsible
/// </summary>
/// <param name="user">The user</param>
public async void ForceSetUser(String user) {
var adminCap = _machine.Admin;
if (adminCap == null) {
throw new UnauthorizedException();
}
await adminCap.ForceSetUser(user).ConfigureAwait(false);
}
}
}

View File

@ -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
}
}

1
FabAccessAPI/schema Submodule

@ -0,0 +1 @@
Subproject commit 4adb05341763b96a43440a6a96e0d9959ba71e89

View File

@ -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<string, object>{{"Username", "Testuser"}, {"Password", "secret"}});
}
[Test]
public async Task Machines() {
await _connection.Auth("PLAIN", new Dictionary<string, object>{{"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()}");
}
}
}

View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="5.0.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FabAccessAPI\FabAccessAPI.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.ConsoleAppender">
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="ALL" />
<appender-ref ref="A1" />
</root>
</log4net>

View File

@ -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

1
external/SASL vendored Submodule

@ -0,0 +1 @@
Subproject commit cb9a9919c971e00c52732fba983e82d795fbe4ad