diff --git a/.gitignore b/.gitignore index 1d233990b8b65a79da75dd14974ab365924e387b..5f106c031d5843d8053affb54921a4ef3da48dff 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ **/**notes.md nuget_api.txt nuget_local.sh +global.json private_notes/ IntegrationTests/certificates @@ -489,4 +490,4 @@ Temporary Items .apdisk deploy.sh -global.json \ No newline at end of file +global.json diff --git a/.reuse/dep5 b/.reuse/dep5 index 62a5388b414308a72ed8150447feee00b900139d..81ab3827bc4fe8980d1e5420ae13e32a56ccc44e 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -2,7 +2,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: FitConnect/* BasicUnitTest/* E2ETest/* EncryptionTests/* IntegrationTests/* MockContainer/* FitConnect.sln test.Dockerfile Test.pdf version.sh FitConnect.sln.DotSettings - README.md CHANGELOG.md *.md Dockerfile nuget_upload.sh ConsoleAppExample/* + README.md CHANGELOG.md *.md Dockerfile nuget_upload.sh ConsoleAppExample/* global.json DummyServerForHeaderTests/* Copyright: 2022 FIT-Connect contributors License: EUPL-1.2 diff --git a/BasicUnitTest/BasicUnitTest.csproj b/BasicUnitTest/BasicUnitTest.csproj index 21a1d50c77d550f5fd7f5c5985afd172523b5baf..8f62f86091a6568e68294cc7b643f29352a62d86 100644 --- a/BasicUnitTest/BasicUnitTest.csproj +++ b/BasicUnitTest/BasicUnitTest.csproj @@ -8,13 +8,16 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="FluentAssertions" Version="6.7.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/> - <PackageReference Include="Moq" Version="4.18.1"/> - <PackageReference Include="NUnit" Version="3.13.2"/> - <PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/> - <PackageReference Include="coverlet.collector" Version="3.1.0"/> + <PackageReference Include="FluentAssertions" Version="6.8.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageReference Include="Moq" Version="4.18.4" /> + <PackageReference Include="NUnit" Version="3.13.3" /> + <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> </ItemGroup> <ItemGroup> diff --git a/BasicUnitTest/JsonSchemaValidation.cs b/BasicUnitTest/JsonSchemaValidation.cs index 669b9349eaa84fe17c210107e1fd1cfcc4aaeb87..efd62b76d65b6b74f85e3760916966d7e2aa66b4 100644 --- a/BasicUnitTest/JsonSchemaValidation.cs +++ b/BasicUnitTest/JsonSchemaValidation.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using System.Text.RegularExpressions; +using FitConnect.Encryption; +using FitConnect.Models; using FluentAssertions; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Linq; @@ -60,4 +63,12 @@ public class JsonSchemaValidation { "{\"sub\":\"submission:22a69c34-4ca4-43f7-a909-dcfe7f972c27\",\"$schema\":\"https://schema.fitko.de/fit-connect/set-payload/1.0.0/set-payload.schema.json\",\"iss\":\"https://submission-api-testing.fit-connect.fitko.dev\",\"txn\":\"case:5ef6d3c7-a9db-4b23-89a6-75f5011bd57a\",\"iat\":1661228799,\"jti\":\"c7ff0d9d-2caa-481d-b9ce-c6d4a1c6ee02\",\"event\":{\"https://schema.fitko.de/fit-connect/events/submit-submission\":{\"authenticationTags\":{\"data\":\"6hvHPpXSgRfeFAUWJRb5eQ\",\"metadata\":\"T_kVHCwoMCpg42c3CjeZAQ\",\"attachments\":{\"33b17890-2a86-47a4-8fb9-92befc782e13\":\"DxndA7vZAq_karEDRL31-Q\"}}}}}") .IsValid(schema).Should().BeFalse(); } + + [Test] + public void JsonSchemaOfMetadataInResourcesShouldMatchSchemaUriString() { + var schema = JSchema.Parse(JsonHelper.LoadContentOfResource("metadata.schema.json")); + schema.Id.Should().Be(Metadata.SchemaUrl); + + new Regex(Metadata.SchemaPattern).IsMatch(Metadata.SchemaUrl).Should().BeTrue(); + } } diff --git a/BasicUnitTest/SecurityEventTokenTests.cs b/BasicUnitTest/SecurityEventTokenTests.cs index da9967d56c8c8a9068fb0655cc1b23ec0f41a10c..695ea083a39439d9fe7600a4f96d27594bafec4c 100644 --- a/BasicUnitTest/SecurityEventTokenTests.cs +++ b/BasicUnitTest/SecurityEventTokenTests.cs @@ -50,7 +50,7 @@ public class SecurityEventTokenTests { var securityEventToken = SecurityEventToken.FromJson(json); securityEventToken?.EventType.Should().Be(EventType.Reject); - securityEventToken?.Events?.RejectSubmissionEvent?.Problems[0].Type.Should() + securityEventToken?.Events?.RejectSubmissionEvent?.Problems![0].Type.Should() .Contain("authentication-tag-incorrect"); } diff --git a/ConsoleAppExample/ConsoleAppExample.csproj b/ConsoleAppExample/ConsoleAppExample.csproj index da7bcce36f4aa8f7618abe2b93a813b52b2f32f3..3d4acd306f8418e9913aab5f9c079f3e5ad78186 100644 --- a/ConsoleAppExample/ConsoleAppExample.csproj +++ b/ConsoleAppExample/ConsoleAppExample.csproj @@ -10,11 +10,11 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="FitConnect" Version="0.0.1-beta.2"/> - <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0"/> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0"/> - <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/> + <PackageReference Include="FitConnect" Version="0.0.1-beta.2" /> + <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> </ItemGroup> <ItemGroup> @@ -42,7 +42,7 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> </ItemGroup> </Project> diff --git a/ConsoleAppExample/SenderDemo.cs b/ConsoleAppExample/SenderDemo.cs index 244be4b62a57e8e5b3408d111901c4eaa4d6b2e3..4822650cfb1379b07c35448739df89e6f4e322af 100644 --- a/ConsoleAppExample/SenderDemo.cs +++ b/ConsoleAppExample/SenderDemo.cs @@ -13,6 +13,10 @@ public static class SenderDemo { var destinationId = config["FitConnect:Sender:DestinationId"]; var leikaKey = config["FitConnect:Sender:LeikaKey"]; + if (clientId == null || clientSecret == null || destinationId == null || leikaKey == null) { + logger.LogError("Missing configuration values"); + return; + } OutputHelper.PrintSender(); @@ -35,6 +39,11 @@ public static class SenderDemo { var destinationId = config["FitConnect:Sender:DestinationId"]; var leikaKey = config["FitConnect:Sender:LeikaKey"]; + if (clientId == null || clientSecret == null || destinationId == null || leikaKey == null) { + logger.LogError("Missing configuration values"); + return; + } + var encryption = new FitEncryption("", "", null); Dictionary<string, string>? encryptedAttachments = null; diff --git a/ConsoleAppExample/SubscriberDemo.cs b/ConsoleAppExample/SubscriberDemo.cs index 54ab8dd5591911653534c521e248290e5cd563b4..d69b4d47be32835cdf8cbbd9e7c3197e10c3c444 100644 --- a/ConsoleAppExample/SubscriberDemo.cs +++ b/ConsoleAppExample/SubscriberDemo.cs @@ -11,6 +11,10 @@ public static class SubscriberDemo { var privateKeyDecryption = config["FitConnect:Subscriber:PrivateKeyDecryption"]; var privateKeySigning = config["FitConnect:Subscriber:PrivateKeySigning"]; + if (clientId == null || clientSecret == null || privateKeyDecryption == null || privateKeySigning == null) { + logger.LogError("Missing configuration values"); + return; + } OutputHelper.PrintSubscriber(); diff --git a/E2ETest/E2ETest.csproj b/E2ETest/E2ETest.csproj index 974a92a07342e19b27e979c3c468d7658703bc45..e13dc61c9178f5adde85d250742a840c2a30e135 100644 --- a/E2ETest/E2ETest.csproj +++ b/E2ETest/E2ETest.csproj @@ -9,17 +9,23 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="FluentAssertions" Version="6.7.0"/> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/> - <PackageReference Include="NUnit" Version="3.13.3"/> - <PackageReference Include="NUnit3TestAdapter" Version="4.2.1"/> - <PackageReference Include="NUnit.Analyzers" Version="3.3.0"/> - <PackageReference Include="coverlet.collector" Version="3.1.2"/> + <PackageReference Include="FluentAssertions" Version="6.8.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageReference Include="NUnit" Version="3.13.3" /> + <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> + <PackageReference Include="NUnit.Analyzers" Version="3.5.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> - <ProjectReference Include="..\MockContainer\MockContainer.csproj"/> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> + <ProjectReference Include="..\MockContainer\MockContainer.csproj" /> </ItemGroup> </Project> diff --git a/E2ETest/RejectSubmissionTest.cs b/E2ETest/RejectSubmissionTest.cs index 74507fe3a659ef1c157d82ec41c74272852c46a8..f938284aa62c130f5c37cf68efc09a048ab815e6 100644 --- a/E2ETest/RejectSubmissionTest.cs +++ b/E2ETest/RejectSubmissionTest.cs @@ -8,8 +8,7 @@ namespace E2ETest; [Order(10)] public class RejectSubmissionTest : EndToEndTestBase { - private string _caseId = null!; - private string _submissionId = null!; + private SentSubmission _submission = null!; [Order(5)] [Test] @@ -40,20 +39,17 @@ public class RejectSubmissionTest : EndToEndTestBase { .WithAttachments(new Attachment("Test.pdf", "A simple PDF")) .Build(); - var submission = Sender.SendAsync(outgoing).Result; + _submission = Sender.SendAsync(outgoing).Result; - _caseId = submission.CaseId!; - _submissionId = submission.Id!; - - _caseId.Should().NotBeNull(); - _submissionId.Should().NotBeNull(); + _submission.CaseId.Should().NotBeNull(); + _submission.SubmissionId.Should().NotBeNull(); } [Test] [Order(20)] public void Sender_GetSubmissionState() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -65,7 +61,7 @@ public class RejectSubmissionTest : EndToEndTestBase { [Order(30)] public void Subscriber_GetSubmissionState() { // Act - var status = Subscriber.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Subscriber.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -76,7 +72,8 @@ public class RejectSubmissionTest : EndToEndTestBase { [Test] [Order(40)] public void Reject_Submission() { - var subscriberWithSubmission = Subscriber.RequestSubmissionAsync(_submissionId).Result; + var subscriberWithSubmission = + Subscriber.RequestSubmissionAsync(_submission.SubmissionId).Result; var data = subscriberWithSubmission.GetDataString(); Logger.LogInformation("Data {Data}", data); @@ -87,15 +84,17 @@ public class RejectSubmissionTest : EndToEndTestBase { attachments.First().Filename.Should().Be("Test.pdf"); subscriberWithSubmission.RejectSubmissionAsync( - new Problems(Problems.ProblemTypeEnum.SchemaViolation, "") + new Problems(Problems.ProblemTypeEnum.SchemaViolation, ProblemDetail) ).Wait(); } + private const string ProblemDetail = "Reject submission test"; + [Test] [Order(50)] public void Sender_GetSubmissionState_AfterRejecting() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -108,4 +107,14 @@ public class RejectSubmissionTest : EndToEndTestBase { rejection.Events?.RejectSubmissionEvent?.Problems?.ForEach(p => Logger.LogWarning(p.Detail)); } + + [Test] + [Order(60)] + public void Sender_GetStatus() { + var (status, problems) = + Sender.GetStatusForSubmissionAsync(_submission).Result; + + status.Should().Be(EventType.Reject); + problems!.Any(p => p.Detail == ProblemDetail).Should().BeTrue(); + } } diff --git a/E2ETest/StraightForwardTest.cs b/E2ETest/StraightForwardTest.cs index 87cfd66c53151486e2ec23de6b3ec0f6d561ba16..3155bd6aa46eef899f84d24673b2dc1dea62f76b 100644 --- a/E2ETest/StraightForwardTest.cs +++ b/E2ETest/StraightForwardTest.cs @@ -1,3 +1,4 @@ +using System.Security.Authentication; using FitConnect; using FitConnect.Models; using FluentAssertions; @@ -8,8 +9,7 @@ namespace E2ETest; [Order(20)] [TestFixture] public class StraightForwardTest : EndToEndTestBase { - private string _caseId = null!; - private string _submissionId = null!; + private SentSubmission _submission = null!; [Order(10)] [Test] @@ -20,15 +20,20 @@ public class StraightForwardTest : EndToEndTestBase { .WithJsonData(@"{""data"":""value""}") .WithAttachments(new Attachment("Test.pdf", "Attachment #1"), new Attachment("Test.pdf", "Attachment #2")) + .WithAttachments(new Attachment("Test.pdf", "Attachment #3")) .Build(); - var submission = Sender.SendAsync(outgoing).Result; + _submission = Sender.SendAsync(outgoing).Result; - _caseId = submission.CaseId!; - _submissionId = submission.Id!; + _submission.Should().NotBeNull(); - _caseId.Should().NotBeNull(); - _submissionId.Should().NotBeNull(); + _submission.CaseId.Should().NotBeNull(); + _submission.SubmissionId.Should().NotBeNull(); + _submission.AuthenticationTags.Metadata.Should().NotBeNullOrWhiteSpace(); + _submission.AuthenticationTags.Data.Should().NotBeNullOrWhiteSpace(); + _submission.AuthenticationTags.Attachments!.All(item => + !(string.IsNullOrWhiteSpace(item.Value) || string.IsNullOrWhiteSpace(item.Key))) + .Should().BeTrue(); } [Order(11)] @@ -42,7 +47,7 @@ public class StraightForwardTest : EndToEndTestBase { var submission = Sender.SendAsync(outgoing).Result; var caseId = submission.CaseId!; - var submissionId = submission.Id!; + var submissionId = submission.SubmissionId!; caseId.Should().NotBeNull(); submissionId.Should().NotBeNull(); @@ -52,7 +57,18 @@ public class StraightForwardTest : EndToEndTestBase { [Order(20)] public void Sender_GetSubmissionState() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetStatusForSubmissionAsync(_submission) + .Result; + + // Assert + status.Status.Should().Be(EventType.Submit); + } + + [Test] + [Order(21)] + public void Sender_GetEventLogAfterSubmit() { + // Act + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -64,7 +80,7 @@ public class StraightForwardTest : EndToEndTestBase { [Order(30)] public void Subscriber_GetSubmissionState() { // Act - var status = Subscriber.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Subscriber.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -75,7 +91,8 @@ public class StraightForwardTest : EndToEndTestBase { [Test] [Order(40)] public void RequestSubmission() { - var subscriberWithSubmission = Subscriber.RequestSubmissionAsync(_submissionId).Result; + var subscriberWithSubmission = + Subscriber.RequestSubmissionAsync(_submission.SubmissionId).Result; var data = subscriberWithSubmission.GetDataString(); Logger.LogInformation("Data {Data}", data); @@ -83,18 +100,20 @@ public class StraightForwardTest : EndToEndTestBase { data.Should().Be(@"{""data"":""value""}"); var attachments = subscriberWithSubmission.GetAttachmentsAsync().Result.ToList(); - attachments.First().Filename.Should().Be("Test.pdf"); + attachments.TrueForAll(a => a.Filename == "Test.pdf"); attachments.TrueForAll(a => a.Description?.StartsWith("Attachment #") ?? false).Should() .BeTrue(); - + attachments.Count.Should().Be(3); + var size = File.ReadAllBytes("Test.pdf").Length; + attachments.ForEach(a => a.Content!.Length.Should().Be(size)); subscriberWithSubmission.AcceptSubmissionAsync().Wait(); } [Test] [Order(50)] - public void Sender_GetSubmissionState_AfterAccepting() { + public void Sender_GetEventLog_AfterAccepting() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -107,4 +126,43 @@ public class StraightForwardTest : EndToEndTestBase { status.ForEach( s => Logger.LogInformation("Status {When} {Event}", s.EventTime, s.EventType)); } + + [Test] + [Order(51)] + public void Sender_GetSubmissionState_AfterAccepting() { + var status = Sender.GetStatusForSubmissionAsync(_submission).Result; + + status.Status.Should().Be(EventType.Accept); + status.Problems.Should().BeNullOrEmpty(); + } + + [Test] + [Order(60)] + public void Sender_GetStatus_AfterAccepting() { + // Act + var (status, problems) = + Sender.GetStatusForSubmissionAsync(_submission).Result; + + // Assert + status.Should().Be(EventType.Accept); + } + + [Test] + [Order(61)] + public void Sender_GetStatusForSubmission_FailsOnManipulatedAuthenticationTags() { + var manipulated = new SentSubmission(_submission.SubmissionId, _submission.CaseId, + _submission.DestinationId, + new FitConnect.Models.AuthenticationTags() { + Data = _submission.AuthenticationTags.Data + "Invalid", + Metadata = _submission.AuthenticationTags.Metadata + "Invalid", + Attachments = _submission.AuthenticationTags.Attachments!.ToDictionary( + i => i.Key, + i => "InvalidTag!") + }); + + var exception = Assert.Throws<AggregateException>(() => + Sender.GetStatusForSubmissionAsync(manipulated).Wait()); + + exception.InnerExceptions.Any(e => e is AuthenticationTagException).Should().BeTrue(); + } } diff --git a/E2ETest/StraightPreEncryptedForwardTest.cs b/E2ETest/StraightPreEncryptedForwardTest.cs index 6a97d4919be74590f686f5f8c53c627b1bfd8b48..28f22f762e2e8d0803ef2fcbb319688493387d2a 100644 --- a/E2ETest/StraightPreEncryptedForwardTest.cs +++ b/E2ETest/StraightPreEncryptedForwardTest.cs @@ -1,6 +1,8 @@ +using System.Security.Authentication; using FitConnect; using FitConnect.Encryption; using FitConnect.Models; +using FitConnect.Models.v1.Api; using FluentAssertions; using Microsoft.Extensions.Logging; using Newtonsoft.Json; @@ -10,8 +12,7 @@ namespace E2ETest; [Order(30)] [TestFixture] public class StraightPreEncryptedForwardTest : EndToEndTestBase { - private string _caseId = null!; - private string _submissionId = null!; + private SentSubmission _submission = null!; [Order(10)] [Test] @@ -59,19 +60,21 @@ public class StraightPreEncryptedForwardTest : EndToEndTestBase { var result = Sender.SendAsync(submission).Result; - if (result.CaseId == null || result.CaseId == null) - throw new NullReferenceException( - $"Neither {nameof(result.CaseId)} nor {nameof(result.SubmissionId)} must not be null"); + // Assert + _submission = result; - _caseId = result.CaseId!; - _submissionId = result.SubmissionId!; + _submission.CaseId.Should().NotBeNullOrWhiteSpace(); + _submission.SubmissionId.Should().NotBeNullOrWhiteSpace(); + _submission.AuthenticationTags.Attachments!.All(item => + !(string.IsNullOrWhiteSpace(item.Key) || string.IsNullOrWhiteSpace(item.Value))) + .Should().BeTrue(); } [Test] [Order(20)] - public void Sender_GetSubmissionState() { + public void Sender_GetEventLogForSubmission() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -80,11 +83,19 @@ public class StraightPreEncryptedForwardTest : EndToEndTestBase { s => Logger.LogInformation("Status {When} {Event}", s.EventTime, s.EventType)); } + [Test] + [Order(21)] + public void Sender_GetStatusForSubmission() { + var (status, problems) = Sender.GetStatusForSubmissionAsync(_submission).Result; + status.Should().Be(EventType.Submit); + } + + [Test] [Order(30)] public void Subscriber_GetSubmissionState() { // Act - var status = Subscriber.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Subscriber.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); @@ -95,7 +106,8 @@ public class StraightPreEncryptedForwardTest : EndToEndTestBase { [Test] [Order(40)] public void RequestSubmission() { - var subscriberWithSubmission = Subscriber.RequestSubmissionAsync(_submissionId).Result; + var subscriberWithSubmission = + Subscriber.RequestSubmissionAsync(_submission.SubmissionId).Result; var data = subscriberWithSubmission.GetDataString(); Logger.LogInformation("Data {Data}", data); @@ -106,18 +118,47 @@ public class StraightPreEncryptedForwardTest : EndToEndTestBase { var attachments = subscriberWithSubmission.GetAttachmentsAsync().Result; attachments.First().Filename.Should().Be("Test.pdf"); - subscriberWithSubmission.AcceptSubmissionAsync().Wait(); + subscriberWithSubmission + .AcceptSubmissionAsync(new Problems(Problems.ProblemTypeEnum.MissingSchema, + "Schema missing")).Wait(); } [Test] [Order(50)] public void Sender_GetSubmissionState_AfterAccepting() { // Act - var status = Sender.GetStatusForSubmissionAsync(_caseId, Settings.DestinationId).Result; + var status = Sender.GetEventLogAsync(_submission.CaseId, Settings.DestinationId).Result; // Assert status.Count.Should().BeGreaterThan(0); status.ForEach( s => Logger.LogInformation("Status {When} {Event}", s.EventTime, s.EventType)); + + var acceptanceMessage = status.Last(); + acceptanceMessage.EventType.Should().Be(EventType.Accept); + + acceptanceMessage.Events!.AcceptSubmissionEvent!.Problems.Should().NotBeEmpty(); + + acceptanceMessage.Events?.AcceptSubmissionEvent?.Problems! + .Any(p => p.Detail == "Schema missing").Should().BeTrue(); + } + + [Test] + [Order(51)] + public void Sender_GetStatusForSubmission_FailsOnManipulatedAuthenticationTags() { + var manipulated = new SentSubmission(_submission.SubmissionId, _submission.CaseId, + _submission.DestinationId, + new FitConnect.Models.AuthenticationTags() { + Data = _submission.AuthenticationTags.Data + "Invalid", + Metadata = _submission.AuthenticationTags.Metadata + "Invalid", + Attachments = _submission.AuthenticationTags.Attachments!.ToDictionary( + i => i.Key, + i => "InvalidTag!") + }); + + var exception = Assert.Throws<AggregateException>(() => + Sender.GetStatusForSubmissionAsync(manipulated).Wait()); + + exception!.InnerExceptions.Any(e=>e is AuthenticationTagException).Should().BeTrue(); } } diff --git a/EncryptionTests/Certificates/certificate.cer b/EncryptionTests/Certificates/certificate.cer deleted file mode 100644 index 73f0387ee1b1ff8bdc9fe430d24b328c0e860b85..0000000000000000000000000000000000000000 --- a/EncryptionTests/Certificates/certificate.cer +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEyjCCArKgAwIBAgIIMIw7HOs7698wDQYJKoZIhvcNAQELBQAwJTEWMBQGA1UEAxMNZml0Y29u -bmVjdC5kZTELMAkGA1UEBhMCREUwHhcNMjIwNjA4MTEwNzQ3WhcNMjcwNjA4MTEwNzUyWjAlMRYw -FAYDVQQDEw1maXRjb25uZWN0LmRlMQswCQYDVQQGEwJERTCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAJxxbIieIEY9Vvql4R6ED5HqtGMjoJZ2xiwF5Zv/DuLseAZEUR7QuBvPG8CLcYOE -JArXcAS5smLfG2LiNerMgDnrLGvBl3qrYOEq0kRIqGkSr5JM2V/bk2UgMToOJGQDBMh6IbU707Xp -u1gHe2wuNxbJE4kALZV8zJX9fE2bc87o3URsw4yv3EnCpR3TskPI2uazNxKWVWjgpZ5yB6PQMbG0 -R7e0qGdEeIo5PpaxdkzbJUN4LwnEdZCKbXt3bMqr7wUJ4lXrqCUhO1Y57iR5YoNBTkKgJHV9fbcy -EHs6IZAhxQCCyuczby5UDS4VzXvnqpxxnvI/rv12nc2WPPctvmA3AyHOH7hTugg7UZ75UKFFHqfU -TeY6lWkifUpTpWVZ3Krm0+3Ja/VZzufPSybifTR9v5JMxQdwaqp9E53Cqh1csqciLVazDOjU6Lhn -fjLr4GwSri1FveXDqpq/jJhkTbuDL1fAEs+eDauD3vNxb0Bverpb5/7rOcAKTH0GEzZhSHV23fyP -DpKmpi5h0py42SdbBAZat8C1StMQ7m3UxuDdoMg+zRS5d12d6s0ZFf2xwFCjDVNVN6y7BkSGuA2l -2/I5ojXhK/Wm+DAcKT/osZznyNq4OlHL2ck6LOSSgFCJFrdkpku++gROx2xjUIFQjryUkcPZ6y0l -PLXAHJUEGO97AgMBAAEwDQYJKoZIhvcNAQELBQADggIBADXJ6LP4HdnUiFCgQR8PoY+fR8XCnYx8 -FdVB9v9xdsBhE1/B1c8z6kUInQdq2NfsRjyfgA1wIdPElP+b1bu/TKS/GpWrsPHUXDRv67qT13cB -rw8I6rHTDdnF00pEU4mabI32EqGnEAwu8DGlKnDbnh/dY7IQjYK40/ZutcqzybzvBiNFoYu7rKXg -7CThPwc92fnPfFcDJJMU9YlA5C6MaLSxDj0e3z1rl+ew9pab8gclnbzgxGOqFMNPhNRHx0SvFrCF -dPzva9mYDATHgR66hJAo/hov8qJnz6/7xkQgFPy4jHwBk2ubM2SN8+UEyklm7u3v8X6mpvdE7/gC -qWJYJjcpzz7QEBmvJ9yYIALNo8E9UyZNfYgE/hEyBpEccNk7z6y/yHNThcojwbfmYjztl5Ed4uHY -i1ycf5LYcSAxtzD6V5CuIVzmPNkSgB1m8Wu/+5Sy2/uQGIKNK/X6E9f1GjTDBv5KzxOoongc4yri -VinYgmrpoF6Uh6G423IGT6/+SpyQa5oegpSbKKDcSAQKK/fCJQbck03WTbpgKDhc0KDLB2C07yiI -RNgSDRO+eg8BTFjxE3uPVwY8AP+QxK2+Xn5ozuT7aedeS41MQYeeatd6StNy809DvIMb44ZUA6JU -/n8Ok8eHWVY+aShfomcLyH7+EeL9e2LTSp0geUdmsnNI ------END CERTIFICATE----- \ No newline at end of file diff --git a/EncryptionTests/Certificates/certificate.pfx b/EncryptionTests/Certificates/certificate.pfx deleted file mode 100644 index ec4092f2c68e0c8865fe9333be770dd09fb5c6f9..0000000000000000000000000000000000000000 Binary files a/EncryptionTests/Certificates/certificate.pfx and /dev/null differ diff --git a/EncryptionTests/EncryptionTests.csproj b/EncryptionTests/EncryptionTests.csproj index 698bfa2db59a60db4866262ad61b60712dc207ba..c8bdd34574e78b2270e12a92907c8a0d5de12505 100644 --- a/EncryptionTests/EncryptionTests.csproj +++ b/EncryptionTests/EncryptionTests.csproj @@ -10,18 +10,21 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="FluentAssertions" Version="6.7.0"/> - <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/> - <PackageReference Include="NUnit" Version="3.13.2"/> - <PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/> - <PackageReference Include="coverlet.collector" Version="3.1.0"/> + <PackageReference Include="FluentAssertions" Version="6.8.0" /> + <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageReference Include="NUnit" Version="3.13.3" /> + <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> - <ProjectReference Include="..\MockContainer\MockContainer.csproj"/> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> + <ProjectReference Include="..\MockContainer\MockContainer.csproj" /> </ItemGroup> <ItemGroup> diff --git a/FitConnect/Encryption/FitEncryption.cs b/FitConnect/Encryption/FitEncryption.cs index 42e8dc32960bd07818fcc20bf3d9601447d93cb0..e95e44ca87c45181ce29dd830da0732a0aaf9979 100644 --- a/FitConnect/Encryption/FitEncryption.cs +++ b/FitConnect/Encryption/FitEncryption.cs @@ -115,6 +115,7 @@ public class FitEncryption { public async Task<string> CreateAcceptSecurityEventToken(ISubmitted submission, + IEnumerable<Problems>? problems = null, dynamic? payload = null) { if (submission?.SubmissionId == null || submission?.CaseId == null || submission?.DestinationId == null) @@ -123,17 +124,17 @@ public class FitEncryption { return await CreateSecurityEventToken(submission.SubmissionId, submission.CaseId, submission.DestinationId, new Events { - AcceptSubmissionEvent = new AcceptSubmissionEvent(payload) + AcceptSubmissionEvent = new AcceptSubmissionEvent(payload, problems) }); } - public async Task<string> CreateAcceptSecurityEventToken(Submission submission) { + public async Task<string> CreateAcceptSecurityEventToken(Submission submission, IEnumerable<Problems>? problems) { var payload = GenerateAuthenticationTags(submission); return await CreateSecurityEventToken(submission.Id, submission.CaseId, submission.DestinationId, new Events { - AcceptSubmissionEvent = new AcceptSubmissionEvent(payload) + AcceptSubmissionEvent = new AcceptSubmissionEvent(payload, problems) }); } diff --git a/FitConnect/Encryption/JoseEncryptor.cs b/FitConnect/Encryption/JoseEncryptor.cs index 12503ae9bb5b185dbc402b2e95468ed33db52421..26a174723df1d7719add7d155b2c770142c5b153 100644 --- a/FitConnect/Encryption/JoseEncryptor.cs +++ b/FitConnect/Encryption/JoseEncryptor.cs @@ -65,15 +65,6 @@ public class JoseEncryptor : IEncryptor { } } - // public bool TokenValidator(string signature, string key) { - // Jose.Jwk jwk = Jwk.FromJson(key, new JsonMapper()); - // var token = Jose.JweToken.FromString(signature); - // Jose. - // - // - // - // } - private (string plainText, byte[] plainBytes, byte[] tag) Decrypt(Jwk key, string payload) { var result = JWE.Decrypt(payload, key, Algorithm, Encryption); diff --git a/FitConnect/Encryption/JsonHelper.cs b/FitConnect/Encryption/JsonHelper.cs index 9a7d78ea6ba643b78d48b3c1722766c6a0d68181..898db9c8caca9974745edf6ed92284f5f219e6d8 100644 --- a/FitConnect/Encryption/JsonHelper.cs +++ b/FitConnect/Encryption/JsonHelper.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Text.RegularExpressions; +using FitConnect.Models; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; @@ -25,9 +26,9 @@ public static class JsonHelper { schema ??= await JsonSchema.FromJsonAsync(schemaString); var schemaName = schema.ExtensionData["$id"].ToString(); - if (schemaName == null || !new Regex( - "https://schema.fitko.de/fit-connect/metadata/1.[0-9]{1,}.[0-9]{1,}/metadata.schema.json") - .IsMatch(schemaName)) { + + + if (!(schemaName == null || new Regex(Metadata.SchemaPattern).IsMatch(schemaName))) { logger?.LogError("Unsupported metadata schema version"); return false; } diff --git a/FitConnect/FitConnect.csproj b/FitConnect/FitConnect.csproj index a2743441ce76fbf323e2e9d2ae151c9027187e18..1852e1afae50a4dffc2fd2579e040af38cc20740 100644 --- a/FitConnect/FitConnect.csproj +++ b/FitConnect/FitConnect.csproj @@ -6,7 +6,7 @@ <Nullable>enable</Nullable> <AssemblyVersion></AssemblyVersion> <FileVersion></FileVersion> - <PackageVersion>0.9.3-beta.2</PackageVersion> + <PackageVersion>0.9.1-beta.1</PackageVersion> <Title>FIT-Connect .NET SDK</Title> <Description>Library for sending and receiving submissions via FIT-Connect</Description> <Copyright>2022 FIT-Connect contributors</Copyright> @@ -22,33 +22,35 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Autofac" Version="6.4.0"/> - <PackageReference Include="IdentityModel" Version="6.0.0"/> - <PackageReference Include="jose-jwt" Version="4.0.0"/> - <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1"/> - <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.22.0"/> - <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.22.0"/> - <PackageReference Include="MimeTypeMap.List" Version="2.1.0"/> - <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/> - <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14"/> - <PackageReference Include="NJsonSchema" Version="10.7.2"/> - <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.21.0"/> + <PackageReference Include="Autofac" Version="6.5.0" /> + <PackageReference Include="IdentityModel" Version="6.0.0" /> + <PackageReference Include="jose-jwt" Version="4.1.0" /> + <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" /> + <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.25.1" /> + <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.25.1" /> + <PackageReference Include="MimeTypeMap.List" Version="2.1.0" /> + <PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> + <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" /> + <PackageReference Include="NJsonSchema" Version="10.8.0" /> + <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" /> </ItemGroup> <ItemGroup> - <None Include="./nuget-readme.md" Pack="true" PackagePath="\"/> - <None Include="icon.png" Pack="true" Visible="false" PackagePath=""/> - <None Remove="metadata.schema.json"/> - <EmbeddedResource Include="metadata.schema.json"/> - <None Remove="Certificates\PCA-1-Verwaltung-14.pem"/> - <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-14.pem"/> - <None Remove="Certificates\PCA-1-Verwaltung-15.pem"/> - <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-15.pem"/> - <None Remove="Certificates\PCA-1-Verwaltung-17.pem"/> - <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-17.pem"/> - <None Remove="Certificates\PCA-1-Verwaltung-20.pem"/> - <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-20.pem"/> + <None Include="./nuget-readme.md" Pack="true" PackagePath="\" /> + <None Include="icon.png" Pack="true" Visible="false" PackagePath="" /> + <None Remove="metadata.schema.json" /> + <EmbeddedResource Include="metadata.schema.json" /> + <None Remove="Certificates\PCA-1-Verwaltung-14.pem" /> + <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-14.pem" /> + <None Remove="Certificates\PCA-1-Verwaltung-15.pem" /> + <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-15.pem" /> + <None Remove="Certificates\PCA-1-Verwaltung-17.pem" /> + <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-17.pem" /> + <None Remove="Certificates\PCA-1-Verwaltung-20.pem" /> + <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-20.pem" /> + <None Remove="TrustedRootCertificates\PCA-1-Verwaltung-23.pem" /> + <EmbeddedResource Include="TrustedRootCertificates\PCA-1-Verwaltung-23.pem" /> <None Remove="commitId.tmp"/> <EmbeddedResource Include="commitId.tmp"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> diff --git a/FitConnect/FitConnectClient.cs b/FitConnect/FitConnectClient.cs index adb2c8055ade6d4f8cf5093a648437499c0c8287..6596703ef579a1cf52936b0b77ed9acd53cffa64 100644 --- a/FitConnect/FitConnectClient.cs +++ b/FitConnect/FitConnectClient.cs @@ -1,7 +1,9 @@ using System.Net; +using System.Security.Authentication; using Autofac; using FitConnect.Encryption; using FitConnect.Models; +using FitConnect.Models.v1.Api; using FitConnect.Services; using FitConnect.Services.Interfaces; using FitConnect.Services.Models.v1.Submission; @@ -9,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using NJsonSchema; using NJsonSchema.Validation; +using SecurityEventToken = FitConnect.Models.SecurityEventToken; namespace FitConnect; @@ -118,11 +121,11 @@ public abstract class FitConnectClient { /// <param name="destinationId"></param> /// <param name="skipTest"></param> /// <returns></returns> - public async Task<List<SecurityEventToken>> GetStatusForSubmissionAsync(string caseId, + public async Task<List<SecurityEventToken>> GetEventLogAsync(string caseId, string destinationId, bool skipTest = false) { var events = - (await SubmissionService.GetStatusForSubmissionAsync(caseId, destinationId, skipTest)) + (await SubmissionService.GetEventLogAsync(caseId, destinationId, skipTest)) ?.Select(SecurityEventToken.FromJwtEncodedString).ToList() ?? new List<SecurityEventToken>(); return events; @@ -134,18 +137,45 @@ public abstract class FitConnectClient { /// <param name="submission"></param> /// <param name="skipTest"></param> /// <returns></returns> - public async Task<List<SecurityEventToken>> GetStatusForSubmissionAsync( + public async Task<List<SecurityEventToken>> GetEventLogAsync( SubmissionForPickupDto submission, bool skipTest = false) { if (submission?.CaseId == null || submission.DestinationId == null) throw new ArgumentNullException(nameof(submission)); var events = await SubmissionService - .GetStatusForSubmissionAsync(submission.CaseId, submission.DestinationId, skipTest); + .GetEventLogAsync(submission.CaseId, submission.DestinationId, skipTest); return events?.Select(SecurityEventToken.FromJwtEncodedString).ToList() ?? new List<SecurityEventToken>(); } + /// <summary> + /// + /// </summary> + /// <param name="submission"></param> + /// <returns></returns> + public async Task<SubmissionState> GetStatusForSubmissionAsync( + SentSubmission submission) { + var eventLog = await GetEventLogAsync(submission.CaseId, submission.DestinationId); + + var status = eventLog.Last(e => e.SubmissionId == submission.SubmissionId); + List<Problems>? problems = null; + + if (status.Events?.AcceptSubmissionEvent != null) { + if (!(status.Events.AcceptSubmissionEvent.AuthenticationTags ?? + throw new InvalidOperationException()).AreEqualTo(submission + .AuthenticationTags)) { + throw new AuthenticationTagException("Authentication tags are not equal"); + } + + problems = status.Events.AcceptSubmissionEvent.Problems; + } + else if (status.Events?.RejectSubmissionEvent != null) + problems = status.Events.RejectSubmissionEvent.Problems; + + return new SubmissionState(status.EventType, problems); + } + /// <summary> /// Encrypt attachments (Anhänge) /// </summary> diff --git a/FitConnect/Interfaces/IFitConnectClient.cs b/FitConnect/Interfaces/IFitConnectClient.cs index fc63df44acfdd09924b32e2bf915e33d90b1fa21..5d4aa3444cfa638118188cc13559223bc0c00f80 100644 --- a/FitConnect/Interfaces/IFitConnectClient.cs +++ b/FitConnect/Interfaces/IFitConnectClient.cs @@ -10,7 +10,7 @@ public interface IFitConnectClient { /// <param name="destinationId">Skips signature validation if null!</param> /// <param name="skipTest">Specifies if the SET signature test is skipped</param> /// <returns>List of the <see cref="SecurityEventToken" /></returns> - public Task<List<SecurityEventToken>> GetStatusForSubmissionAsync(string caseId, + public Task<List<SecurityEventToken>> GetEventLogAsync(string caseId, string destinationId, bool skipTest = false); } diff --git a/FitConnect/Interfaces/ISender.cs b/FitConnect/Interfaces/ISender.cs index b82825555277f3df386e3976160db017f7f96e20..aa5f1ef45e99f327638deaac68a73c6a43b14945 100644 --- a/FitConnect/Interfaces/ISender.cs +++ b/FitConnect/Interfaces/ISender.cs @@ -1,4 +1,5 @@ using FitConnect.Models; +using FitConnect.Models.v1.Api; using Route = FitConnect.Services.Models.v1.Routes.Route; namespace FitConnect.Interfaces; @@ -25,7 +26,7 @@ public interface ISender : IFitConnectClient { /// the submission /// </param> /// <returns></returns> - Task<Submission> SendAsync(SendableSubmission submission); + Task<SentSubmission> SendAsync(SendableSubmission submission); /// <summary> /// Sending a pre-encrypted submission @@ -35,7 +36,7 @@ public interface ISender : IFitConnectClient { /// to create the submission /// </param> /// <returns></returns> - Task<Submitted> SendAsync(SendableEncryptedSubmission submission); + Task<SentSubmission> SendAsync(SendableEncryptedSubmission submission); /// <summary> /// Receives the public key for the destination with the id <paramref name="destinationId" /> @@ -43,4 +44,10 @@ public interface ISender : IFitConnectClient { /// <param name="destinationId">Destination id of the requested public key</param> /// <returns></returns> Task<string> GetPublicKeyForDestinationAsync(string destinationId); + + /// <summary> + /// Requesting the state of the submission + /// </summary> + /// <returns></returns> + Task<SubmissionState> GetStatusForSubmissionAsync(SentSubmission submission); } diff --git a/FitConnect/Interfaces/Subscriber/ISubscriber.cs b/FitConnect/Interfaces/Subscriber/ISubscriber.cs index f3c92c5ae30882a91fec5a611706fede2dddb075..83f42b19a048fa749d1b43e5878f903a14b5d9fd 100644 --- a/FitConnect/Interfaces/Subscriber/ISubscriber.cs +++ b/FitConnect/Interfaces/Subscriber/ISubscriber.cs @@ -20,10 +20,16 @@ public interface ISubscriber : IFitConnectClient { /// Loads a single Submission by id. /// </summary> /// <param name="submissionId">unique identifier of a <see cref="Submission" /></param> - /// <param name="skipSchemaTest"></param> /// <returns>A subscriber object with a submission</returns> public Task<ISubscriberWithSubmission> RequestSubmissionAsync(string submissionId); + /// <summary> + /// Loads a single Submission. + /// </summary> + /// <param name="submission"></param> + /// <returns>A subscriber object with a submission</returns> + public Task<ISubscriberWithSubmission> RequestSubmissionAsync(SubmissionForPickupDto submission); + /// <summary> /// Rejecting a submission /// </summary> diff --git a/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs b/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs index 09a0b47d50800f22a8a2499121d7eae6eaee21a0..eca2ff16e14c406d1e45aea0dc8eb3df9797bd3f 100644 --- a/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs +++ b/FitConnect/Interfaces/Subscriber/ISubscriberWithSubmission.cs @@ -24,7 +24,7 @@ public interface ISubscriberWithSubmission : WithSubmission { /// <summary> /// Accept submission and delete it from the server /// </summary> - public Task AcceptSubmissionAsync(); + public Task AcceptSubmissionAsync(params Problems[] problems); /// <summary> /// Rejects the submission diff --git a/FitConnect/Models/Attachment.cs b/FitConnect/Models/Attachment.cs index 22360bfbbf0225c95447f2c153b738f8a7cf5f52..7c18f24fd27f4691b785162d130fc64c289b593a 100644 --- a/FitConnect/Models/Attachment.cs +++ b/FitConnect/Models/Attachment.cs @@ -17,19 +17,15 @@ public class Attachment { : metadata.MimeType; } - - /// <summary> - /// Attachment for a submission. - /// </summary> - /// <param name="filename">File name to load</param> - /// <param name="description">Description of the attachment</param> + /// <summary> /// Attachment for a submission. /// </summary> /// <param name="filename">File name to load</param> /// <param name="description">Description of the attachment</param> - public Attachment(string filename, string description, string? mimeType = null) { - Content = File.ReadAllBytes(filename); + public Attachment(string filename, string description, string? mimeType = null, + byte[]? buffer = null) { + Content = buffer ?? File.ReadAllBytes(filename); Filename = Path.GetFileName(filename); MimeType = mimeType ?? MimeTypeMap.List.MimeTypeMap.GetMimeType(Path.GetExtension(filename)) .First(); @@ -45,7 +41,7 @@ public class Attachment { public string Id { get; } = Guid.NewGuid().ToString(); public byte[]? Content { get; init; } - public string? AttachmentAuthentication { get; } + public string? AttachmentAuthentication { get; internal set; } public string? Hash => CalculateHash(); diff --git a/FitConnect/Models/EncryptedSubmission.cs b/FitConnect/Models/EncryptedSubmission.cs new file mode 100644 index 0000000000000000000000000000000000000000..cc7c4bf7f875431dfb92b08370654432d8b51aa8 --- /dev/null +++ b/FitConnect/Models/EncryptedSubmission.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json; + +namespace FitConnect.Models; + +public class EncryptedSubmission { + public EncryptedSubmission(string destinationId, string serviceName, string leikaKey, + string data, string metadata, Dictionary<string, string> attachments) { + DestinationId = destinationId; + ServiceName = serviceName; + LeikaKey = leikaKey; + Data = data; + Metadata = metadata; + Attachments = attachments; + } + + [JsonProperty("destinationId")] + public string DestinationId { get; init; } + + [JsonProperty("serviceName")] + public string ServiceName { get; init; } + + [JsonProperty("leikaKey")] + public string LeikaKey { get; init; } + + [JsonProperty("data")] + public string Data { get; init; } + + [JsonProperty("metadata")] + public string Metadata { get; init; } + + [JsonProperty("attachments")] + public Dictionary<string, string> Attachments { get; init; } + + public void Deconstruct(out string destinationId, out string serviceName, out string leikaKey, + out string data, out string metadata, out Dictionary<string, string> attachments) { + destinationId = DestinationId; + serviceName = ServiceName; + leikaKey = LeikaKey; + data = Data; + metadata = Metadata; + attachments = Attachments; + } +} diff --git a/FitConnect/Models/FitConnectException.cs b/FitConnect/Models/FitConnectException.cs index 91ca6c592ae3525804d42abe2e2e3a576ab3173a..d1fdf759d9b3380df5b560677a7f6c4e32643e33 100644 --- a/FitConnect/Models/FitConnectException.cs +++ b/FitConnect/Models/FitConnectException.cs @@ -15,3 +15,9 @@ public class FitConnectException : Exception { public ErrorTypeEnum ErrorType { get; set; } } + +public class AuthenticationTagException : Exception { + public AuthenticationTagException(string message, + Exception? innerException = null) : base(message, innerException) { + } +} diff --git a/FitConnect/Models/Metadata.cs b/FitConnect/Models/Metadata.cs index ac4a19be1f24d7b1a6a4f05f001607bee13b0314..467991352498e15dea87823cfa71257cd06fd373 100644 --- a/FitConnect/Models/Metadata.cs +++ b/FitConnect/Models/Metadata.cs @@ -5,6 +5,9 @@ namespace FitConnect.Models; public class Metadata : Api.Metadata.Metadata { public const string SchemaUrl = "https://schema.fitko.de/fit-connect/metadata/1.0.0/metadata.schema.json"; + + public const string SchemaPattern = + "https://schema.fitko.de/fit-connect/metadata/1.[0-9]{1,}.[0-9]{1,}/metadata.schema.json"; } public class Data { diff --git a/FitConnect/Models/SET/AcceptSubmissionEvent.cs b/FitConnect/Models/SET/AcceptSubmissionEvent.cs index eb7c8b55d05ad669f28333020af3ec72ca974897..36d405a63601af681024bf603f853a1d5cda0dcd 100644 --- a/FitConnect/Models/SET/AcceptSubmissionEvent.cs +++ b/FitConnect/Models/SET/AcceptSubmissionEvent.cs @@ -3,14 +3,20 @@ using Newtonsoft.Json; namespace FitConnect.Models; -public class AcceptSubmissionEvent : SubmitSubmissionEvent, ISubmissionEvent { +public class AcceptSubmissionEvent : SubmitSubmissionEvent, ISubmissionEvent, IStatusWithProblems { public AcceptSubmissionEvent() { } - public AcceptSubmissionEvent(AuthenticationTags payload) { - AuthenticationTags = payload; + + public AcceptSubmissionEvent(AuthenticationTags payload, IEnumerable<Problems>? problems) { + this.AuthenticationTags = payload; + this.Problems = problems?.ToList(); } [JsonProperty("problems", NullValueHandling = NullValueHandling.Ignore)] public List<Problems>? Problems { get; set; } } + +public interface IStatusWithProblems { + public List<Problems>? Problems { get; set; } +} diff --git a/FitConnect/Models/SET/AuthenticationTags.cs b/FitConnect/Models/SET/AuthenticationTags.cs index 8f25e5d54eae2968241ada5c25c1e8d32756823a..13c12f5c7036eb24f3f63b1ad8fc797524a84c58 100644 --- a/FitConnect/Models/SET/AuthenticationTags.cs +++ b/FitConnect/Models/SET/AuthenticationTags.cs @@ -1,3 +1,4 @@ +using FitConnect.Models.v1.Api; using Newtonsoft.Json; namespace FitConnect.Models; @@ -12,3 +13,21 @@ public class AuthenticationTags { [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)] public Dictionary<string, string>? Attachments { get; set; } } + +public static class AuthenticationTagExtensions { + public static bool AreEqualTo(this AuthenticationTags expected, AuthenticationTags actual) { + if (expected.Attachments == null && actual.Attachments == null) { + return expected.Data == actual.Data && expected.Metadata == actual.Metadata; + } + + if (actual.Attachments == null ^ expected.Attachments == null) { + return false; + } + + return expected.Metadata == actual.Metadata + && expected.Data == actual.Data + && expected.Attachments!.Any((item) => + actual.Attachments!.ContainsKey(item.Key) && + actual.Attachments[item.Key] == item.Value); + } +} diff --git a/FitConnect/Models/SET/RejectSubmissionEvent.cs b/FitConnect/Models/SET/RejectSubmissionEvent.cs index d7832009ea27e4c02b62a8abdd5b53209e424a2e..ec61294f98e972f18662d743a3bad9511aa84678 100644 --- a/FitConnect/Models/SET/RejectSubmissionEvent.cs +++ b/FitConnect/Models/SET/RejectSubmissionEvent.cs @@ -3,15 +3,15 @@ using Newtonsoft.Json; namespace FitConnect.Models; -public class RejectSubmissionEvent : ISubmissionEvent { +public class RejectSubmissionEvent : ISubmissionEvent, IStatusWithProblems { public RejectSubmissionEvent() { Problems = new List<Problems>(); } - public RejectSubmissionEvent(Problems[] problemsArray) { - Problems = problemsArray.ToList(); + public RejectSubmissionEvent(IEnumerable<Problems> problemsArray) { + this.Problems = problemsArray.ToList(); } [JsonProperty("problems", NullValueHandling = NullValueHandling.Ignore)] - public List<Problems> Problems { get; set; } -} + public List<Problems>? Problems { get; set; } +} \ No newline at end of file diff --git a/FitConnect/Models/SET/SubmissionState.cs b/FitConnect/Models/SET/SubmissionState.cs new file mode 100644 index 0000000000000000000000000000000000000000..cff517930ad913e25fc6d5551a286c45a232d190 --- /dev/null +++ b/FitConnect/Models/SET/SubmissionState.cs @@ -0,0 +1,21 @@ +using FitConnect.Models.v1.Api; + +namespace FitConnect.Models; + +/// <summary> +/// The current status of the submission. +/// </summary> +public class SubmissionState { + public SubmissionState(EventType statusEventType, List<Problems>? problems) { + Status = statusEventType; + Problems = problems; + } + + public EventType Status { get; } + public List<Problems>? Problems { get; } + + public void Deconstruct(out EventType status, out List<Problems>? problems) { + status = Status; + problems = Problems; + } +} diff --git a/FitConnect/Models/Submission.cs b/FitConnect/Models/Submission.cs index 84ec1db5c5ded95611bc691b8a4a1632e58a34d7..509fc67399f7bf5ea4099056644954dcedd0826a 100644 --- a/FitConnect/Models/Submission.cs +++ b/FitConnect/Models/Submission.cs @@ -27,6 +27,7 @@ public class Submission : ISubmitted { public string SubmissionId => Id; public string CaseId { get; set; } = null!; + internal string? GeneratedMetadata { get; set; } public string DestinationId { get => Destination.DestinationId ?? throw new ArgumentNullException(nameof(DestinationId)); @@ -110,6 +111,35 @@ public interface ISubmitted { public string? DestinationId { get; } } +public class SentSubmission : ISubmitted { + public SentSubmission(string submissionId, string caseId, string destinationId, + AuthenticationTags authenticationTags) { + SubmissionId = submissionId; + CaseId = caseId; + DestinationId = destinationId; + AuthenticationTags = authenticationTags; + } + + public string SubmissionId { get; set; } + public string CaseId { get; set; } + public string DestinationId { get; set; } + public AuthenticationTags AuthenticationTags { get; set; } + + + public static explicit operator SentSubmission(Submission submission) + => new SentSubmission( + submission.Id, + submission.CaseId, + submission.DestinationId, + new AuthenticationTags { + Data = submission.DataAuthentication, + Metadata = submission.MetaAuthentication, + Attachments = + submission.Attachments?.ToDictionary(i => i.Id, + i => i.AttachmentAuthentication!) + }); +} + public class Submitted : ISubmitted { public Submitted() { } diff --git a/FitConnect/Sender.cs b/FitConnect/Sender.cs index e16aacee206ec77392caaafe179804a63c8a2870..a70f3126ca3e5b13b501166b53989a2475a62009 100644 --- a/FitConnect/Sender.cs +++ b/FitConnect/Sender.cs @@ -54,7 +54,7 @@ public class Sender : FitConnectClient, ISender { return publicKey; } - public async Task<Submission> SendAsync(SendableSubmission submission) { + public async Task<SentSubmission> SendAsync(SendableSubmission submission) { var sendable = await CreateSubmission(submission.DestinationId); sendable.AddServiceType(submission.ServiceName!, submission.LeikaKey!); @@ -75,7 +75,12 @@ public class Sender : FitConnectClient, ISender { var encryptedAttachments = Encrypt(PublicKey!, sendable.Attachments); await UploadAttachmentsAsync(sendable.Id!, encryptedAttachments); - return await Submit(sendable); + foreach (var (key, value) in encryptedAttachments) { + var item = submission.Attachments!.Single(i => i.Id == key); + item.AttachmentAuthentication = value.Split('.').Last(); + } + + return (SentSubmission)await Submit(sendable); } @@ -86,7 +91,7 @@ public class Sender : FitConnectClient, ISender { /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> - public async Task<Submitted> SendAsync(SendableEncryptedSubmission submission) { + public async Task<SentSubmission> SendAsync(SendableEncryptedSubmission submission) { SubmissionSenderGuards(submission); var sendable = await CreateSubmission(submission.DestinationId); @@ -109,7 +114,7 @@ public class Sender : FitConnectClient, ISender { Logger?.LogInformation("Uploading pre encrypted attachments"); await UploadAttachmentsAsync(sendable.Id!, submission.Attachments!); - return await Submit(sendable); + return (SentSubmission)(await Submit(sendable)); } @@ -134,6 +139,7 @@ public class Sender : FitConnectClient, ISender { if (submission.EncryptedMetadata == null) { var metadata = CreateMetadata(submission); + submission.GeneratedMetadata = metadata; Logger?.LogTrace("MetaData: {Metadata}", metadata); var valid = await JsonHelper.VerifyMetadata(metadata, null); if (!valid) { diff --git a/FitConnect/Services/Interfaces/ISubmissionService.cs b/FitConnect/Services/Interfaces/ISubmissionService.cs index 6bbbd1047085b6763202103d17f5cd6eb59bbe27..78a015c4efcab4ec138c85231e3956d79bf04e0f 100644 --- a/FitConnect/Services/Interfaces/ISubmissionService.cs +++ b/FitConnect/Services/Interfaces/ISubmissionService.cs @@ -85,7 +85,7 @@ public interface ISubmissionService : IRestCallService { /// <param name="destinationId"></param> /// <param name="skipTest"></param> /// <returns></returns> - Task<List<string>?> GetStatusForSubmissionAsync(string caseId, string destinationId, + Task<List<string>?> GetEventLogAsync(string caseId, string destinationId, bool skipTest = false); } diff --git a/FitConnect/Services/SubmissionService.cs b/FitConnect/Services/SubmissionService.cs index 7c24344ced1957acab8253b2f38551b4692fc92d..9dca6567170fca313964763c16661f9af016e6ca 100644 --- a/FitConnect/Services/SubmissionService.cs +++ b/FitConnect/Services/SubmissionService.cs @@ -145,7 +145,7 @@ internal class SubmissionService : RestCallService, ISubmissionService { throw new NotImplementedException(); } - public async Task<List<string>?> GetStatusForSubmissionAsync(string caseId, + public async Task<List<string>?> GetEventLogAsync(string caseId, string destinationId, bool skipTest = false) { await _oAuthService.EnsureAuthenticatedAsync(); diff --git a/FitConnect/SubmissionBuilder.cs b/FitConnect/SubmissionBuilder.cs index 4fedb82be8dcc9326017c4e6cc9c546fe486db35..ca0d6b974c1cde74ea424f2e243203cd461a0535 100644 --- a/FitConnect/SubmissionBuilder.cs +++ b/FitConnect/SubmissionBuilder.cs @@ -19,8 +19,27 @@ public class SubmissionBuilder : ISubmissionWithDestination, return WithAttachments(attachments.ToArray()); } + private ISubmissionWithData WithAttachments(string fileName, string description, string mimeType, + FileStream fileStream) { + byte[]? buffer; + using (fileStream) { + buffer = new byte[fileStream.Length]; + fileStream.Read(buffer, 0, (int)fileStream.Length); + } + + var attachment = new Attachment(fileName, description, mimeType, buffer); + + return WithAttachments(attachment); + } + public ISubmissionWithData WithAttachments(params Attachment[] attachments) { - _submissionToSend.Attachments = attachments; + _submissionToSend.Attachments ??= new Attachment[] { }; + + foreach (var attachment in attachments) { + _submissionToSend.Attachments = + _submissionToSend.Attachments.Append(attachment).ToArray(); + } + return this; } @@ -89,6 +108,13 @@ public interface ISubmissionWithData { ISubmissionWithData WithAttachments(params Attachment[] attachments); ISubmissionWithData WithAttachments(List<Attachment> attachments); + /// <summary> + /// For future use + /// </summary> + /// <returns></returns> + // ISubmissionWithData WithAttachments(string fileName, string description, string mimeType, + // FileStream content); + SendableSubmission Build(); } diff --git a/FitConnect/Subscriber.cs b/FitConnect/Subscriber.cs index 9a1f858b6abc8b92671df7affe7e3080120cef2e..7bb4ee1b506ccf892cb28da7e17b8a35e2306ff9 100644 --- a/FitConnect/Subscriber.cs +++ b/FitConnect/Subscriber.cs @@ -68,6 +68,10 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, } + public async Task<ISubscriberWithSubmission> + RequestSubmissionAsync(SubmissionForPickupDto submission) => + await RequestSubmissionAsync(submission.Id); + /// <summary> /// Receives a specific submission from the server and verifies submission<br /> /// https://docs.fitko.de/fit-connect/docs/receiving/verification/ @@ -79,66 +83,6 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, return await CheckSubmissionAsync(submission); } - public async Task RejectSubmissionAsync(ISubmitted submission, params Problems[] problems) { - await CompleteSubmission(submission, FinishSubmissionStatus.Rejected, problems); - } - - public Submission? Submission { get; private set; } - - - /// <summary> - /// Reading attachments for a submission. - /// </summary> - /// <returns></returns> - /// <summary> - /// Reading attachments for a submission. - /// </summary> - /// <returns></returns> - public async Task<IEnumerable<Attachment>> GetAttachmentsAsync() { - if (Submission?.Id == null || Submission?.Metadata == null) - throw new Exception("No submission available"); - - var attachments = new List<Attachment>(); - foreach (var id in Submission!.AttachmentIds) { - var encryptedAttachment = await SubmissionService.GetAttachmentAsync(Submission.Id, id); - var (_, content, hash) = Encryption.Decrypt(encryptedAttachment); - var attachmentMeta = - Submission.Metadata.ContentStructure.Attachments.First(a => a.AttachmentId == id); - - attachments.Add(new Attachment(id, attachmentMeta, content, - encryptedAttachment.Split(ProjectSpecification.TokenSeparator).Last()) { - Description = attachmentMeta.Description, - Purpose = attachmentMeta.Purpose, - MimeType = attachmentMeta.MimeType - }); - } - - Submission.Attachments = attachments; - return attachments; - } - - public async Task AcceptSubmissionAsync() { - await CompleteSubmissionAsync(Submission!, FinishSubmissionStatus.Accepted); - } - - public async Task RejectSubmissionAsync(params Problems[] problems) { - await CompleteSubmissionAsync(Submission!, FinishSubmissionStatus.Rejected, problems); - } - - public bool VerifyStatus(AcceptanceStatus acceptanceStatus) { - if (Submission == null) - throw new NullReferenceException("Submission is null"); - var result = true; - result &= acceptanceStatus.Metadata == Submission.MetaAuthentication; - result &= acceptanceStatus.Data == Submission.DataAuthentication; - if (Submission.Attachments != null) - foreach (var attachment in Submission.Attachments) - result &= acceptanceStatus.Attachments[attachment.Id] == - attachment.AttachmentAuthentication; - - return result; - } - internal async Task<ISubscriberWithSubmission> CheckSubmissionAsync(Submission submission) { // SuccessCriteria:2.2, 2.3 var (submitEvent, metadataSignature) = await CheckSecurityEventTokensAsync(submission); @@ -179,24 +123,25 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, Problems.TitleMissingSchema, Problems.DetailMissingSchema, Problems.ProblemInstanceEnum.Metadata); - await RejectSubmissionAsync(submission, problem); - throw new SecurityEventException(problem); + // await RejectSubmissionAsync(submission, problem); + // throw new SecurityEventException(problem); } + else { + JsonSchema? schema; + try { + schema = await JsonSchema.FromUrlAsync(submission.Metadata?.Schema); + } + catch (Exception e) { + // SuccessCriteria:3.5 + var problem = new Problems(Problems.ProblemTypeEnum.UnsupportedMetaSchema, + Problems.DetailUnsupportedSchema); + await RejectSubmissionAsync(submission, problem); + throw new SecurityEventException(problem, e); + } - JsonSchema? schema; - try { - schema = await JsonSchema.FromUrlAsync(submission.Metadata?.Schema); - } - catch (Exception e) { - // SuccessCriteria:3.5 - var problem = new Problems(Problems.ProblemTypeEnum.UnsupportedMetaSchema, - Problems.DetailUnsupportedSchema); - await RejectSubmissionAsync(submission, problem); - throw new SecurityEventException(problem, e); + await VerifyMetadata(submission, metadataString, schema); } - await VerifyMetadata(submission, metadataString, schema); - // SuccessCriteria:3.3 if (submission.Metadata == null) { var problem = new Problems(Problems.ProblemTypeEnum.SyntaxViolation, @@ -317,6 +262,66 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, return this; } + public async Task RejectSubmissionAsync(ISubmitted submission, params Problems[] problems) { + await CompleteSubmission(submission, FinishSubmissionStatus.Rejected, problems); + } + + public Submission? Submission { get; private set; } + + + /// <summary> + /// Reading attachments for a submission. + /// </summary> + /// <returns></returns> + /// <summary> + /// Reading attachments for a submission. + /// </summary> + /// <returns></returns> + public async Task<IEnumerable<Attachment>> GetAttachmentsAsync() { + if (Submission?.Id == null || Submission?.Metadata == null) + throw new Exception("No submission available"); + + var attachments = new List<Attachment>(); + foreach (var id in Submission!.AttachmentIds) { + var encryptedAttachment = await SubmissionService.GetAttachmentAsync(Submission.Id, id); + var (_, content, hash) = Encryption.Decrypt(encryptedAttachment); + var attachmentMeta = + Submission.Metadata.ContentStructure.Attachments.First(a => a.AttachmentId == id); + + attachments.Add(new Attachment(id, attachmentMeta, content, + encryptedAttachment.Split(ProjectSpecification.TokenSeparator).Last()) { + Description = attachmentMeta.Description, + Purpose = attachmentMeta.Purpose, + MimeType = attachmentMeta.MimeType + }); + } + + Submission.Attachments = attachments; + return attachments; + } + + public async Task AcceptSubmissionAsync(params Problems[] problems) { + await CompleteSubmissionAsync(Submission!, FinishSubmissionStatus.Accepted, problems); + } + + public async Task RejectSubmissionAsync(params Problems[] problems) { + await CompleteSubmissionAsync(Submission!, FinishSubmissionStatus.Rejected, problems); + } + + public bool VerifyStatus(AcceptanceStatus acceptanceStatus) { + if (Submission == null) + throw new NullReferenceException("Submission is null"); + var result = true; + result &= acceptanceStatus.Metadata == Submission.MetaAuthentication; + result &= acceptanceStatus.Data == Submission.DataAuthentication; + if (Submission.Attachments != null) + foreach (var attachment in Submission.Attachments) + result &= acceptanceStatus.Attachments[attachment.Id] == + attachment.AttachmentAuthentication; + + return result; + } + private string? GetSchemaUrlFromJson(string? jsonString) { return jsonString == null @@ -372,6 +377,10 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, // SuccessCriteria: Hash-Check 5.4 if (attachmentMeta?.Hash.Content != FitEncryption.CalculateHash(content).Content) { + Logger?.LogWarning( + "Wrong hash for attachment {attachment}\nHash should be {should} but is {is}", + id, FitEncryption.CalculateHash(content).Content, + attachmentMeta?.Hash.Content); var problem = new Problems( Problems.ProblemTypeEnum.HashMismatch, Problems.TitleHashMismatch, @@ -429,11 +438,6 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, submission.DestinationId == null) throw new ArgumentException("Submission does not contain all required fields"); - if (status != FinishSubmissionStatus.Rejected && problems != null) - throw new ArgumentException("Problems can only be set for rejected submissions"); - - // TODO Generate AuthenticationTags for Accept token - string? token; switch (status) { case FinishSubmissionStatus.Rejected: @@ -445,7 +449,7 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, if (submission is Submission sub) { var authenticationTags = FitEncryption.GenerateAuthenticationTags(sub); - token = await Encryption.CreateAcceptSecurityEventToken(submission, + token = await Encryption.CreateAcceptSecurityEventToken(submission, problems, authenticationTags); } else { @@ -589,7 +593,7 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, Submission submission) { List<SecurityEventToken> status; try { - status = await GetStatusForSubmissionAsync(submission); + status = await GetEventLogAsync(submission); } catch (Exception? e) { Logger?.LogWarning("Could not get status for submission: {SubmissionId}", @@ -612,7 +616,7 @@ public class Subscriber : FitConnectClient, ISubscriberWithSubmission, } var submitEvent = status.First(set => set.EventType == EventType.Submit); - var authenticationTag = submitEvent.Events?.SubmitSubmissionEvent?.AuthenticationTags; + var authenticationTag = (submitEvent.Events?.SubmitSubmissionEvent?.AuthenticationTags); var dataSignature = authenticationTag?.Data; var metadataSignature = authenticationTag?.Metadata ?? ""; diff --git a/FitConnect/TrustedRootCertificates/PCA-1-Verwaltung-23.pem b/FitConnect/TrustedRootCertificates/PCA-1-Verwaltung-23.pem new file mode 100644 index 0000000000000000000000000000000000000000..c4ff111515d238e5643906257f3fd6c276c36251 --- /dev/null +++ b/FitConnect/TrustedRootCertificates/PCA-1-Verwaltung-23.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGiDCCBHCgAwIBAgIFAKXvkjAwDQYJKoZIhvcNAQELBQAwRjELMAkGA1UEBhMC +REUxGTAXBgNVBAoTEFBLSS0xLVZlcndhbHR1bmcxHDAaBgNVBAMTE1BDQS0xLVZl +cndhbHR1bmctMjMwHhcNMjIxMjAxMDAwMDAwWhcNMzIxMjMxMjM1OTU5WjBGMQsw +CQYDVQQGEwJERTEZMBcGA1UEChMQUEtJLTEtVmVyd2FsdHVuZzEcMBoGA1UEAxMT +UENBLTEtVmVyd2FsdHVuZy0yMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAMXZeBfphlB9m/8J8wVxSjEtOqfdftHEiQeITx31Md9by7+ppxHKzhQcDrRU +3QxZnpNuoj+pIz73ZTPzBuz8PnW5BMnzPYK/0+TJiWDiUmWQ4NxwgHxV+S1KLX6S +gkwr3L6C3VuC54rVnGeDTV6RyVzzQBpaGv1H7E2HRJh7MP3adV+Wk9DyvR4m6AE7 +6R8rI8qCv99pso2y5mAR0ulfA9zyNKxBooYSefGxQgNn+Fwb+41TBMv3iDEro7x5 +3tGHsOC/8F+xOxBe093/P2GHtEdGdAKeo6hhEq8encG0+uHIj4SljiTzKZw7PYcK +LLZvvUyCytSuFA5Z4nRXLO2hsdqaJfT1+qecs1uJjYx0ZlzVGEsIWOpKtbB9vcvq +IkYspykCsWcBnf1tB9F+IERw59jsGD9nZKWIgHRgI5fqHzaf0A+LwbeIO4Fn1keY +wmYF8bNFtMCWW51ukrTQjoOc7EaE9S460E7pdmCG4NGau/bN8iQQf/cDBuRvlEpG +aN7NeaEXWFbOBHyJKWqpquJBABFVZFaaQpBxWlnnV+o1EZ5IZPtefHFSGMxycJRb +m6pVfGmuEo+z7xYClLH1gMDHiIDKy+RbLNMyVWUh5viu28EU29CXA7Nx6LmfLwvl +Gr1vyCu4UAXOTrRZwff0X19tk5TjHy/SK9jnXkKURe9WuKW1AgMBAAGjggF7MIIB +dzAPBgNVHRMBAf8EBTADAQH/MD8GA1UdIAQ4MDYwNAYLBAB/AAcDBgEBAQQwJTAj +BggrBgEFBQcCARYXaHR0cHM6Ly93d3cuYnNpLmJ1bmQuZGUwgbwGA1UdHwSBtDCB +sTBioGCgXoZcbGRhcDovL3g1MDAuYnVuZC5kZS9DTj1QQ0EtMS1WZXJ3YWx0dW5n +LTIzLE89UEtJLTEtVmVyd2FsdHVuZyxDPURFP2NlcnRpZmljYXRlUmV2b2NhdGlv +bkxpc3QwS6BJoEeGRWh0dHA6Ly94NTAwLmJ1bmQuZGUvY2dpLWJpbi9zaG93X2F0 +dHI/Y249UENBLTEtVmVyd2FsdHVuZy0yMyZhdHRyPWNybDAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFJ2BqFcYkLlZ9LOTo6q+LLqE9BnGMDUGA1UdEQQuMCyBEVYt +UEtJQGJzaS5idW5kLmRlhhdodHRwczovL3d3dy5ic2kuYnVuZC5kZTANBgkqhkiG +9w0BAQsFAAOCAgEAQiNGSGjBukYu5TRDaw2J7RPcDZltf/Nz6dmwzo1gXFb7ALnd +X+Kv9OLxqTKcpywwI017xgRjzxoLlk8PZNkxl+2fuY0vqvW/c8ys2/KjXGHTgwKm +xwvDov8vBfQxl4fXiFPxeixZujT0YZKVJ2L+6HcqzrNVNDpgNzpeLPVZxNZc0rOz +3jxnCGwGH0mskPAMjObn9KizDge1Zve9l10jaPyyKeZq+MDhHtbwxBEICC8yCx4W ++FcETRJpQSmfP7Yr33cbitb+IaQpUf9pPRqIRUfc8Zc/lOBv49q2N+iOrCNv5XHw +mS5HnuQ4yvSJBNgVqU+dTHvcZZVjSXCTpdwmzlmJpEtPFtJks09Ug1TYU0gQY1JU +KW3rfndKCcsXcYA0Frvm/EA3arwbXIQRdiz1cQchHSn+IVRiUY7H9Am7ktHhPpM9 +haNpg7rfLQJnYyKKZxEs2LI84oPqRNvHBVFMFO2rY+K9HG/HWV3SixQtLAvLQOAe +vIXuLdCZJMG8O/3Of0xLlGC/GhCXoP0g4lr7KluUzUTZj6K1q0b8NgVUqLtNiKfY +58BefTEwUv4kSMmyQQAH69sx2wLKVKTqkOP3c81jDhf7OQs41cMGu+HRXtgy4FEI +F3WfIOCHR3z1jJckcZwjh4WoaOgv8sGxubwjGLi2ZelCjt9ZKql+oF5LWvY= +-----END CERTIFICATE----- diff --git a/FitConnect/nuget-readme.md b/FitConnect/nuget-readme.md index dd8bbe6e03f3da7ed5d37b1d435bf11063cec244..198ed5d498554d6982a5fd0a6aae10debdd3684e 100644 --- a/FitConnect/nuget-readme.md +++ b/FitConnect/nuget-readme.md @@ -12,7 +12,7 @@ Auf OSX wird das SDK nur dann unterstützt, wenn OpenSSL auf dem System installi Zum Installieren von OpenSSL können Sie Homebrew verwenden: ```sh -brew install openssl@1.1 +brew install openssl ``` Die Environment-Variable ```DYLD_LIBRARY_PATH``` muss auf den Pfad zu OpenSSL verweisen. @@ -21,7 +21,7 @@ _Beispiele:_ ```sh export DYLD_LIBRARY_PATH=/usr/local/opt/openssl/lib -export DYLD_LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib +export DYLD_LIBRARY_PATH=/opt/homebrew/opt/openssl@3/lib ``` ### Sender @@ -46,27 +46,52 @@ In der Produktivumgebung müssen hierzu [Zertifikate der Verwaltungs-PKI zum Ein ## Sender +### Instanz des Senders erzeugen + +Der `Sender` wir in einem *Builder* im `Client` erstellt. +Dazu werden die `FitConnectEnvironment`, die _ClientId_ und das _ClientSecret_ benötigt. + +```csharp +var sender = Client.GetSender(FitConnectEnvironment.Testing, clientId, clientSecret, logger); +``` + ### Erstellen einer Einreichung (submission) Das folgende Beispiel zeigt, wie Sie das SDK in einem sendenden System nutzen, um eine Submission (eine Einreichung) zu erzeugen und zu senden: ```csharp -vvar client = Client.GetSender(FitConnectEnvironment.Testing, clientId, clientSecret, logger); - var submission = SubmissionBuilder .WithDestination(destinationId) .WithServiceType("FIT Connect Demo", leikaKey) .WithAttachments(new Attachment("Test.pdf", "Test Attachment")) - .WithJsonData("{\"message\":\"Hello World\"}") + .WithData("{\"message\":\"Hello World\"}") .Build(); -await client.SendAsync(submission); +await sender.SendAsync(submission); ``` Im Beispiel oben stellt das Argument "FitConnectEnvironment.Testing" die FIT-Connect-Endpunkte zur Verfügung, die aufgerufen werden sollen. Das Argument 'destinationId" enthält die eindeutige Adresse des empfangenden Systems (Fachverfahrens), das die Antragsdaten über FIT-Connect erhalten soll. Das Argument "leikaKey" stellt die ID der beantragten Leistung bereit. "Leika" ist die Abkürzung für "Leistungskatalog". +### Abrufen des Event-Logs und des Status' + +Der `sender` verfügt über eine Methode `GetEventLogAsync()` und `GetStatusForSubmissionAsync()` mit denen der aktuelle Status der Submission beim Zustelldienst abgefragt werden kann. + +```csharp +var (status, problems) = await sender.GetStatusForSubmission(sentSubmission); +``` + +Die SentSubmission enthält alle nottwendigen Informationen, um den Status abzufragen. +Auch die `AthenticationTags` sind darin enthalten mit denen der Status überprüft werden kann. + +Der `status` beinhaltet den aktuellen _Zustand_ der Submission. +In den `problems` sind Rückmeldungen vom _Subscriber_ enthalten. + +Das gesamte `EventLog` kann ebenfalls abgerufen werden. +Dazu wird die Methode `sender.GetEventLogAsync(caseId, destinationId)` aufgerufen. +Eine Überprüfung der Gültikeit des Statuses erfolgt hier **nicht**. + ## Subscriber ### Erstellen eines Subscribers @@ -88,6 +113,7 @@ Das Argument 'privateKeySigning" enthält den JWK des privaten Schlüssels zum S Das Argument 'publicKeyEncryption" liefert den JWK des öffentlichen Schlüssels zum Verschlüsseln. Das Argument 'publicKeySignatureVerification" enthält den JWK des öffentlichen Schlüssels zum Überprüfen der Signatur. + ### Abrufen der verfügbaren Submissions Das folgende Beispiel zeigt, wie Sie das SDK in einem empfangenden System nutzen, um beim Server (dem Zustelldienst von FIT-Connect) nachzufragen, welche Einreichungen (Submissions) zur Abholung bereitliegen. diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj index e98fdfa30328e02eb43801fe8e63c95690ee343c..582d796fd27062dabdc60c6a4e83fcb2873ce7d7 100644 --- a/IntegrationTests/IntegrationTests.csproj +++ b/IntegrationTests/IntegrationTests.csproj @@ -8,22 +8,25 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="DotNet.Testcontainers" Version="1.6.0"/> - <PackageReference Include="FluentAssertions" Version="6.7.0"/> - <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2"/> - <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/> - <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.22.0"/> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/> - <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14"/> - <PackageReference Include="NUnit" Version="3.13.2"/> - <PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/> - <PackageReference Include="coverlet.collector" Version="3.1.0"/> + <PackageReference Include="DotNet.Testcontainers" Version="1.6.0" /> + <PackageReference Include="FluentAssertions" Version="6.8.0" /> + <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> + <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> + <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.25.1" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" /> + <PackageReference Include="NUnit" Version="3.13.3" /> + <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> - <ProjectReference Include="..\MockContainer\MockContainer.csproj"/> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> + <ProjectReference Include="..\MockContainer\MockContainer.csproj" /> </ItemGroup> <ItemGroup> diff --git a/IntegrationTests/Sender/SenderTestHappyPath.cs b/IntegrationTests/Sender/SenderTestHappyPath.cs index 4ce8d67fbc0b3f19cc995d05d3d062b82fb611f5..12f030972c24d27059428829f10bd74083944f28 100644 --- a/IntegrationTests/Sender/SenderTestHappyPath.cs +++ b/IntegrationTests/Sender/SenderTestHappyPath.cs @@ -23,7 +23,7 @@ public class SenderTestHappyPath : SenderTestBase { [TestCase("d2be2027-9368-4c0c-a265-2fdbf7ecd4d9")] public void GetSubmissionStatus(string caseId) { var status = - Sender.GetStatusForSubmissionAsync(caseId, "aa3704d6-8bd7-4d40-a8af-501851f93934", true) + Sender.GetEventLogAsync(caseId, "aa3704d6-8bd7-4d40-a8af-501851f93934", true) .Result; status.ForEach(s => Console.WriteLine($"{s.EventTime} - {s.EventType}")); status.Count.Should().BeGreaterThan(0); @@ -83,12 +83,12 @@ public class SenderTestHappyPath : SenderTestBase { // Assert var statusForSubmission = - Sender.GetStatusForSubmissionAsync(submission.CaseId, DestinationId).Result; + Sender.GetEventLogAsync(submission.CaseId, DestinationId).Result; statusForSubmission.Should().HaveCountGreaterThan(0); Console.WriteLine($"Case ID: {submission.CaseId}"); - Console.WriteLine($"Submission ID: {submission.Id}"); + Console.WriteLine($"Submission ID: {submission.SubmissionId}"); submission.Should().NotBeNull(); } @@ -127,11 +127,11 @@ public class SenderTestHappyPath : SenderTestBase { // Assert var statusForSubmission = - Sender.GetStatusForSubmissionAsync(submission.Id, DestinationId).Result; + Sender.GetEventLogAsync(submission.SubmissionId, DestinationId).Result; statusForSubmission.Should().HaveCountGreaterThan(0); Console.WriteLine($"Case ID: {submission.CaseId}"); - Console.WriteLine($"Submission ID: {submission.Id}"); + Console.WriteLine($"Submission ID: {submission.SubmissionId}"); submission.Should().NotBeNull(); } diff --git a/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs b/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs index 90458d5a4e00ead02c4741ec57d7a67371559e8c..286826e8ecce0866449434a7fbccee38dab7ce2a 100644 --- a/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs +++ b/IntegrationTests/Subscriber/SubscriberTestHappyPath.cs @@ -78,7 +78,7 @@ public class SubscriberTestHappyPath : SubscriberTestBase { submissions.Count().Should().BeGreaterThan(0); foreach (var submission in submissions) - Subscriber.GetStatusForSubmissionAsync(submission.CaseId!, DestinationId).Result + Subscriber.GetEventLogAsync(submission.CaseId!, DestinationId).Result .ForEach(s => Logger.LogInformation("{SubmissionCaseId} - {ObjEventTime} - {ObjEventType}", submission.CaseId, s.EventTime, s.EventType)); @@ -99,7 +99,7 @@ public class SubscriberTestHappyPath : SubscriberTestBase { if (!Directory.Exists($"./attachments/{submissionId}/")) Directory.CreateDirectory($"./attachments/{submissionId}/"); - Subscriber.GetStatusForSubmissionAsync(submission.CaseId!, DestinationId).Result + Subscriber.GetEventLogAsync(submission.CaseId!, DestinationId).Result .ForEach(s => Console.WriteLine($"{s.EventTime} - {s.EventType}")); diff --git a/MockContainer/MockContainer.csproj b/MockContainer/MockContainer.csproj index 11e75c39d7f925887db00eb4dc4e413eb83f4805..6ba4b2bd062c8de222e6886b1a34fd732cc012d5 100644 --- a/MockContainer/MockContainer.csproj +++ b/MockContainer/MockContainer.csproj @@ -8,14 +8,14 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Autofac" Version="6.4.0"/> - <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0"/> - <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/> - <PackageReference Include="Moq" Version="4.18.1"/> + <PackageReference Include="Autofac" Version="6.5.0" /> + <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" /> + <PackageReference Include="Moq" Version="4.18.4" /> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> </ItemGroup> <ItemGroup> diff --git a/readme.md b/readme.md index 3fe0de74c1c98a518184f634f237ad5423c7be11..f7571810b5961d4795832904734389af2c48170b 100644 --- a/readme.md +++ b/readme.md @@ -19,20 +19,18 @@ Das FIT-Connect .NET SDK bietet eine einfache Möglichkeit, sowohl einen Antrags ### OSX -__Anmerkung:__ -Bei einem Mac mit einem M1 chip, muss brew in der _x86-64_-Version installiert werden. +Für die Installation von OpenSSL muss die [Homebrew](https://brew.sh/) Paketverwaltung verwendet werden. +Zur installation von HomeBrew muss der folgende Befehl ausgeführt werden: -_Die Version x86_64 kann parallel zur arm64 Version installiert werden._ - -```sh -arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` Auf OSX wird das SDK nur dann unterstützt, wenn OpenSSL auf dem System installiert ist. Zum Installieren von OpenSSL können Sie Homebrew verwenden: -```sh -brew install openssl@1.1 +```bash +brew install openssl ``` Die Environment-Variable ```DYLD_LIBRARY_PATH``` muss auf den Pfad zu OpenSSL verweisen. @@ -41,7 +39,7 @@ _Beispiele:_ ```sh export DYLD_LIBRARY_PATH=/usr/local/opt/openssl/lib -export DYLD_LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib +export DYLD_LIBRARY_PATH=/opt/homebrew/opt/openssl@3/lib ``` ## Weitere Informationen zu FIT-Connect finden Sie hier: @@ -67,7 +65,7 @@ Hierfür muss der Client die Environment auswählen oder einen eigenen Environme ## Credentials ClientId und ClientSecret sind die Grundlage, um einen Token vom OAuth-Server abfragen zu können. -Die beiden Werte sind im [Self-Service Portal der Testumgebung von FIT-Connect](https://portal.auth-testing.FIT-Connect.fitko.dev/clients) zu erstellen. +Die beiden Werte sind im [Self-Service-Portal der Testumgebung von FIT-Connect](https://portal.auth-testing.FIT-Connect.fitko.dev/clients) zu erstellen. # Sender @@ -82,11 +80,12 @@ Das folgende Beispiel zeigt, wie Sie das SDK in einem sendenden System nutzen, u ### Plain Submission Builder -Wenn Antragsdaten im Backend des Senders (Onlinedienst) unverschlüsselt vorliegen, können diese mit dem `SubmissionBuilder` in ein verschlüsseltes Submission-Objekt umgewandelt werden. +Wenn Antragsdaten im Backend des Senders (Onlinedienst) unverschlüsselt vorliegen, können diese mit dem `SubmissionBuilder` in ein verschlüsseltes Submission-Objekt umgewandelt werden. Diese Umsetzungsvariante bietet eine geringere Sicherheit als die Umsetzung der Ende-zu-Ende-Verschlüsselung ab dem Endgerät der Antragsteller:in. Zudem erhöhen sich in dieser Umsetzungsvariante die IT-Sicherheits- und Datenschutzanforderungen an den Betrieb des Onlinedienstes, da Antragsdaten in dieser Umsetzungsvariante im Klartext durch das Backend des Onlinedienstes verarbeitet werden. -Eine Alternative stellt die Verschlüsselung von Antragsdaten im Frontend des Onlinedientes dar (siehe nächster Abschnitt *Encrypted Submission Builder*). +Eine Alternative stellt die Verschlüsselung von Antragsdaten im Frontend des Onlinedientes dar (siehe nächster Abschnitt +*Encrypted Submission Builder*). Weitere Informationen finden sich im Artikel [Verschlüsselte Übertragung von Antragsdaten](https://docs.fitko.de/fit-connect/docs/getting-started/encryption/) der FIT-Connect-Dokumentation. @@ -146,8 +145,8 @@ var encryptedSubmission = EncryptedSubmissionBuilder await Client.GetSender(FitConnectEnvironment.Testing, clientId, clientSecret, logger).SendAsync(encryptedSubmission); ``` -| **Wichtig** | -| ----------- | +| **Wichtig** | +|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Bei der Übernahme der Destination-ID (`destinationId`) und des Leistungsschlüssels (`leikaKey`) aus dem Frontend MUSS im Backend geprüft werden, ob ein Versand von Einreichungen dieser Leistung an den angegebenen Zustellpunkt zulässig ist. | ## Details @@ -230,6 +229,25 @@ Der Parameter ```logger``` ist optional und muss das Interface ```Microsoft.Exte Das Abschicken der Submission erfolgt mit diesem Aufruf. +### Abrufen des Event-Logs und des Status' + +Der `sender` verfügt über eine Methode `GetEventLogAsync()` und `GetStatusForSubmissionAsync()` mit denen der aktuelle Status der Submission beim Zustelldienst abgefragt werden kann. + +```csharp +var (status, problems) = await sender.GetStatusForSubmission(sentSubmission); +``` + +Die SentSubmission enthält alle nottwendigen Informationen, um den Status abzufragen. +Auch die `AthenticationTags` sind darin enthalten mit denen der Status überprüft werden kann. + +Der `status` beinhaltet den aktuellen _Zustand_ der Submission. +In den `problems` sind Rückmeldungen vom _Subscriber_ enthalten. + +Das gesamte `EventLog` kann ebenfalls abgerufen werden. +Dazu wird die Methode `sender.GetEventLogAsync(caseId, destinationId)` aufgerufen. +Eine Überprüfung der Gültikeit des Statuses erfolgt hier **nicht**. + + # Subscriber Das folgende Beispiel zeigt, wie Sie das SDK in einem empfangenden System nutzen, um einen Subscriber zu erzeugen, der Einreichungen von sendenden Systemen erhält. @@ -341,7 +359,6 @@ Akzeptiert die Submission und löscht die Submission, die auf dem Server gespeic Weist die Submission zurück. - ## Beispiel ```csharp