Skip to content
Snippets Groups Projects
Commit 1b2f8719 authored by Klaus Fischer's avatar Klaus Fischer
Browse files

Merge from implementation branch

parents a91bad56 7cf1d6c7
No related branches found
No related tags found
1 merge request!3Feature/440 mvp net sdk part 1
Showing
with 2386 additions and 32 deletions
...@@ -25,13 +25,24 @@ ...@@ -25,13 +25,24 @@
## Links ## Links
### Documentation
- [SDK-Konzept im Wiki](https://wiki.fit-connect.fitko.dev/de/Konzeption/Konzeption_SDK) - [SDK-Konzept im Wiki](https://wiki.fit-connect.fitko.dev/de/Konzeption/Konzeption_SDK)
- [inoffizielles Python-SDK](https://github.com/codedust/fitconnect-sdk-python) - [inoffizielles Python-SDK](https://github.com/codedust/fitconnect-sdk-python)
- [Project management](https://wiki.fit-connect.fitko.dev/de/PM_PUBLIC/Projektvorgehensmodell) - [Project management](https://wiki.fit-connect.fitko.dev/de/PM_PUBLIC/Projektvorgehensmodell)
- [Wiki SDK Description](https://wiki.fit-connect.fitko.dev/de/PM_PUBLIC/Epics/SDK_Initialisierung) - [Wiki SDK Description](https://wiki.fit-connect.fitko.dev/de/PM_PUBLIC/Epics/SDK_Initialisierung)
- [Containing GitLab](https://git.fitko.de/) - [Containing GitLab](https://git.fitko.de/)
- [Board filtered for SDK](https://git.fitko.de/fit-connect/planning/-/boards/44?label_name%5B%5D=component%3A%3ASDK) - [Board filtered for SDK](https://git.fitko.de/fit-connect/planning/-/boards/44?label_name%5B%5D=component%3A%3ASDK)
- [Documentation](https://docs.fitko.de/fit-connect/docs/getting-started/first-steps/) - [FitConnect First Steps](https://docs.fitko.de/fit-connect/docs/getting-started/first-steps/)
- [Security Event Token Requirements](https://wiki.fit-connect.fitko.dev/de/Konzeption/Security_Event_Token_Future) - [Security Event Token Requirements](https://wiki.fit-connect.fitko.dev/de/Konzeption/Security_Event_Token_Future)
- [glossary](https://docs.fitko.de/fit-connect/docs/glossary/) - [glossary](https://docs.fitko.de/fit-connect/docs/glossary/)
### Helper
- [Create C# class from JSON schema](https://app.quicktype.io/?l=csharp)
## Values for testing
| Topic | Value | Description |
|:-------------------|:---------------|:----------------------------------------|
| Leika Key | 99099002067003 | Deutsche Staatsangehörigkeit beantragen |
| Gemeinde Schlüssel | 09 3 72 126 | Furth im Wald |
\ No newline at end of file
...@@ -9,6 +9,7 @@ var clientSecret = ""; ...@@ -9,6 +9,7 @@ var clientSecret = "";
PublicKey publicKey; PublicKey publicKey;
ILogger _logger; ILogger _logger;
Client client; Client client;
X509Certificate2 certificate;
/* /*
* The easy way to call the FitConnect API * The easy way to call the FitConnect API
...@@ -29,8 +30,8 @@ async Task AbstractCall() { ...@@ -29,8 +30,8 @@ async Task AbstractCall() {
void FluentSenderCall() { void FluentSenderCall() {
client.Sender client.Sender
.Authenticate(clientId!, clientSecret!) .Authenticate(clientId!, clientSecret!)
.CreateSubmission(new Submission()) .CreateSubmission(new Submission { Attachments = new List<Attachment>() })
.UploadAttachments(new List<Attachment>()) .UploadAttachments()
.SendSubmission(new Metadata(), new Data()); .SendSubmission(new Metadata(), new Data());
} }
...@@ -61,7 +62,7 @@ void FluentSubscriberCall() { ...@@ -61,7 +62,7 @@ void FluentSubscriberCall() {
async Task DetailSenderCall() { async Task DetailSenderCall() {
var sender = var sender =
new Sender( new Sender(
FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development)); FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development), certificate);
var submissionDto = sender.AddSubmission(new Submission()); var submissionDto = sender.AddSubmission(new Submission());
var encryptedAttachments = sender.Encrypt(publicKey, new List<Attachment>()); var encryptedAttachments = sender.Encrypt(publicKey, new List<Attachment>());
...@@ -79,7 +80,7 @@ async Task DetailSenderCall() { ...@@ -79,7 +80,7 @@ async Task DetailSenderCall() {
async Task DetailSubscriberCall() { async Task DetailSubscriberCall() {
var subscriber = var subscriber =
new Subscriber( new Subscriber(
FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development)); FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development), certificate);
var submissions = await subscriber.GetSubmissionsAsync("destinationId"); var submissions = await subscriber.GetSubmissionsAsync("destinationId");
/* /*
.... ....
......
using System; using System;
using System.IO; using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using FitConnect; using FitConnect;
using FitConnect.Security;
using FluentAssertions; using FluentAssertions;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
...@@ -44,7 +48,8 @@ public class SenderTest { ...@@ -44,7 +48,8 @@ public class SenderTest {
[SetUp] [SetUp]
public void Setup() { public void Setup() {
_sender = new Sender( _sender = new Sender(
FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development)); FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development),
RsaEncryption.CreateSelfSignedCertificate());
} }
[Test] [Test]
......
...@@ -11,24 +11,26 @@ public abstract class FunctionalBaseClass { ...@@ -11,24 +11,26 @@ public abstract class FunctionalBaseClass {
protected readonly FitConnectApiService ApiService; protected readonly FitConnectApiService ApiService;
public readonly IEncryption Encryption; public readonly IEncryption Encryption;
protected readonly ILogger? Logger; protected readonly ILogger? Logger;
protected readonly X509Certificate2 Certificate;
internal Client Owner { get; set; } internal Client Owner { get; set; }
/// <summary> /// <summary>
/// Constructor for the FunctionalBaseClass /// Constructor for the FunctionalBaseClass
/// </summary> /// </summary>
/// <param name="logger">ILogger implementation</param>
/// <param name="endpoints">FitConnect endpoints</param> /// <param name="endpoints">FitConnect endpoints</param>
/// <param name="certificate">The Encryption certificate</param> /// <param name="certificate">The Encryption certificate</param>
/// <param name="logger">ILogger implementation</param>
/// <example> /// <example>
/// new Sender(logger, FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development)) /// new Sender(logger, FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development))
/// </example> /// </example>
protected FunctionalBaseClass(ILogger? logger, FitConnectEndpoints? endpoints, protected FunctionalBaseClass(FitConnectEndpoints? endpoints,
X509Certificate2? certificate) { X509Certificate2 certificate, ILogger? logger) {
Endpoints = endpoints ?? Endpoints = endpoints ??
FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development); FitConnectEndpoints.Create(FitConnectEndpoints.EndpointType.Development);
Logger = logger; Logger = logger;
Certificate = certificate;
Encryption = new RsaEncryption(logger, certificate); Encryption = new RsaEncryption(logger, certificate);
ApiService = new FitConnectApiService(Endpoints, logger); ApiService = new FitConnectApiService(Endpoints, logger);
} }
...@@ -43,7 +45,8 @@ public abstract class FunctionalBaseClass { ...@@ -43,7 +45,8 @@ public abstract class FunctionalBaseClass {
/// <param name="clientSecret"></param> /// <param name="clientSecret"></param>
/// <param name="scope"></param> /// <param name="scope"></param>
/// <returns></returns> /// <returns></returns>
public Task<OAuthAccessToken?> AuthenticateAsync(string clientId, string clientSecret, string? scope) { public Task<OAuthAccessToken?> AuthenticateAsync(string clientId, string clientSecret,
string? scope) {
return ApiService.OAuthService.AuthenticateAsync(clientId, clientSecret, scope); return ApiService.OAuthService.AuthenticateAsync(clientId, clientSecret, scope);
} }
......
...@@ -30,6 +30,7 @@ public class FluentSender : Sender { ...@@ -30,6 +30,7 @@ public class FluentSender : Sender {
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
public FluentSender CreateSubmission(Submission submission) { public FluentSender CreateSubmission(Submission submission) {
this.Submission = submission;
if (token == null) if (token == null)
throw new InvalidOperationException("You must authenticate first."); throw new InvalidOperationException("You must authenticate first.");
...@@ -41,26 +42,36 @@ public class FluentSender : Sender { ...@@ -41,26 +42,36 @@ public class FluentSender : Sender {
return this; return this;
} }
public Submission Submission { get; set; }
public FluentSender SendSubmission() { public FluentSender SendSubmission() {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public FluentSender UploadAttachments(List<Attachment> attachments) { public FluentSender UploadAttachments() {
if (NewSubmission == null) if (NewSubmission == null)
throw new InvalidOperationException("You must create a submission first."); throw new InvalidOperationException("You must create a submission first.");
if (attachments.Count == 0) if (Submission.Attachments.Count == 0)
return this; return this;
var encryptedAttachments = Encrypt(PublicKey, attachments); var encryptedAttachments = Encrypt(PublicKey, Submission.Attachments);
UploadAttachmentsAsync(encryptedAttachments).Wait(); UploadAttachmentsAsync(encryptedAttachments).Wait();
return this; return this;
} }
public FluentSender SendSubmission(Metadata metadata, Data? data = null) { public FluentSender SendSubmission(Metadata metadata, Data? data = null) {
throw new NotImplementedException(); if (NewSubmission == null)
throw new InvalidOperationException("You must create a submission first.");
Submission.Metadata = metadata;
Submission.Data = data;
var submitSubmissionDto = CreateSubmitSubmissionDto(Submission);
ApiService.SubmissionService.SubmitSubmission(Submission.Id, submitSubmissionDto);
return this;
} }
public FluentSender(FitConnectEndpoints endpoints, X509Certificate2? certificate = null, public FluentSender(FitConnectEndpoints endpoints, X509Certificate2? certificate = null,
ILogger? logger = null) : base(endpoints, certificate, logger) { ILogger? logger = null) : base(endpoints, certificate, logger) {
......
namespace FitConnect.Models;
/// <summary>
/// Representation of FitConnect error responses
/// </summary>
public class FitConnectException : Exception {
public enum ErrorTypeEnum {
Unknown,
}
public ErrorTypeEnum ErrorType { get; set; }
public FitConnectException(string message, ErrorTypeEnum errorType = ErrorTypeEnum.Unknown,
Exception? innerException = null) : base(message, innerException) {
ErrorType = errorType;
}
}
using System.Security.Cryptography.X509Certificates;
using FitConnect.Models;
namespace FitConnect.Models; namespace FitConnect.Models;
public record Metadata; public interface IEncrypt {
public string Encrypt(PublicKey publicKey);
}
public class Metadata : IEncrypt {
public string Encrypt(PublicKey publicKey) {
return "";
}
}
public record Data; public class Data : IEncrypt {
public string Encrypt(PublicKey publicKey) {
return "";
}
}
...@@ -2,7 +2,7 @@ using FitConnect.Services.Models; ...@@ -2,7 +2,7 @@ using FitConnect.Services.Models;
namespace FitConnect.Models; namespace FitConnect.Models;
public record ServiceType { public class ServiceType {
public string? Name { get; set; } public string? Name { get; set; }
public string? Description { get; set; } public string? Description { get; set; }
...@@ -23,4 +23,7 @@ public record ServiceType { ...@@ -23,4 +23,7 @@ public record ServiceType {
Name = model.Name Name = model.Name
}; };
} }
public bool IsValid() =>
(!string.IsNullOrWhiteSpace(Name)) && (!string.IsNullOrWhiteSpace(Identifier));
} }
using System.Security.Cryptography.X509Certificates;
using FitConnect.Services.Models; using FitConnect.Services.Models;
namespace FitConnect.Models; namespace FitConnect.Models;
public record Submission { public class Submission {
public string? Id { get; set; } public string Id { get; set; }
public string DestinationId { get; init; } public string DestinationId { get; init; }
public string? CaseId { get; set; } public string? CaseId { get; set; }
...@@ -13,9 +14,22 @@ public record Submission { ...@@ -13,9 +14,22 @@ public record Submission {
public Callback? Callback { get; set; } public Callback? Callback { get; set; }
public bool IsSubmissionReadyToAdd(out string? error) { public bool IsSubmissionReadyToAdd(out string? error) {
error = null; var innerError = "";
return true; if (string.IsNullOrEmpty(DestinationId)) innerError += "DestinationId is required\r\n";
if (ServiceType.IsValid()) {
innerError += "ServiceType is invalid\r\n";
}
if (string.IsNullOrWhiteSpace(innerError)) {
error = null;
return true;
}
error = innerError.Trim();
return false;
} }
public bool IsSubmissionReadyToSend() { public bool IsSubmissionReadyToSend() {
...@@ -26,7 +40,9 @@ public record Submission { ...@@ -26,7 +40,9 @@ public record Submission {
/// <summary> /// <summary>
/// Fachdaten /// Fachdaten
/// </summary> /// </summary>
public string? Data { get; set; } public Data? Data { get; set; }
public Metadata Metadata { get; set; }
public static explicit operator Submission(SubmissionForPickupDto dto) { public static explicit operator Submission(SubmissionForPickupDto dto) {
return new() { return new() {
......
...@@ -7,9 +7,10 @@ using Microsoft.Extensions.Logging; ...@@ -7,9 +7,10 @@ using Microsoft.Extensions.Logging;
namespace FitConnect; namespace FitConnect;
public partial class Sender : FunctionalBaseClass { public partial class Sender : FunctionalBaseClass {
public Sender(FitConnectEndpoints endpoints, public Sender(FitConnectEndpoints endpoints,
X509Certificate2? certificate = null, ILogger? logger = null) : base(logger, endpoints, X509Certificate2 certificate, ILogger? logger = null) : base(endpoints,
certificate) { certificate, logger) {
} }
...@@ -144,4 +145,11 @@ public partial class Sender : FunctionalBaseClass { ...@@ -144,4 +145,11 @@ public partial class Sender : FunctionalBaseClass {
public async Task<bool> SubmitAsync(string encryptedData, string encryptedMetadata) { public async Task<bool> SubmitAsync(string encryptedData, string encryptedMetadata) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected SubmitSubmissionDto CreateSubmitSubmissionDto(Submission submission) {
return new() {
EncryptedData = submission.Data?.Encrypt(this.Certificate.PublicKey),
EncryptedMetadata = submission.Metadata.Encrypt(Certificate.PublicKey),
};
}
} }
...@@ -10,8 +10,8 @@ namespace FitConnect; ...@@ -10,8 +10,8 @@ namespace FitConnect;
public class Subscriber : FunctionalBaseClass { public class Subscriber : FunctionalBaseClass {
public Subscriber(FitConnectEndpoints endpoints, public Subscriber(FitConnectEndpoints endpoints,
X509Certificate2? certificate = null, ILogger? logger = null) : base(logger, endpoints, X509Certificate2 certificate, ILogger? logger = null) : base(endpoints,
certificate) { certificate, logger) {
} }
...@@ -110,4 +110,4 @@ public class Subscriber : FunctionalBaseClass { ...@@ -110,4 +110,4 @@ public class Subscriber : FunctionalBaseClass {
int limit = 100) { int limit = 100) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
\ No newline at end of file
...@@ -7,4 +7,8 @@ ...@@ -7,4 +7,8 @@
<RootNamespace>FitConnect.Models</RootNamespace> <RootNamespace>FitConnect.Models</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project> </Project>
...@@ -7,7 +7,7 @@ public class FitConnectApiService { ...@@ -7,7 +7,7 @@ public class FitConnectApiService {
protected readonly DestinationService DestinationService; protected readonly DestinationService DestinationService;
public readonly OAuthService OAuthService; public readonly OAuthService OAuthService;
public readonly RouteService RouteService; public readonly RouteService RouteService;
protected readonly SubmissionService SubmissionService; public readonly SubmissionService SubmissionService;
public FitConnectApiService(FitConnectEndpoints endpoints, ILogger? logger = null) { public FitConnectApiService(FitConnectEndpoints endpoints, ILogger? logger = null) {
CasesService = new CasesService(endpoints.SubmissionUrl); CasesService = new CasesService(endpoints.SubmissionUrl);
......
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using FitConnect;
//
// var metadata = Metadata.FromJson(jsonString);
namespace FitConnect.Models.Api.Metadata
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Metadata
{
/// <summary>
/// Eine Struktur, um zusätzliche Informationen zu hinterlegen
/// </summary>
[JsonProperty("additionalReferenceInfo", NullValueHandling = NullValueHandling.Ignore)]
public AdditionalReferenceInfo AdditionalReferenceInfo { get; set; }
/// <summary>
/// Eine Liste aller Identifikationsnachweise der Einreichung.
/// </summary>
[JsonProperty("authenticationInformation", NullValueHandling = NullValueHandling.Ignore)]
public List<AuthenticationInformation> AuthenticationInformation { get; set; }
/// <summary>
/// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
/// Fachdaten.
/// </summary>
[JsonProperty("contentStructure")]
public ContentStructure ContentStructure { get; set; }
/// <summary>
/// Dieses Objekt enthält die Informationen vom Bezahldienst.
/// </summary>
[JsonProperty("paymentInformation", NullValueHandling = NullValueHandling.Ignore)]
public PaymentInformation PaymentInformation { get; set; }
/// <summary>
/// Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit
/// einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine
/// LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer
/// anderen eindeutigen Schema-URN beschrieben werden.
/// </summary>
[JsonProperty("publicServiceType", NullValueHandling = NullValueHandling.Ignore)]
public Verwaltungsleistung PublicServiceType { get; set; }
[JsonProperty("replyChannel", NullValueHandling = NullValueHandling.Ignore)]
public ReplyChannel ReplyChannel { get; set; }
}
/// <summary>
/// Eine Struktur, um zusätzliche Informationen zu hinterlegen
/// </summary>
public partial class AdditionalReferenceInfo
{
/// <summary>
/// Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der
/// Einreichung des Antrags über FIT-Connect sein.
/// </summary>
[JsonProperty("applicationDate", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? ApplicationDate { get; set; }
/// <summary>
/// Eine Referenz zum Vorgang im sendenden System, um bei Problemen und Rückfragen
/// außerhalb von FIT-Connect den Vorgang im dortigen System schneller zu identifizieren.
/// </summary>
[JsonProperty("senderReference", NullValueHandling = NullValueHandling.Ignore)]
public string SenderReference { get; set; }
}
/// <summary>
/// Eine Struktur, die einen Identifikationsnachweis beschreibt.
/// </summary>
public partial class AuthenticationInformation
{
/// <summary>
/// Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Definiert die Art des Identifikationsnachweises.
/// </summary>
[JsonProperty("type")]
public AuthenticationInformationType Type { get; set; }
/// <summary>
/// semver kompatible Versionsangabe des genutzten Nachweistyps.
/// </summary>
[JsonProperty("version")]
public string Version { get; set; }
}
/// <summary>
/// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
/// Fachdaten.
/// </summary>
public partial class ContentStructure
{
[JsonProperty("attachments")]
public List<Attachment> Attachments { get; set; }
/// <summary>
/// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
/// </summary>
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public Data Data { get; set; }
}
/// <summary>
/// Eine in der Einreichung enthaltene Anlage.
/// </summary>
public partial class Attachment
{
/// <summary>
/// Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.
/// </summary>
[JsonProperty("attachmentId")]
public Guid AttachmentId { get; set; }
/// <summary>
/// Optionale Beschreibung der Anlage
/// </summary>
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string Description { get; set; }
/// <summary>
/// Ursprünglicher Dateiname bei Erzeugung oder Upload
/// </summary>
[JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
public string Filename { get; set; }
/// <summary>
/// Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch
/// Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
[JsonProperty("hash")]
public AttachmentHash Hash { get; set; }
/// <summary>
/// Internet Media Type gemäß RFC 2045, z. B. application/pdf.
/// </summary>
[JsonProperty("mimeType")]
public string MimeType { get; set; }
/// <summary>
/// Zweck/Art der Anlage
/// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
/// - attachment: Anlage, die von einem Bürger hochgeladen wurde
/// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
/// </summary>
[JsonProperty("purpose")]
public Purpose Purpose { get; set; }
[JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
public AttachmentSignature Signature { get; set; }
}
/// <summary>
/// Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch
/// Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
public partial class AttachmentHash
{
/// <summary>
/// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
[JsonProperty("type")]
public HashType Type { get; set; }
}
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
public partial class AttachmentSignature
{
/// <summary>
/// Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder
/// Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei
/// Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.
/// </summary>
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
/// <summary>
/// Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des
/// Fachdatensatzes bzw. der Anlage (`false`) übertragen wird. Wenn der Wert `true` ist,
/// dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen.
/// </summary>
[JsonProperty("detachedSignature")]
public bool DetachedSignature { get; set; }
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
[JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
public EidasAdesProfile? EidasAdesProfile { get; set; }
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
[JsonProperty("signatureFormat")]
public SignatureFormat SignatureFormat { get; set; }
}
/// <summary>
/// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
/// </summary>
public partial class Data
{
/// <summary>
/// Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten
/// durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
[JsonProperty("hash")]
public DataHash Hash { get; set; }
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
[JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
public DataSignature Signature { get; set; }
/// <summary>
/// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
/// </summary>
[JsonProperty("submissionSchema")]
public Fachdatenschema SubmissionSchema { get; set; }
}
/// <summary>
/// Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten
/// durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
public partial class DataHash
{
/// <summary>
/// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
[JsonProperty("type")]
public HashType Type { get; set; }
}
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
public partial class DataSignature
{
/// <summary>
/// Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder
/// Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei
/// Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.
/// </summary>
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
/// <summary>
/// Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des
/// Fachdatensatzes bzw. der Anlage (`false`) übertragen wird. Wenn der Wert `true` ist,
/// dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen.
/// </summary>
[JsonProperty("detachedSignature")]
public bool DetachedSignature { get; set; }
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
[JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
public EidasAdesProfile? EidasAdesProfile { get; set; }
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
[JsonProperty("signatureFormat")]
public SignatureFormat SignatureFormat { get; set; }
}
/// <summary>
/// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
/// </summary>
public partial class Fachdatenschema
{
/// <summary>
/// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
/// XSD- oder JSON-Schema).
/// </summary>
[JsonProperty("mimeType")]
public MimeType MimeType { get; set; }
/// <summary>
/// URI des Fachschemas. Wird hier eine URL verwendet, sollte das Schema unter der
/// angegebenen URL abrufbar sein. Eine Verfügbarkeit des Schemas unter der angegebenen URL
/// darf jedoch nicht vorausgesetzt werden.
/// </summary>
[JsonProperty("schemaUri")]
public Uri SchemaUri { get; set; }
}
/// <summary>
/// Dieses Objekt enthält die Informationen vom Bezahldienst.
/// </summary>
public partial class PaymentInformation
{
/// <summary>
/// Bruttobetrag
/// </summary>
[JsonProperty("grossAmount", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(MinMaxValueCheckConverter))]
public double? GrossAmount { get; set; }
/// <summary>
/// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
/// vorhanden / befüllt.
/// </summary>
[JsonProperty("paymentMethod")]
public PaymentMethod PaymentMethod { get; set; }
/// <summary>
/// Weitere Erläuterung zur gewählten Zahlart.
/// </summary>
[JsonProperty("paymentMethodDetail", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
public string PaymentMethodDetail { get; set; }
/// <summary>
/// - INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine
/// Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine
/// Wirkung erzeugt.
/// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
/// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
/// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
/// </summary>
[JsonProperty("status")]
public Status Status { get; set; }
/// <summary>
/// Eine vom Bezahldienst vergebene Transaktions-Id.
/// </summary>
[JsonProperty("transactionId")]
[JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
public string TransactionId { get; set; }
/// <summary>
/// Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen.
/// </summary>
[JsonProperty("transactionReference")]
public string TransactionReference { get; set; }
/// <summary>
/// Zeitstempel der erfolgreichen Durchführung der Bezahlung.
/// </summary>
[JsonProperty("transactionTimestamp", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? TransactionTimestamp { get; set; }
/// <summary>
/// Die Rest-URL der Payment Transaction für die Statusabfrage.
/// </summary>
[JsonProperty("transactionUrl", NullValueHandling = NullValueHandling.Ignore)]
public Uri TransactionUrl { get; set; }
}
/// <summary>
/// Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit
/// einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine
/// LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer
/// anderen eindeutigen Schema-URN beschrieben werden.
/// </summary>
public partial class Verwaltungsleistung
{
/// <summary>
/// (Kurz-)Beschreibung der Verwaltungsleistung
/// </summary>
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string Description { get; set; }
/// <summary>
/// URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier
/// `urn:de:fim:leika:leistung:` vorangestellt werden.
/// </summary>
[JsonProperty("identifier")]
[JsonConverter(typeof(FluffyMinMaxLengthCheckConverter))]
public string Identifier { get; set; }
/// <summary>
/// Name/Bezeichnung der Verwaltungsleistung
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
}
public partial class ReplyChannel
{
/// <summary>
/// Akkreditierte Anbieter siehe
/// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
/// </summary>
[JsonProperty("deMail", NullValueHandling = NullValueHandling.Ignore)]
public DeMail DeMail { get; set; }
/// <summary>
/// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
/// </summary>
[JsonProperty("elster", NullValueHandling = NullValueHandling.Ignore)]
public Elster Elster { get; set; }
[JsonProperty("eMail", NullValueHandling = NullValueHandling.Ignore)]
public EMail EMail { get; set; }
/// <summary>
/// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
/// </summary>
[JsonProperty("fink", NullValueHandling = NullValueHandling.Ignore)]
public Fink Fink { get; set; }
}
/// <summary>
/// Akkreditierte Anbieter siehe
/// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
/// </summary>
public partial class DeMail
{
[JsonProperty("address")]
public string Address { get; set; }
}
public partial class EMail
{
[JsonProperty("address")]
public string Address { get; set; }
/// <summary>
/// Hilfe zur Erstellung gibt es in der Dokumentation unter
/// https://docs.fitko.de/fit-connect/details/pgp-export
/// </summary>
[JsonProperty("pgpPublicKey", NullValueHandling = NullValueHandling.Ignore)]
public string PgpPublicKey { get; set; }
}
/// <summary>
/// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
/// </summary>
public partial class Elster
{
[JsonProperty("accountId")]
public string AccountId { get; set; }
[JsonProperty("geschaeftszeichen", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(TentacledMinMaxLengthCheckConverter))]
public string Geschaeftszeichen { get; set; }
[JsonProperty("lieferTicket", NullValueHandling = NullValueHandling.Ignore)]
public string LieferTicket { get; set; }
}
/// <summary>
/// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
/// </summary>
public partial class Fink
{
/// <summary>
/// FINK Postfachadresse
/// </summary>
[JsonProperty("finkPostfachRef")]
[JsonConverter(typeof(StickyMinMaxLengthCheckConverter))]
public string FinkPostfachRef { get; set; }
/// <summary>
/// URL des Servicekontos, in dem das Ziel-Postfach liegt
/// </summary>
[JsonProperty("host", NullValueHandling = NullValueHandling.Ignore)]
public Uri Host { get; set; }
}
/// <summary>
/// Definiert die Art des Identifikationsnachweises.
/// </summary>
public enum AuthenticationInformationType { IdentificationReport };
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
public enum HashType { Sha512 };
/// <summary>
/// Zweck/Art der Anlage
/// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
/// - attachment: Anlage, die von einem Bürger hochgeladen wurde
/// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
/// </summary>
public enum Purpose { Attachment, Form, Report };
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
public enum EidasAdesProfile { HttpUriEtsiOrgAdes191X2LevelBaselineBB, HttpUriEtsiOrgAdes191X2LevelBaselineBLt, HttpUriEtsiOrgAdes191X2LevelBaselineBLta, HttpUriEtsiOrgAdes191X2LevelBaselineBT };
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
public enum SignatureFormat { Asic, Cms, Json, Pdf, Xml };
/// <summary>
/// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
/// XSD- oder JSON-Schema).
/// </summary>
public enum MimeType { ApplicationJson, ApplicationXml };
/// <summary>
/// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
/// vorhanden / befüllt.
/// </summary>
public enum PaymentMethod { Creditcard, Giropay, Invoice, Other, Paydirect, Paypal };
/// <summary>
/// - INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine
/// Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine
/// Wirkung erzeugt.
/// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
/// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
/// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
/// </summary>
public enum Status { Booked, Canceled, Failed, Initial };
public partial class Metadata
{
public static Metadata FromJson(string json) => JsonConvert.DeserializeObject<Metadata>(json, FitConnect.Models.Api.Metadata.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this Metadata self) => JsonConvert.SerializeObject(self, FitConnect.Models.Api.Metadata.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
AuthenticationInformationTypeConverter.Singleton,
HashTypeConverter.Singleton,
PurposeConverter.Singleton,
EidasAdesProfileConverter.Singleton,
SignatureFormatConverter.Singleton,
MimeTypeConverter.Singleton,
PaymentMethodConverter.Singleton,
StatusConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class AuthenticationInformationTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AuthenticationInformationType) || t == typeof(AuthenticationInformationType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (value == "identificationReport")
{
return AuthenticationInformationType.IdentificationReport;
}
throw new Exception("Cannot unmarshal type AuthenticationInformationType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (AuthenticationInformationType)untypedValue;
if (value == AuthenticationInformationType.IdentificationReport)
{
serializer.Serialize(writer, "identificationReport");
return;
}
throw new Exception("Cannot marshal type AuthenticationInformationType");
}
public static readonly AuthenticationInformationTypeConverter Singleton = new AuthenticationInformationTypeConverter();
}
internal class HashTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(HashType) || t == typeof(HashType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (value == "sha512")
{
return HashType.Sha512;
}
throw new Exception("Cannot unmarshal type HashType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (HashType)untypedValue;
if (value == HashType.Sha512)
{
serializer.Serialize(writer, "sha512");
return;
}
throw new Exception("Cannot marshal type HashType");
}
public static readonly HashTypeConverter Singleton = new HashTypeConverter();
}
internal class PurposeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Purpose) || t == typeof(Purpose?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "attachment":
return Purpose.Attachment;
case "form":
return Purpose.Form;
case "report":
return Purpose.Report;
}
throw new Exception("Cannot unmarshal type Purpose");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (Purpose)untypedValue;
switch (value)
{
case Purpose.Attachment:
serializer.Serialize(writer, "attachment");
return;
case Purpose.Form:
serializer.Serialize(writer, "form");
return;
case Purpose.Report:
serializer.Serialize(writer, "report");
return;
}
throw new Exception("Cannot marshal type Purpose");
}
public static readonly PurposeConverter Singleton = new PurposeConverter();
}
internal class EidasAdesProfileConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EidasAdesProfile) || t == typeof(EidasAdesProfile?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "http://uri.etsi.org/ades/191x2/level/baseline/B-B#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-T#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT;
}
throw new Exception("Cannot unmarshal type EidasAdesProfile");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (EidasAdesProfile)untypedValue;
switch (value)
{
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-B#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-T#");
return;
}
throw new Exception("Cannot marshal type EidasAdesProfile");
}
public static readonly EidasAdesProfileConverter Singleton = new EidasAdesProfileConverter();
}
internal class SignatureFormatConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(SignatureFormat) || t == typeof(SignatureFormat?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "asic":
return SignatureFormat.Asic;
case "cms":
return SignatureFormat.Cms;
case "json":
return SignatureFormat.Json;
case "pdf":
return SignatureFormat.Pdf;
case "xml":
return SignatureFormat.Xml;
}
throw new Exception("Cannot unmarshal type SignatureFormat");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (SignatureFormat)untypedValue;
switch (value)
{
case SignatureFormat.Asic:
serializer.Serialize(writer, "asic");
return;
case SignatureFormat.Cms:
serializer.Serialize(writer, "cms");
return;
case SignatureFormat.Json:
serializer.Serialize(writer, "json");
return;
case SignatureFormat.Pdf:
serializer.Serialize(writer, "pdf");
return;
case SignatureFormat.Xml:
serializer.Serialize(writer, "xml");
return;
}
throw new Exception("Cannot marshal type SignatureFormat");
}
public static readonly SignatureFormatConverter Singleton = new SignatureFormatConverter();
}
internal class MimeTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(MimeType) || t == typeof(MimeType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "application/json":
return MimeType.ApplicationJson;
case "application/xml":
return MimeType.ApplicationXml;
}
throw new Exception("Cannot unmarshal type MimeType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (MimeType)untypedValue;
switch (value)
{
case MimeType.ApplicationJson:
serializer.Serialize(writer, "application/json");
return;
case MimeType.ApplicationXml:
serializer.Serialize(writer, "application/xml");
return;
}
throw new Exception("Cannot marshal type MimeType");
}
public static readonly MimeTypeConverter Singleton = new MimeTypeConverter();
}
internal class MinMaxValueCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(double) || t == typeof(double?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<double>(reader);
if (value >= 0.01)
{
return value;
}
throw new Exception("Cannot unmarshal type double");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (double)untypedValue;
if (value >= 0.01)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type double");
}
public static readonly MinMaxValueCheckConverter Singleton = new MinMaxValueCheckConverter();
}
internal class PaymentMethodConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(PaymentMethod) || t == typeof(PaymentMethod?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "CREDITCARD":
return PaymentMethod.Creditcard;
case "GIROPAY":
return PaymentMethod.Giropay;
case "INVOICE":
return PaymentMethod.Invoice;
case "OTHER":
return PaymentMethod.Other;
case "PAYDIRECT":
return PaymentMethod.Paydirect;
case "PAYPAL":
return PaymentMethod.Paypal;
}
throw new Exception("Cannot unmarshal type PaymentMethod");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (PaymentMethod)untypedValue;
switch (value)
{
case PaymentMethod.Creditcard:
serializer.Serialize(writer, "CREDITCARD");
return;
case PaymentMethod.Giropay:
serializer.Serialize(writer, "GIROPAY");
return;
case PaymentMethod.Invoice:
serializer.Serialize(writer, "INVOICE");
return;
case PaymentMethod.Other:
serializer.Serialize(writer, "OTHER");
return;
case PaymentMethod.Paydirect:
serializer.Serialize(writer, "PAYDIRECT");
return;
case PaymentMethod.Paypal:
serializer.Serialize(writer, "PAYPAL");
return;
}
throw new Exception("Cannot marshal type PaymentMethod");
}
public static readonly PaymentMethodConverter Singleton = new PaymentMethodConverter();
}
internal class PurpleMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length >= 1 && value.Length <= 36)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length >= 1 && value.Length <= 36)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly PurpleMinMaxLengthCheckConverter Singleton = new PurpleMinMaxLengthCheckConverter();
}
internal class StatusConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Status) || t == typeof(Status?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "BOOKED":
return Status.Booked;
case "CANCELED":
return Status.Canceled;
case "FAILED":
return Status.Failed;
case "INITIAL":
return Status.Initial;
}
throw new Exception("Cannot unmarshal type Status");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (Status)untypedValue;
switch (value)
{
case Status.Booked:
serializer.Serialize(writer, "BOOKED");
return;
case Status.Canceled:
serializer.Serialize(writer, "CANCELED");
return;
case Status.Failed:
serializer.Serialize(writer, "FAILED");
return;
case Status.Initial:
serializer.Serialize(writer, "INITIAL");
return;
}
throw new Exception("Cannot marshal type Status");
}
public static readonly StatusConverter Singleton = new StatusConverter();
}
internal class FluffyMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length >= 7 && value.Length <= 255)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length >= 7 && value.Length <= 255)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly FluffyMinMaxLengthCheckConverter Singleton = new FluffyMinMaxLengthCheckConverter();
}
internal class TentacledMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length <= 10)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length <= 10)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly TentacledMinMaxLengthCheckConverter Singleton = new TentacledMinMaxLengthCheckConverter();
}
internal class StickyMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length <= 150)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length <= 150)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly StickyMinMaxLengthCheckConverter Singleton = new StickyMinMaxLengthCheckConverter();
}
}
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using FitConnect;
//
// var metadata = Metadata.FromJson(jsonString);
namespace FitConnect.Models.Api.Set
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Metadata
{
/// <summary>
/// Eine Struktur, um zusätzliche Informationen zu hinterlegen
/// </summary>
[JsonProperty("additionalReferenceInfo", NullValueHandling = NullValueHandling.Ignore)]
public AdditionalReferenceInfo AdditionalReferenceInfo { get; set; }
/// <summary>
/// Eine Liste aller Identifikationsnachweise der Einreichung.
/// </summary>
[JsonProperty("authenticationInformation", NullValueHandling = NullValueHandling.Ignore)]
public List<AuthenticationInformation> AuthenticationInformation { get; set; }
/// <summary>
/// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
/// Fachdaten.
/// </summary>
[JsonProperty("contentStructure")]
public ContentStructure ContentStructure { get; set; }
/// <summary>
/// Dieses Objekt enthält die Informationen vom Bezahldienst.
/// </summary>
[JsonProperty("paymentInformation", NullValueHandling = NullValueHandling.Ignore)]
public PaymentInformation PaymentInformation { get; set; }
/// <summary>
/// Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit
/// einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine
/// LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer
/// anderen eindeutigen Schema-URN beschrieben werden.
/// </summary>
[JsonProperty("publicServiceType", NullValueHandling = NullValueHandling.Ignore)]
public Verwaltungsleistung PublicServiceType { get; set; }
[JsonProperty("replyChannel", NullValueHandling = NullValueHandling.Ignore)]
public ReplyChannel ReplyChannel { get; set; }
}
/// <summary>
/// Eine Struktur, um zusätzliche Informationen zu hinterlegen
/// </summary>
public partial class AdditionalReferenceInfo
{
/// <summary>
/// Das Datum der Antragstellung. Das Datum muss nicht zwingend identisch mit dem Datum der
/// Einreichung des Antrags über FIT-Connect sein.
/// </summary>
[JsonProperty("applicationDate", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? ApplicationDate { get; set; }
/// <summary>
/// Eine Referenz zum Vorgang im sendenden System, um bei Problemen und Rückfragen
/// außerhalb von FIT-Connect den Vorgang im dortigen System schneller zu identifizieren.
/// </summary>
[JsonProperty("senderReference", NullValueHandling = NullValueHandling.Ignore)]
public string SenderReference { get; set; }
}
/// <summary>
/// Eine Struktur, die einen Identifikationsnachweis beschreibt.
/// </summary>
public partial class AuthenticationInformation
{
/// <summary>
/// Der Nachweis wird als Base64Url-kodierte Zeichenkette angegeben.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Definiert die Art des Identifikationsnachweises.
/// </summary>
[JsonProperty("type")]
public AuthenticationInformationType Type { get; set; }
/// <summary>
/// semver kompatible Versionsangabe des genutzten Nachweistyps.
/// </summary>
[JsonProperty("version")]
public string Version { get; set; }
}
/// <summary>
/// Beschreibt die Struktur der zusätzlichen Inhalte der Einreichung, wie Anlagen oder
/// Fachdaten.
/// </summary>
public partial class ContentStructure
{
[JsonProperty("attachments")]
public List<Attachment> Attachments { get; set; }
/// <summary>
/// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
/// </summary>
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public Data Data { get; set; }
}
/// <summary>
/// Eine in der Einreichung enthaltene Anlage.
/// </summary>
public partial class Attachment
{
/// <summary>
/// Innerhalb einer Einreichung eindeutige Id der Anlage im Format einer UUIDv4.
/// </summary>
[JsonProperty("attachmentId")]
public Guid AttachmentId { get; set; }
/// <summary>
/// Optionale Beschreibung der Anlage
/// </summary>
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string Description { get; set; }
/// <summary>
/// Ursprünglicher Dateiname bei Erzeugung oder Upload
/// </summary>
[JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
public string Filename { get; set; }
/// <summary>
/// Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch
/// Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
[JsonProperty("hash")]
public AttachmentHash Hash { get; set; }
/// <summary>
/// Internet Media Type gemäß RFC 2045, z. B. application/pdf.
/// </summary>
[JsonProperty("mimeType")]
public string MimeType { get; set; }
/// <summary>
/// Zweck/Art der Anlage
/// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
/// - attachment: Anlage, die von einem Bürger hochgeladen wurde
/// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
/// </summary>
[JsonProperty("purpose")]
public Purpose Purpose { get; set; }
[JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
public AttachmentSignature Signature { get; set; }
}
/// <summary>
/// Der Hashwert der unverschlüsselten Anlage. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Anlage durch
/// Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
public partial class AttachmentHash
{
/// <summary>
/// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
[JsonProperty("type")]
public HashType Type { get; set; }
}
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
public partial class AttachmentSignature
{
/// <summary>
/// Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder
/// Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei
/// Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.
/// </summary>
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
/// <summary>
/// Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des
/// Fachdatensatzes bzw. der Anlage (`false`) übertragen wird. Wenn der Wert `true` ist,
/// dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen.
/// </summary>
[JsonProperty("detachedSignature")]
public bool DetachedSignature { get; set; }
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
[JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
public EidasAdesProfile? EidasAdesProfile { get; set; }
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
[JsonProperty("signatureFormat")]
public SignatureFormat SignatureFormat { get; set; }
}
/// <summary>
/// Definiert das Schema und die Signatur(-art), die für die Fachdaten verwendet werden.
/// </summary>
public partial class Data
{
/// <summary>
/// Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten
/// durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
[JsonProperty("hash")]
public DataHash Hash { get; set; }
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
[JsonProperty("signature", NullValueHandling = NullValueHandling.Ignore)]
public DataSignature Signature { get; set; }
/// <summary>
/// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
/// </summary>
[JsonProperty("submissionSchema")]
public Fachdatenschema SubmissionSchema { get; set; }
}
/// <summary>
/// Der Hashwert der unverschlüsselten Fachdaten. Die Angabe des Hashwertes dient der
/// Integritätssicherung des Gesamtantrags und schützt vor einem Austausch der Fachdaten
/// durch Systeme zwischen Sender und Subscriber (z.B. dem Zustelldienst).
/// </summary>
public partial class DataHash
{
/// <summary>
/// Der Hex-kodierte Hashwert gemäß des angegebenen Algorithmus.
/// </summary>
[JsonProperty("content")]
public string Content { get; set; }
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
[JsonProperty("type")]
public HashType Type { get; set; }
}
/// <summary>
/// Beschreibt das Signaturformt und Profile
/// </summary>
public partial class DataSignature
{
/// <summary>
/// Hier wird die Signatur im Falle einer Detached-Signatur als Base64- oder
/// Base64Url-kodierte Zeichenkette hinterlegt. Eine Base64Url-Kodierung kommt nur bei
/// Einsatz von JSON Web Signatures (JWS / JAdES) zum Einsatz.
/// </summary>
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
/// <summary>
/// Beschreibt, ob die Signatur als seperate (detached) Signatur (`true`) oder als Teil des
/// Fachdatensatzes bzw. der Anlage (`false`) übertragen wird. Wenn der Wert `true` ist,
/// dann wird die Signatur Base64- oder Base64Url-kodiert im Feld `content` übertragen.
/// </summary>
[JsonProperty("detachedSignature")]
public bool DetachedSignature { get; set; }
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
[JsonProperty("eidasAdesProfile", NullValueHandling = NullValueHandling.Ignore)]
public EidasAdesProfile? EidasAdesProfile { get; set; }
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
[JsonProperty("signatureFormat")]
public SignatureFormat SignatureFormat { get; set; }
}
/// <summary>
/// Referenz auf ein Schema, das die Struktur der Fachdaten einer Einreichung beschreibt.
/// </summary>
public partial class Fachdatenschema
{
/// <summary>
/// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
/// XSD- oder JSON-Schema).
/// </summary>
[JsonProperty("mimeType")]
public MimeType MimeType { get; set; }
/// <summary>
/// URI des Fachschemas. Wird hier eine URL verwendet, sollte das Schema unter der
/// angegebenen URL abrufbar sein. Eine Verfügbarkeit des Schemas unter der angegebenen URL
/// darf jedoch nicht vorausgesetzt werden.
/// </summary>
[JsonProperty("schemaUri")]
public Uri SchemaUri { get; set; }
}
/// <summary>
/// Dieses Objekt enthält die Informationen vom Bezahldienst.
/// </summary>
public partial class PaymentInformation
{
/// <summary>
/// Bruttobetrag
/// </summary>
[JsonProperty("grossAmount", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(MinMaxValueCheckConverter))]
public double? GrossAmount { get; set; }
/// <summary>
/// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
/// vorhanden / befüllt.
/// </summary>
[JsonProperty("paymentMethod")]
public PaymentMethod PaymentMethod { get; set; }
/// <summary>
/// Weitere Erläuterung zur gewählten Zahlart.
/// </summary>
[JsonProperty("paymentMethodDetail", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
public string PaymentMethodDetail { get; set; }
/// <summary>
/// - INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine
/// Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine
/// Wirkung erzeugt.
/// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
/// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
/// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
/// </summary>
[JsonProperty("status")]
public Status Status { get; set; }
/// <summary>
/// Eine vom Bezahldienst vergebene Transaktions-Id.
/// </summary>
[JsonProperty("transactionId")]
[JsonConverter(typeof(PurpleMinMaxLengthCheckConverter))]
public string TransactionId { get; set; }
/// <summary>
/// Bezahlreferenz bzw. Verwendungszweck, wie z. B. ein Kassenzeichen.
/// </summary>
[JsonProperty("transactionReference")]
public string TransactionReference { get; set; }
/// <summary>
/// Zeitstempel der erfolgreichen Durchführung der Bezahlung.
/// </summary>
[JsonProperty("transactionTimestamp", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? TransactionTimestamp { get; set; }
/// <summary>
/// Die Rest-URL der Payment Transaction für die Statusabfrage.
/// </summary>
[JsonProperty("transactionUrl", NullValueHandling = NullValueHandling.Ignore)]
public Uri TransactionUrl { get; set; }
}
/// <summary>
/// Beschreibung der Art der Verwaltungsleistung. Eine Verwaltungsleistung sollte immer mit
/// einer LeiKa-Id beschrieben werden. Ist für die gegebene Verwaltungsleistung keine
/// LeiKa-Id vorhanden, kann die Verwaltungsleistung übergangsweise über die Angabe einer
/// anderen eindeutigen Schema-URN beschrieben werden.
/// </summary>
public partial class Verwaltungsleistung
{
/// <summary>
/// (Kurz-)Beschreibung der Verwaltungsleistung
/// </summary>
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string Description { get; set; }
/// <summary>
/// URN einer Leistung. Im Falle einer Leistung aus dem Leistungskatalog sollte hier
/// `urn:de:fim:leika:leistung:` vorangestellt werden.
/// </summary>
[JsonProperty("identifier")]
[JsonConverter(typeof(FluffyMinMaxLengthCheckConverter))]
public string Identifier { get; set; }
/// <summary>
/// Name/Bezeichnung der Verwaltungsleistung
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
}
public partial class ReplyChannel
{
/// <summary>
/// Akkreditierte Anbieter siehe
/// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
/// </summary>
[JsonProperty("deMail", NullValueHandling = NullValueHandling.Ignore)]
public DeMail DeMail { get; set; }
/// <summary>
/// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
/// </summary>
[JsonProperty("elster", NullValueHandling = NullValueHandling.Ignore)]
public Elster Elster { get; set; }
[JsonProperty("eMail", NullValueHandling = NullValueHandling.Ignore)]
public EMail EMail { get; set; }
/// <summary>
/// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
/// </summary>
[JsonProperty("fink", NullValueHandling = NullValueHandling.Ignore)]
public Fink Fink { get; set; }
}
/// <summary>
/// Akkreditierte Anbieter siehe
/// https://www.bsi.bund.de/DE/Themen/Oeffentliche-Verwaltung/Moderner-Staat/De-Mail/Akkreditierte-DMDA/akkreditierte-dmda_node.html
/// </summary>
public partial class DeMail
{
[JsonProperty("address")]
public string Address { get; set; }
}
public partial class EMail
{
[JsonProperty("address")]
public string Address { get; set; }
/// <summary>
/// Hilfe zur Erstellung gibt es in der Dokumentation unter
/// https://docs.fitko.de/fit-connect/details/pgp-export
/// </summary>
[JsonProperty("pgpPublicKey", NullValueHandling = NullValueHandling.Ignore)]
public string PgpPublicKey { get; set; }
}
/// <summary>
/// Siehe https://www.elster.de/elsterweb/infoseite/elstertransfer_hilfe_schnittstellen
/// </summary>
public partial class Elster
{
[JsonProperty("accountId")]
public string AccountId { get; set; }
[JsonProperty("geschaeftszeichen", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(TentacledMinMaxLengthCheckConverter))]
public string Geschaeftszeichen { get; set; }
[JsonProperty("lieferTicket", NullValueHandling = NullValueHandling.Ignore)]
public string LieferTicket { get; set; }
}
/// <summary>
/// Postfachadresse in einem interoperablen Servicekonto (FINK.PFISK)
/// </summary>
public partial class Fink
{
/// <summary>
/// FINK Postfachadresse
/// </summary>
[JsonProperty("finkPostfachRef")]
[JsonConverter(typeof(StickyMinMaxLengthCheckConverter))]
public string FinkPostfachRef { get; set; }
/// <summary>
/// URL des Servicekontos, in dem das Ziel-Postfach liegt
/// </summary>
[JsonProperty("host", NullValueHandling = NullValueHandling.Ignore)]
public Uri Host { get; set; }
}
/// <summary>
/// Definiert die Art des Identifikationsnachweises.
/// </summary>
public enum AuthenticationInformationType { IdentificationReport };
/// <summary>
/// Der verwendete Hash-Algorithmus. Derzeit ist nur `sha512` erlaubt.
/// </summary>
public enum HashType { Sha512 };
/// <summary>
/// Zweck/Art der Anlage
/// - form: Automatisch generierte PDF-Repräsentation des vollständigen Antragsformulars
/// - attachment: Anlage, die von einem Bürger hochgeladen wurde
/// - report: Vom Onlinedienst, nachträglich erzeugte Unterlage
/// </summary>
public enum Purpose { Attachment, Form, Report };
/// <summary>
/// Referenziert ein eindeutiges Profil einer AdES (advanced electronic signature/seal)
/// gemäß eIDAS-Verordnung über eine URI gemäß [ETSI TS 119
/// 192](https://www.etsi.org/deliver/etsi_ts/119100_119199/119192/01.01.01_60/ts_119192v010101p.pdf).
///
/// Für die Details zur Verwendung und Validierung von Profilen siehe auch
/// https://ec.europa.eu/cefdigital/DSS/webapp-demo/doc/dss-documentation.html#_signatures_profile_simplification
/// </summary>
public enum EidasAdesProfile { HttpUriEtsiOrgAdes191X2LevelBaselineBB, HttpUriEtsiOrgAdes191X2LevelBaselineBLt, HttpUriEtsiOrgAdes191X2LevelBaselineBLta, HttpUriEtsiOrgAdes191X2LevelBaselineBT };
/// <summary>
/// Beschreibt, welches Signaturformat die genutzte Signatur / das genutzte Siegel nutzt.
/// Aktuell wird die Hinterlegung folgender Signaturformate unterstützt: CMS = Cryptographic
/// Message Syntax, Asic = Associated Signature Containers, PDF = PDF Signatur, XML =
/// XML-Signature, JSON = JSON Web Signature.
/// </summary>
public enum SignatureFormat { Asic, Cms, Json, Pdf, Xml };
/// <summary>
/// Mimetype (z.B. application/json oder application/xml) des referenzierten Schemas (z.B.
/// XSD- oder JSON-Schema).
/// </summary>
public enum MimeType { ApplicationJson, ApplicationXml };
/// <summary>
/// Die vom Benutzer ausgewählte Zahlart. Das Feld ist nur bei einer erfolgreichen Zahlung
/// vorhanden / befüllt.
/// </summary>
public enum PaymentMethod { Creditcard, Giropay, Invoice, Other, Paydirect, Paypal };
/// <summary>
/// - INITIAL - der Einreichung hat einen Payment-Request ausgelöst und eine
/// Payment-Transaction wurde angelegt. Der Nutzer hat aber im Bezahldienst noch keine
/// Wirkung erzeugt.
/// - BOOKED - der Nutzer hat die Bezahlung im Bezahldienst autorisiert.
/// - FAILED - der Vorgang wurde vom Bezahldienst aufgrund der Nutzereingaben abgebrochen.
/// - CANCELED - der Nutzer hat die Bezahlung im Bezahldienst abgebrochen.
/// </summary>
public enum Status { Booked, Canceled, Failed, Initial };
public partial class Metadata
{
public static Metadata FromJson(string json) => JsonConvert.DeserializeObject<Metadata>(json, FitConnect.Models.Api.Set.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this Metadata self) => JsonConvert.SerializeObject(self, FitConnect.Models.Api.Set.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
AuthenticationInformationTypeConverter.Singleton,
HashTypeConverter.Singleton,
PurposeConverter.Singleton,
EidasAdesProfileConverter.Singleton,
SignatureFormatConverter.Singleton,
MimeTypeConverter.Singleton,
PaymentMethodConverter.Singleton,
StatusConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class AuthenticationInformationTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AuthenticationInformationType) || t == typeof(AuthenticationInformationType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (value == "identificationReport")
{
return AuthenticationInformationType.IdentificationReport;
}
throw new Exception("Cannot unmarshal type AuthenticationInformationType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (AuthenticationInformationType)untypedValue;
if (value == AuthenticationInformationType.IdentificationReport)
{
serializer.Serialize(writer, "identificationReport");
return;
}
throw new Exception("Cannot marshal type AuthenticationInformationType");
}
public static readonly AuthenticationInformationTypeConverter Singleton = new AuthenticationInformationTypeConverter();
}
internal class HashTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(HashType) || t == typeof(HashType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
if (value == "sha512")
{
return HashType.Sha512;
}
throw new Exception("Cannot unmarshal type HashType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (HashType)untypedValue;
if (value == HashType.Sha512)
{
serializer.Serialize(writer, "sha512");
return;
}
throw new Exception("Cannot marshal type HashType");
}
public static readonly HashTypeConverter Singleton = new HashTypeConverter();
}
internal class PurposeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Purpose) || t == typeof(Purpose?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "attachment":
return Purpose.Attachment;
case "form":
return Purpose.Form;
case "report":
return Purpose.Report;
}
throw new Exception("Cannot unmarshal type Purpose");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (Purpose)untypedValue;
switch (value)
{
case Purpose.Attachment:
serializer.Serialize(writer, "attachment");
return;
case Purpose.Form:
serializer.Serialize(writer, "form");
return;
case Purpose.Report:
serializer.Serialize(writer, "report");
return;
}
throw new Exception("Cannot marshal type Purpose");
}
public static readonly PurposeConverter Singleton = new PurposeConverter();
}
internal class EidasAdesProfileConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EidasAdesProfile) || t == typeof(EidasAdesProfile?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "http://uri.etsi.org/ades/191x2/level/baseline/B-B#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta;
case "http://uri.etsi.org/ades/191x2/level/baseline/B-T#":
return EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT;
}
throw new Exception("Cannot unmarshal type EidasAdesProfile");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (EidasAdesProfile)untypedValue;
switch (value)
{
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBB:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-B#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLt:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LT#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBLta:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-LTA#");
return;
case EidasAdesProfile.HttpUriEtsiOrgAdes191X2LevelBaselineBT:
serializer.Serialize(writer, "http://uri.etsi.org/ades/191x2/level/baseline/B-T#");
return;
}
throw new Exception("Cannot marshal type EidasAdesProfile");
}
public static readonly EidasAdesProfileConverter Singleton = new EidasAdesProfileConverter();
}
internal class SignatureFormatConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(SignatureFormat) || t == typeof(SignatureFormat?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "asic":
return SignatureFormat.Asic;
case "cms":
return SignatureFormat.Cms;
case "json":
return SignatureFormat.Json;
case "pdf":
return SignatureFormat.Pdf;
case "xml":
return SignatureFormat.Xml;
}
throw new Exception("Cannot unmarshal type SignatureFormat");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (SignatureFormat)untypedValue;
switch (value)
{
case SignatureFormat.Asic:
serializer.Serialize(writer, "asic");
return;
case SignatureFormat.Cms:
serializer.Serialize(writer, "cms");
return;
case SignatureFormat.Json:
serializer.Serialize(writer, "json");
return;
case SignatureFormat.Pdf:
serializer.Serialize(writer, "pdf");
return;
case SignatureFormat.Xml:
serializer.Serialize(writer, "xml");
return;
}
throw new Exception("Cannot marshal type SignatureFormat");
}
public static readonly SignatureFormatConverter Singleton = new SignatureFormatConverter();
}
internal class MimeTypeConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(MimeType) || t == typeof(MimeType?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "application/json":
return MimeType.ApplicationJson;
case "application/xml":
return MimeType.ApplicationXml;
}
throw new Exception("Cannot unmarshal type MimeType");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (MimeType)untypedValue;
switch (value)
{
case MimeType.ApplicationJson:
serializer.Serialize(writer, "application/json");
return;
case MimeType.ApplicationXml:
serializer.Serialize(writer, "application/xml");
return;
}
throw new Exception("Cannot marshal type MimeType");
}
public static readonly MimeTypeConverter Singleton = new MimeTypeConverter();
}
internal class MinMaxValueCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(double) || t == typeof(double?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<double>(reader);
if (value >= 0.01)
{
return value;
}
throw new Exception("Cannot unmarshal type double");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (double)untypedValue;
if (value >= 0.01)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type double");
}
public static readonly MinMaxValueCheckConverter Singleton = new MinMaxValueCheckConverter();
}
internal class PaymentMethodConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(PaymentMethod) || t == typeof(PaymentMethod?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "CREDITCARD":
return PaymentMethod.Creditcard;
case "GIROPAY":
return PaymentMethod.Giropay;
case "INVOICE":
return PaymentMethod.Invoice;
case "OTHER":
return PaymentMethod.Other;
case "PAYDIRECT":
return PaymentMethod.Paydirect;
case "PAYPAL":
return PaymentMethod.Paypal;
}
throw new Exception("Cannot unmarshal type PaymentMethod");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (PaymentMethod)untypedValue;
switch (value)
{
case PaymentMethod.Creditcard:
serializer.Serialize(writer, "CREDITCARD");
return;
case PaymentMethod.Giropay:
serializer.Serialize(writer, "GIROPAY");
return;
case PaymentMethod.Invoice:
serializer.Serialize(writer, "INVOICE");
return;
case PaymentMethod.Other:
serializer.Serialize(writer, "OTHER");
return;
case PaymentMethod.Paydirect:
serializer.Serialize(writer, "PAYDIRECT");
return;
case PaymentMethod.Paypal:
serializer.Serialize(writer, "PAYPAL");
return;
}
throw new Exception("Cannot marshal type PaymentMethod");
}
public static readonly PaymentMethodConverter Singleton = new PaymentMethodConverter();
}
internal class PurpleMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length >= 1 && value.Length <= 36)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length >= 1 && value.Length <= 36)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly PurpleMinMaxLengthCheckConverter Singleton = new PurpleMinMaxLengthCheckConverter();
}
internal class StatusConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Status) || t == typeof(Status?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
var value = serializer.Deserialize<string>(reader);
switch (value)
{
case "BOOKED":
return Status.Booked;
case "CANCELED":
return Status.Canceled;
case "FAILED":
return Status.Failed;
case "INITIAL":
return Status.Initial;
}
throw new Exception("Cannot unmarshal type Status");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (Status)untypedValue;
switch (value)
{
case Status.Booked:
serializer.Serialize(writer, "BOOKED");
return;
case Status.Canceled:
serializer.Serialize(writer, "CANCELED");
return;
case Status.Failed:
serializer.Serialize(writer, "FAILED");
return;
case Status.Initial:
serializer.Serialize(writer, "INITIAL");
return;
}
throw new Exception("Cannot marshal type Status");
}
public static readonly StatusConverter Singleton = new StatusConverter();
}
internal class FluffyMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length >= 7 && value.Length <= 255)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length >= 7 && value.Length <= 255)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly FluffyMinMaxLengthCheckConverter Singleton = new FluffyMinMaxLengthCheckConverter();
}
internal class TentacledMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length <= 10)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length <= 10)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly TentacledMinMaxLengthCheckConverter Singleton = new TentacledMinMaxLengthCheckConverter();
}
internal class StickyMinMaxLengthCheckConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(string);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader);
if (value.Length <= 150)
{
return value;
}
throw new Exception("Cannot unmarshal type string");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (string)untypedValue;
if (value.Length <= 150)
{
serializer.Serialize(writer, value);
return;
}
throw new Exception("Cannot marshal type string");
}
public static readonly StickyMinMaxLengthCheckConverter Singleton = new StickyMinMaxLengthCheckConverter();
}
}
...@@ -4,7 +4,7 @@ namespace FitConnect.Services.Models; ...@@ -4,7 +4,7 @@ namespace FitConnect.Services.Models;
public class SubmitSubmissionDto { public class SubmitSubmissionDto {
[JsonPropertyName("encryptedData")] [JsonPropertyName("encryptedData")]
public string EncryptedData; public string? EncryptedData;
[JsonPropertyName("encryptedMetadata")] [JsonPropertyName("encryptedMetadata")]
public string EncryptedMetadata; public string EncryptedMetadata;
......
...@@ -8,12 +8,12 @@ ...@@ -8,12 +8,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Models\Models.csproj"/> <ProjectReference Include="..\Models\Models.csproj" />
<ProjectReference Include="..\RestService\RestService.csproj"/> <ProjectReference Include="..\RestService\RestService.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1"/> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>
...@@ -133,6 +133,21 @@ You need a secret file for e2e test like: ...@@ -133,6 +133,21 @@ You need a secret file for e2e test like:
} }
``` ```
# API Abstraction
```mermaid
sequenceDiagram
Client->>FitConnectServer: SendSubmission
FitConnectServer->>Client: Success
```
## The sender process
```mermaid
sequenceDiagram
Sender->>FitConnectServer: GetSubmissions
FitConnectServer->>Sender: Success
```
[glossary](https://docs.fitko.de/fit-connect/docs/glossary/) [glossary](https://docs.fitko.de/fit-connect/docs/glossary/)
### Tickets ### Tickets
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment