diff --git a/.idea/.idea.FitConnect/.idea/.name b/.idea/.idea.FitConnect/.idea/.name deleted file mode 100644 index 04b5af4f764eb04374cbc16e0e0def5774fddae9..0000000000000000000000000000000000000000 --- a/.idea/.idea.FitConnect/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -FitConnect \ No newline at end of file diff --git a/BasicUnitTest/BasicUnitTest.csproj b/BasicUnitTest/BasicUnitTest.csproj index a25229d9e9f5e8e59e9fc82b1b505b8f02f4e46a..057e18bb263762eb67d8a5b62554aac2515dbaee 100644 --- a/BasicUnitTest/BasicUnitTest.csproj +++ b/BasicUnitTest/BasicUnitTest.csproj @@ -5,23 +5,27 @@ <Nullable>enable</Nullable> <IsPackable>false</IsPackable> - - <RootNamespace>FluentApiTest</RootNamespace> </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.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" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\FitConnect\FitConnect.csproj" /> + <ProjectReference Include="..\MockContainer\MockContainer.csproj" /> </ItemGroup> <ItemGroup> - <ProjectReference Include="..\FitConnect\FitConnect.csproj"/> - <ProjectReference Include="..\MockContainer\MockContainer.csproj"/> + <None Update="events.txt"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> </Project> diff --git a/BasicUnitTest/JsonSchemaValidation.cs b/BasicUnitTest/JsonSchemaValidation.cs new file mode 100644 index 0000000000000000000000000000000000000000..390a14410a29d34612d23707f83ee2c2ee3cc511 --- /dev/null +++ b/BasicUnitTest/JsonSchemaValidation.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using FluentAssertions; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace BasicUnitTest; + +[TestFixture] +public class JsonSchemaValidation { + private List<string> _jsonData = null!; + private string _schemaJson = null!; + + [OneTimeSetUp] + public void OneTimeSetUp() { + var jsonItems = File.ReadAllLines("events.txt"); + _jsonData = jsonItems.Where(l => !l.StartsWith("#")) + .Select(l => Base64UrlEncoder.Decode(l.Split('.')[1])) + .ToList(); + + var schemaResponse = new HttpClient() + .GetAsync( + "https://schema.fitko.de/fit-connect/set-payload/1.0.0/set-payload.schema.json") + .Result; + + _schemaJson = schemaResponse.Content.ReadAsStringAsync().Result; + } + + [Test] + [Ignore("Package does not support this yet")] + public void NJson_ShouldPass() { + var schema = NJsonSchema.JsonSchema.FromJsonAsync(_schemaJson).Result; + _jsonData.ForEach(l => schema.Validate(l)); + } + + [Test] + public void JsonSchema_ShouldPass() { + _jsonData.ForEach(l => { + var jObject = JObject.Parse(l); + var schema = Newtonsoft.Json.Schema.JSchema.Parse(_schemaJson); + var result = jObject.IsValid(schema); + if (!result) { + Console.WriteLine(jObject.ToString()); + } + else Console.WriteLine("OK"); + + result.Should().BeTrue(); + }); + } + + [Test] + public void JsonSchema_ShouldFail() { + var schema = Newtonsoft.Json.Schema.JSchema.Parse(_schemaJson); + JObject.Parse( + "{\"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(); + } +} diff --git a/BasicUnitTest/SecurityEventTokenTests.cs b/BasicUnitTest/SecurityEventTokenTests.cs index 1763033e7d525e7cd0e67e59ae9406d339501bea..ddddef9579bd34c02d80b3efa899cc57c4aad5a1 100644 --- a/BasicUnitTest/SecurityEventTokenTests.cs +++ b/BasicUnitTest/SecurityEventTokenTests.cs @@ -9,7 +9,7 @@ using MockContainer; using NUnit.Framework; using SecurityEventToken = FitConnect.Models.SecurityEventToken; -namespace FluentApiTest; +namespace BasicUnitTest; [TestFixture] public class SecurityEventTokenTests { diff --git a/BasicUnitTest/SenderTests.cs b/BasicUnitTest/SenderTests.cs index 2d81e5a54d03f1dc76f48196969e7051c62f1e5b..ac5740fc719002b0b5c48af1a6dbb0c2fdff49fd 100644 --- a/BasicUnitTest/SenderTests.cs +++ b/BasicUnitTest/SenderTests.cs @@ -1,13 +1,14 @@ using System; using Autofac; using FitConnect; +using FitConnect.Encryption; using FitConnect.Models; using FluentAssertions; using Microsoft.Extensions.Logging; using MockContainer; using NUnit.Framework; -namespace FluentApiTest; +namespace BasicUnitTest; public class SenderTests { private IContainer _container = null!; @@ -18,11 +19,11 @@ public class SenderTests { [OneTimeSetUp] public void OneTimeSetup() { _container = Container.Create(); - logger = _container.Resolve<ILogger>(); } [SetUp] public void Setup() { + logger = _container.Resolve<ILogger>(); } [Test] @@ -72,10 +73,9 @@ public class SenderTests { var metadata = Sender.CreateMetadata(submission); // Assert - var errors = Subscriber.VerifyMetadata(metadata); - foreach (var error in errors) Console.WriteLine(error.ToString()); + var valid = JsonHelper.VerifyMetadata(metadata, logger); - errors.Count.Should().Be(0); + valid.Should().BeTrue(); } [Test] @@ -88,9 +88,7 @@ public class SenderTests { var metadata = Sender.CreateMetadata(submission); // Assert - var errors = Subscriber.VerifyMetadata(metadata); - foreach (var error in errors) Console.WriteLine(error.ToString()); - - errors.Count.Should().Be(1); + var valid = JsonHelper.VerifyMetadata(metadata, logger); + valid.Should().BeFalse(); } } diff --git a/BasicUnitTest/SubscriberReceiveTests.cs b/BasicUnitTest/SubscriberReceiveTests.cs index 0a33fbe52a09c0f9551676dc00b57a4a98aec495..36cf79d037bce410b25eb6858851a1a0efd6c989 100644 --- a/BasicUnitTest/SubscriberReceiveTests.cs +++ b/BasicUnitTest/SubscriberReceiveTests.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using MockContainer; using NUnit.Framework; -namespace FluentApiTest; +namespace BasicUnitTest; public class SubscriberReceiveTests { private IContainer _container = null!; diff --git a/BasicUnitTest/events.txt b/BasicUnitTest/events.txt new file mode 100644 index 0000000000000000000000000000000000000000..8758f9ea52932a99d3d5f8f7035880881c2c46b1 --- /dev/null +++ b/BasicUnitTest/events.txt @@ -0,0 +1,14 @@ +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk3LCJqdGkiOiJiN2U2ZjQwNi01ODE2LTRmNTAtYmU4MC00YzM2ODIwY2Q5MWEiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.Vh9wOX6EYP0NiXDGKusXKdKxaLbIM6wW_dQnYA3DQbl6v9SNPzx2i-6DBTwX-6VUZSChV12QErM6k5WziX1AFmLmCh9S22oKlU-q4ra1JBZxgbupyMoMztvgYTneiG3wgUarAQ9ocKclmvbXF-sP45hYeP1yBUYBwMq0O1rY5CZPY1BUpwA7J8MAx8KEw-Io0Fw46gO5KV1ppLPdUB24wT1pCjZuntP6_qlp-EAxJrdmOfHLncjnzUt3bGn-s1I5yNgVYcs9C4uV6_bGkFFJHMc18ZtpQ9dZTVcEIJrgPRThCoKKFGpjDoX1u1uXML-iklM3bydbqk3Xe1SV6HdOGqlVurHJ2yDUzOkih6tz8S2PvsUvbEGOunJAeQncdjyaaW8tEFXDnDbaQpd9WSamMm4zTRB062zqIhGXBzHubCmK2Ry_0o5zm-MId4YuYXF5AgXcCozdERjjmiYYcfspam_Ytsq_K84k11SSGDuEAWWvrRyH4srRgzBSw9vMNSmVtvNolfi-wfFux8BTcG4-QyBS342PH-UjTrrmOqpfpWW7hZDb_3U_b7yAxjGP4jMtpM86lv-rwKSn--2UkwHsLZkxfLfNXho-YtKjs3jazeCqxFbSxwwWf6TmPszVW6QsFQtr4hTpwB_taxpTtm4T9U7b3kl-lnUV0K-rMJQUWVA +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk5LCJqdGkiOiJjN2ZmMGQ5ZC0yY2FhLTQ4MWQtYjljZS1jNmQ0YTFjNmVlMDIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiI2aHZIUHBYU2dSZmVGQVVXSlJiNWVRIiwibWV0YWRhdGEiOiJUX2tWSEN3b01DcGc0MmMzQ2plWkFRIiwiYXR0YWNobWVudHMiOnsiMzNiMTc4OTAtMmE4Ni00N2E0LThmYjktOTJiZWZjNzgyZTEzIjoiRHhuZEE3dlpBcV9rYXJFRFJMMzEtUSJ9fX19fQ.q1uZRkb8WIBZUqZdEJlvuvW_XrKTock743dF3nrjwfG7EWZ1hyZylLAT4-d-TUJDeidLUAYmathMN8ZTzCUYhpYwBTZZK68qbmYHlzR7axxoQsBzeV1AVe6HfBU2LAebCBzEHc3Hk1H52p0A1s7JJ2jSoIlwDMi7ErVqrgedY53rntvkabhwbvK8x8Kb2c4yEFBBBoAF5TGFZcpP8oDN-vg8VuilZ0OwYMxTBr_SvmxUSvKDEhV_YuFP4uj13SfHBlpDAtxbI5BrdWGj6banQ6qari_zbbVmiM820SVMagDiGvJDctqiRBxNpLmxkAZ1CWUn26vmsq7ZyU26x2vhKbAWGYgVu5zXiVwG6KU7-XNfuQC8v7ZfqMv6Jg3qsAQSESiOcC5pHw_fAOhUkMGhdQadffmdVJMVSCAmWQAz6X3xvF39SfdtrcsBP3YlnZvpg76FVpIEtcBkfyPWnJbqF_31sRqorD4S9T4mG9R16oFcAlt_Zw-15fkrQ9k8JkzbtSFNxvy7CTO7ssHSHeBco35uQHKuUee7of3PDtsS-slyoraMlgQSZTjwnooaF2U4VMVEPr-mcQqHj_2kQ1yuxoNs7m47thqajjM4kQKCGksg4jtBIDoqIPKQGwjKkp-LGxNiAVNuyPqxef4FYODOuPKpWWuG9zhF2gJGBqA46nw +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk3LCJqdGkiOiJiN2U2ZjQwNi01ODE2LTRmNTAtYmU4MC00YzM2ODIwY2Q5MWEiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.Vh9wOX6EYP0NiXDGKusXKdKxaLbIM6wW_dQnYA3DQbl6v9SNPzx2i-6DBTwX-6VUZSChV12QErM6k5WziX1AFmLmCh9S22oKlU-q4ra1JBZxgbupyMoMztvgYTneiG3wgUarAQ9ocKclmvbXF-sP45hYeP1yBUYBwMq0O1rY5CZPY1BUpwA7J8MAx8KEw-Io0Fw46gO5KV1ppLPdUB24wT1pCjZuntP6_qlp-EAxJrdmOfHLncjnzUt3bGn-s1I5yNgVYcs9C4uV6_bGkFFJHMc18ZtpQ9dZTVcEIJrgPRThCoKKFGpjDoX1u1uXML-iklM3bydbqk3Xe1SV6HdOGqlVurHJ2yDUzOkih6tz8S2PvsUvbEGOunJAeQncdjyaaW8tEFXDnDbaQpd9WSamMm4zTRB062zqIhGXBzHubCmK2Ry_0o5zm-MId4YuYXF5AgXcCozdERjjmiYYcfspam_Ytsq_K84k11SSGDuEAWWvrRyH4srRgzBSw9vMNSmVtvNolfi-wfFux8BTcG4-QyBS342PH-UjTrrmOqpfpWW7hZDb_3U_b7yAxjGP4jMtpM86lv-rwKSn--2UkwHsLZkxfLfNXho-YtKjs3jazeCqxFbSxwwWf6TmPszVW6QsFQtr4hTpwB_taxpTtm4T9U7b3kl-lnUV0K-rMJQUWVA +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk5LCJqdGkiOiJjN2ZmMGQ5ZC0yY2FhLTQ4MWQtYjljZS1jNmQ0YTFjNmVlMDIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiI2aHZIUHBYU2dSZmVGQVVXSlJiNWVRIiwibWV0YWRhdGEiOiJUX2tWSEN3b01DcGc0MmMzQ2plWkFRIiwiYXR0YWNobWVudHMiOnsiMzNiMTc4OTAtMmE4Ni00N2E0LThmYjktOTJiZWZjNzgyZTEzIjoiRHhuZEE3dlpBcV9rYXJFRFJMMzEtUSJ9fX19fQ.q1uZRkb8WIBZUqZdEJlvuvW_XrKTock743dF3nrjwfG7EWZ1hyZylLAT4-d-TUJDeidLUAYmathMN8ZTzCUYhpYwBTZZK68qbmYHlzR7axxoQsBzeV1AVe6HfBU2LAebCBzEHc3Hk1H52p0A1s7JJ2jSoIlwDMi7ErVqrgedY53rntvkabhwbvK8x8Kb2c4yEFBBBoAF5TGFZcpP8oDN-vg8VuilZ0OwYMxTBr_SvmxUSvKDEhV_YuFP4uj13SfHBlpDAtxbI5BrdWGj6banQ6qari_zbbVmiM820SVMagDiGvJDctqiRBxNpLmxkAZ1CWUn26vmsq7ZyU26x2vhKbAWGYgVu5zXiVwG6KU7-XNfuQC8v7ZfqMv6Jg3qsAQSESiOcC5pHw_fAOhUkMGhdQadffmdVJMVSCAmWQAz6X3xvF39SfdtrcsBP3YlnZvpg76FVpIEtcBkfyPWnJbqF_31sRqorD4S9T4mG9R16oFcAlt_Zw-15fkrQ9k8JkzbtSFNxvy7CTO7ssHSHeBco35uQHKuUee7of3PDtsS-slyoraMlgQSZTjwnooaF2U4VMVEPr-mcQqHj_2kQ1yuxoNs7m47thqajjM4kQKCGksg4jtBIDoqIPKQGwjKkp-LGxNiAVNuyPqxef4FYODOuPKpWWuG9zhF2gJGBqA46nw +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk3LCJqdGkiOiJiN2U2ZjQwNi01ODE2LTRmNTAtYmU4MC00YzM2ODIwY2Q5MWEiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.Vh9wOX6EYP0NiXDGKusXKdKxaLbIM6wW_dQnYA3DQbl6v9SNPzx2i-6DBTwX-6VUZSChV12QErM6k5WziX1AFmLmCh9S22oKlU-q4ra1JBZxgbupyMoMztvgYTneiG3wgUarAQ9ocKclmvbXF-sP45hYeP1yBUYBwMq0O1rY5CZPY1BUpwA7J8MAx8KEw-Io0Fw46gO5KV1ppLPdUB24wT1pCjZuntP6_qlp-EAxJrdmOfHLncjnzUt3bGn-s1I5yNgVYcs9C4uV6_bGkFFJHMc18ZtpQ9dZTVcEIJrgPRThCoKKFGpjDoX1u1uXML-iklM3bydbqk3Xe1SV6HdOGqlVurHJ2yDUzOkih6tz8S2PvsUvbEGOunJAeQncdjyaaW8tEFXDnDbaQpd9WSamMm4zTRB062zqIhGXBzHubCmK2Ry_0o5zm-MId4YuYXF5AgXcCozdERjjmiYYcfspam_Ytsq_K84k11SSGDuEAWWvrRyH4srRgzBSw9vMNSmVtvNolfi-wfFux8BTcG4-QyBS342PH-UjTrrmOqpfpWW7hZDb_3U_b7yAxjGP4jMtpM86lv-rwKSn--2UkwHsLZkxfLfNXho-YtKjs3jazeCqxFbSxwwWf6TmPszVW6QsFQtr4hTpwB_taxpTtm4T9U7b3kl-lnUV0K-rMJQUWVA +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6NWVmNmQzYzctYTlkYi00YjIzLTg5YTYtNzVmNTAxMWJkNTdhIiwiaWF0IjoxNjYxMjI4Nzk5LCJqdGkiOiJjN2ZmMGQ5ZC0yY2FhLTQ4MWQtYjljZS1jNmQ0YTFjNmVlMDIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiI2aHZIUHBYU2dSZmVGQVVXSlJiNWVRIiwibWV0YWRhdGEiOiJUX2tWSEN3b01DcGc0MmMzQ2plWkFRIiwiYXR0YWNobWVudHMiOnsiMzNiMTc4OTAtMmE4Ni00N2E0LThmYjktOTJiZWZjNzgyZTEzIjoiRHhuZEE3dlpBcV9rYXJFRFJMMzEtUSJ9fX19fQ.q1uZRkb8WIBZUqZdEJlvuvW_XrKTock743dF3nrjwfG7EWZ1hyZylLAT4-d-TUJDeidLUAYmathMN8ZTzCUYhpYwBTZZK68qbmYHlzR7axxoQsBzeV1AVe6HfBU2LAebCBzEHc3Hk1H52p0A1s7JJ2jSoIlwDMi7ErVqrgedY53rntvkabhwbvK8x8Kb2c4yEFBBBoAF5TGFZcpP8oDN-vg8VuilZ0OwYMxTBr_SvmxUSvKDEhV_YuFP4uj13SfHBlpDAtxbI5BrdWGj6banQ6qari_zbbVmiM820SVMagDiGvJDctqiRBxNpLmxkAZ1CWUn26vmsq7ZyU26x2vhKbAWGYgVu5zXiVwG6KU7-XNfuQC8v7ZfqMv6Jg3qsAQSESiOcC5pHw_fAOhUkMGhdQadffmdVJMVSCAmWQAz6X3xvF39SfdtrcsBP3YlnZvpg76FVpIEtcBkfyPWnJbqF_31sRqorD4S9T4mG9R16oFcAlt_Zw-15fkrQ9k8JkzbtSFNxvy7CTO7ssHSHeBco35uQHKuUee7of3PDtsS-slyoraMlgQSZTjwnooaF2U4VMVEPr-mcQqHj_2kQ1yuxoNs7m47thqajjM4kQKCGksg4jtBIDoqIPKQGwjKkp-LGxNiAVNuyPqxef4FYODOuPKpWWuG9zhF2gJGBqA46nw +eyJhbGciOiJQUzUxMiIsImtpZCI6InRpMVowS2tHMXVBWHZkMlhuV2I1d1d1OHNsR3lsdnN2ejNuT1NlN3l1QWMiLCJ0eXAiOiJzZWNldmVudCtqd3QifQ.eyJzdWIiOiJzdWJtaXNzaW9uOjIyYTY5YzM0LTRjYTQtNDNmNy1hOTA5LWRjZmU3Zjk3MmMyNyIsImp0aSI6ImI1ZmQ5YzY3LTJmZGQtNGNmZS05MjY0LWFiNjE1YzlkZjQ2NCIsImlhdCI6MTY2MTIyODgwOCwiaXNzIjoiYWEzNzA0ZDYtOGJkNy00ZDQwLWE4YWYtNTAxODUxZjkzOTM0IiwiZXZlbnRzIjp7Imh0dHBzOi8vc2NoZW1hLmZpdGtvLmRlL2ZpdC1jb25uZWN0L2V2ZW50cy9yZWplY3Qtc3VibWlzc2lvbiI6eyJwcm9ibGVtcyI6W3siaW5zdGFuY2UiOiJtZXRhZGF0YSIsImRldGFpbCI6IkRlciBNZXRhZGF0ZW5zYXR6IGlzdCBuaWNodCBzY2hlbWEtdmFsaWRlLiIsInRpdGxlIjoiU2NoZW1hLUZlaGxlciIsInR5cGUiOiJodHRwczovL3NjaGVtYS5maXRrby5kZS9maXQtY29ubmVjdC9ldmVudHMvcHJvYmxlbXMvc2NoZW1hLXZpb2xhdGlvbiJ9XX19LCJ0eG4iOiJjYXNlOjVlZjZkM2M3LWE5ZGItNGIyMy04OWE2LTc1ZjUwMTFiZDU3YSIsIiRzY2hlbWEiOiJodHRwczovL3NjaGVtYS5maXRrby5kZS9maXQtY29ubmVjdC9zZXQtcGF5bG9hZC8xLjAuMC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiJ9.n_CYNTuSS72iNjqnjG_FPLUrw5yaUrAobYwHkjBrv8DtCfAKWwOre83jmptiQtn95Mo95GWF9A9RDJ0xHXlE3x3IBiHoM1gwpnTzQ2druXRTmFAS2yFbNOI6ky9_5X2DGI8AglWa4dXpV_ZkeFs4cRpWEPFCYScMLwvfOwJn02O0PhaVvN52tViFt8TFfPHHnuCtj0XpxJGoPzmTN-LJcXlWTQIfNovS-GFzXqtgHpFvrDeLtOMgJ2TvSvGWDWz2_TWeeb9ToJN78V9WhEdF0kp-o3wpj2wj-NcKxeZ__Xb_9gqbJUoYGapUgMhibE7Aq8v-q5DY9MYJNw9g8tFZ37Sb745x0gnkosbk5CvUhocq1xdgjbBXNjABLnns3DlWoeoFq2PJIrfz-Mjkyux4xbZD5YgOx8Wjb0y9u3IE8kpG5cFSEomTtJy5v7YiZtYj4tyJ3hW6wGA39BzGRD7ip0Se_bJe9Dj0HvbuPWA_mZ1TWqD7jw6SXgWGbxQcbyYAx2fj-QkLqSiKY4kNUXe9FUzBanjm2FoNBu_Gip5wSmPS22Eu2l6UjXWfVyTzGY9y4cw6CE9eillZx5025_ty_6xa5AnvFAtQgiHrAHREr5qoxuUVkH-LrGCqY-qneXnUL8fSms8YkTFsGZ43colhflDQ304ZhqXjk8g2V4eX2ik +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE2LCJqdGkiOiI5NjM5ZTM1OS0xNTY4LTQyOTMtOTgzZS03YWRhZTU3NTY2OGIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.AuzzS006nNp21wAzCZluH3lmLR4lucSa1xFYIxXXRk66XaqQArtn9tlduwCYO_PQCMHpsy1rtcDrJo1267J_GW1uGeDkXQ0vlpJ_Mu5zr09ppIKlMFJpEf9TuNpp3KTBVvbr-3Plb8I7KMee0RazLB2J23ia5kC6cH9xM3UVkuIWpIUyCoe9BcXQhnfemPLBduiaILACqdoHcAGTyAItFYB9nah4dt7pzCCy4aigIp4g5DnfHPPlDFk4cILuD9riv--Mn5njXF7no9wFtqzvyLqUijjbu3lhvE0m8fYd6I6SjIAj2gyE807Aeke9mSuCTVf4rPQvimQpVoWxx4fESpX8FUfpvyaU3suMNNGXfSjwcQTVe6F6fN-A-HFVYjjYiyERMQdznunfwLchECGTJYIPx5ZRjgz4wu1X6KgFBWSj-OwVjeozPahghS2srma3N0lrt_WSliR3Qp1Gzvf-2_ZjPYtYKzYuJki0dXSTKtfA0GevjObPqoG4DAqejFYkoElqrKk9eOUSSR-kjZfytnFGd2Q8QpLvuV9wGYHSXQLRqAHNTYg_4-0NsRNAi5563TJ-V5Adqjxps2euhERTD6IxVndkBt5W3i1oOxg1soojDtyYM2Hr3efSs7xBQKiBbBn46ILeyKtwm1GLF37wlXjxeSw8ULJTdZWVZ_CA4Yw +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE4LCJqdGkiOiI2NDA3OWVmZC1kMGQ2LTRkMGEtODY2MC01Y2UyY2Y3NjJhZTYiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiIzTFFXd0V6blZTY0JuZmV6QjdzTHZRIiwibWV0YWRhdGEiOiJ3TWJORW5iX1pWS25fMGpmd0hnYjlBIiwiYXR0YWNobWVudHMiOnsiODVjNmM3ZGUtYzhiMS00NTQ1LWEyOTgtNDlhYjFhNmYyOTczIjoiZHd3YmkwM0x2ejUzaUN2YVo5NDhKUSJ9fX19fQ.cKFTUJX54xS9aBrqilE14HfcoNn_IajwPDyovO429iaIukg2JmvywRWxjSnJe4bneW8pV4zqYKGNZseNmJH8xMwy2rZIl5J-BgCZzsFpjS3GWCMvO32w5KIHCqFEZzUZ25ou8Umx6k9LFDGHD-3xUG4cjcg4M2FL0pugxClPh8zdUf0c4USZinzyG6-NTNni8GLwXDHe3fF8YW-BYlXNV8D5r_nHLVyC_Z6WAU7cyOR4JImzugEFld6sO7j5mbfhzf7t8Py52pU2pKP-PQgPVLRZPNsbBDcGbxZBUMxgJCX79J5xonob_o5Bve1P_7ZmeL9u1p8kOjmRTIWo1XB6RhdXe-csGarQvisowjvZX3pDOsxodr9XDTY5mhQ9ueZJesKpRjc-oRa5k4vjtDpiO15kGcbdIkGIFDv6QWxBOcPTfn14eGvr3629tOzmo1XFumZz4KfJuoMktpiduo7o4A7WAE_XF9SiwvxsQAbWrwMA0JYeRxKq-EYOwjCUnvVq0VLALaQ6tPki6Ljb1nVOQL9-dfZj8dwc9l91fZDyA6MSrAEfTUtLLPhuIaXw9idvPuSceEFjQb-9KrMrw0IbnUluciVkwueXIz6aRLaPfVE1JCHVIcvjCpCeksScrT7AUqaKThGvxnd510q7vVZArRHpBvlV75vPLTsfgCc3KME +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE2LCJqdGkiOiI5NjM5ZTM1OS0xNTY4LTQyOTMtOTgzZS03YWRhZTU3NTY2OGIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.AuzzS006nNp21wAzCZluH3lmLR4lucSa1xFYIxXXRk66XaqQArtn9tlduwCYO_PQCMHpsy1rtcDrJo1267J_GW1uGeDkXQ0vlpJ_Mu5zr09ppIKlMFJpEf9TuNpp3KTBVvbr-3Plb8I7KMee0RazLB2J23ia5kC6cH9xM3UVkuIWpIUyCoe9BcXQhnfemPLBduiaILACqdoHcAGTyAItFYB9nah4dt7pzCCy4aigIp4g5DnfHPPlDFk4cILuD9riv--Mn5njXF7no9wFtqzvyLqUijjbu3lhvE0m8fYd6I6SjIAj2gyE807Aeke9mSuCTVf4rPQvimQpVoWxx4fESpX8FUfpvyaU3suMNNGXfSjwcQTVe6F6fN-A-HFVYjjYiyERMQdznunfwLchECGTJYIPx5ZRjgz4wu1X6KgFBWSj-OwVjeozPahghS2srma3N0lrt_WSliR3Qp1Gzvf-2_ZjPYtYKzYuJki0dXSTKtfA0GevjObPqoG4DAqejFYkoElqrKk9eOUSSR-kjZfytnFGd2Q8QpLvuV9wGYHSXQLRqAHNTYg_4-0NsRNAi5563TJ-V5Adqjxps2euhERTD6IxVndkBt5W3i1oOxg1soojDtyYM2Hr3efSs7xBQKiBbBn46ILeyKtwm1GLF37wlXjxeSw8ULJTdZWVZ_CA4Yw +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE4LCJqdGkiOiI2NDA3OWVmZC1kMGQ2LTRkMGEtODY2MC01Y2UyY2Y3NjJhZTYiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiIzTFFXd0V6blZTY0JuZmV6QjdzTHZRIiwibWV0YWRhdGEiOiJ3TWJORW5iX1pWS25fMGpmd0hnYjlBIiwiYXR0YWNobWVudHMiOnsiODVjNmM3ZGUtYzhiMS00NTQ1LWEyOTgtNDlhYjFhNmYyOTczIjoiZHd3YmkwM0x2ejUzaUN2YVo5NDhKUSJ9fX19fQ.cKFTUJX54xS9aBrqilE14HfcoNn_IajwPDyovO429iaIukg2JmvywRWxjSnJe4bneW8pV4zqYKGNZseNmJH8xMwy2rZIl5J-BgCZzsFpjS3GWCMvO32w5KIHCqFEZzUZ25ou8Umx6k9LFDGHD-3xUG4cjcg4M2FL0pugxClPh8zdUf0c4USZinzyG6-NTNni8GLwXDHe3fF8YW-BYlXNV8D5r_nHLVyC_Z6WAU7cyOR4JImzugEFld6sO7j5mbfhzf7t8Py52pU2pKP-PQgPVLRZPNsbBDcGbxZBUMxgJCX79J5xonob_o5Bve1P_7ZmeL9u1p8kOjmRTIWo1XB6RhdXe-csGarQvisowjvZX3pDOsxodr9XDTY5mhQ9ueZJesKpRjc-oRa5k4vjtDpiO15kGcbdIkGIFDv6QWxBOcPTfn14eGvr3629tOzmo1XFumZz4KfJuoMktpiduo7o4A7WAE_XF9SiwvxsQAbWrwMA0JYeRxKq-EYOwjCUnvVq0VLALaQ6tPki6Ljb1nVOQL9-dfZj8dwc9l91fZDyA6MSrAEfTUtLLPhuIaXw9idvPuSceEFjQb-9KrMrw0IbnUluciVkwueXIz6aRLaPfVE1JCHVIcvjCpCeksScrT7AUqaKThGvxnd510q7vVZArRHpBvlV75vPLTsfgCc3KME +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE2LCJqdGkiOiI5NjM5ZTM1OS0xNTY4LTQyOTMtOTgzZS03YWRhZTU3NTY2OGIiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvY3JlYXRlLXN1Ym1pc3Npb24iOnt9fX0.AuzzS006nNp21wAzCZluH3lmLR4lucSa1xFYIxXXRk66XaqQArtn9tlduwCYO_PQCMHpsy1rtcDrJo1267J_GW1uGeDkXQ0vlpJ_Mu5zr09ppIKlMFJpEf9TuNpp3KTBVvbr-3Plb8I7KMee0RazLB2J23ia5kC6cH9xM3UVkuIWpIUyCoe9BcXQhnfemPLBduiaILACqdoHcAGTyAItFYB9nah4dt7pzCCy4aigIp4g5DnfHPPlDFk4cILuD9riv--Mn5njXF7no9wFtqzvyLqUijjbu3lhvE0m8fYd6I6SjIAj2gyE807Aeke9mSuCTVf4rPQvimQpVoWxx4fESpX8FUfpvyaU3suMNNGXfSjwcQTVe6F6fN-A-HFVYjjYiyERMQdznunfwLchECGTJYIPx5ZRjgz4wu1X6KgFBWSj-OwVjeozPahghS2srma3N0lrt_WSliR3Qp1Gzvf-2_ZjPYtYKzYuJki0dXSTKtfA0GevjObPqoG4DAqejFYkoElqrKk9eOUSSR-kjZfytnFGd2Q8QpLvuV9wGYHSXQLRqAHNTYg_4-0NsRNAi5563TJ-V5Adqjxps2euhERTD6IxVndkBt5W3i1oOxg1soojDtyYM2Hr3efSs7xBQKiBbBn46ILeyKtwm1GLF37wlXjxeSw8ULJTdZWVZ_CA4Yw +eyJraWQiOiIzMjg1ODE0Ny1mMDkwLTQzYTktYjJmZC1kMjZhZTViNDFjMDMiLCJ0eXAiOiJzZWNldmVudCtqd3QiLCJhbGciOiJQUzUxMiJ9.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsIiRzY2hlbWEiOiJodHRwczpcL1wvc2NoZW1hLmZpdGtvLmRlXC9maXQtY29ubmVjdFwvc2V0LXBheWxvYWRcLzEuMC4wXC9zZXQtcGF5bG9hZC5zY2hlbWEuanNvbiIsImlzcyI6Imh0dHBzOlwvXC9zdWJtaXNzaW9uLWFwaS10ZXN0aW5nLmZpdC1jb25uZWN0LmZpdGtvLmRldiIsInR4biI6ImNhc2U6ZGE1OTA4YTktNjcxZi00ZGEyLWJkOTItZDJkN2I2N2UxOTRmIiwiaWF0IjoxNjYxMjI4ODE4LCJqdGkiOiI2NDA3OWVmZC1kMGQ2LTRkMGEtODY2MC01Y2UyY2Y3NjJhZTYiLCJldmVudHMiOnsiaHR0cHM6XC9cL3NjaGVtYS5maXRrby5kZVwvZml0LWNvbm5lY3RcL2V2ZW50c1wvc3VibWl0LXN1Ym1pc3Npb24iOnsiYXV0aGVudGljYXRpb25UYWdzIjp7ImRhdGEiOiIzTFFXd0V6blZTY0JuZmV6QjdzTHZRIiwibWV0YWRhdGEiOiJ3TWJORW5iX1pWS25fMGpmd0hnYjlBIiwiYXR0YWNobWVudHMiOnsiODVjNmM3ZGUtYzhiMS00NTQ1LWEyOTgtNDlhYjFhNmYyOTczIjoiZHd3YmkwM0x2ejUzaUN2YVo5NDhKUSJ9fX19fQ.cKFTUJX54xS9aBrqilE14HfcoNn_IajwPDyovO429iaIukg2JmvywRWxjSnJe4bneW8pV4zqYKGNZseNmJH8xMwy2rZIl5J-BgCZzsFpjS3GWCMvO32w5KIHCqFEZzUZ25ou8Umx6k9LFDGHD-3xUG4cjcg4M2FL0pugxClPh8zdUf0c4USZinzyG6-NTNni8GLwXDHe3fF8YW-BYlXNV8D5r_nHLVyC_Z6WAU7cyOR4JImzugEFld6sO7j5mbfhzf7t8Py52pU2pKP-PQgPVLRZPNsbBDcGbxZBUMxgJCX79J5xonob_o5Bve1P_7ZmeL9u1p8kOjmRTIWo1XB6RhdXe-csGarQvisowjvZX3pDOsxodr9XDTY5mhQ9ueZJesKpRjc-oRa5k4vjtDpiO15kGcbdIkGIFDv6QWxBOcPTfn14eGvr3629tOzmo1XFumZz4KfJuoMktpiduo7o4A7WAE_XF9SiwvxsQAbWrwMA0JYeRxKq-EYOwjCUnvVq0VLALaQ6tPki6Ljb1nVOQL9-dfZj8dwc9l91fZDyA6MSrAEfTUtLLPhuIaXw9idvPuSceEFjQb-9KrMrw0IbnUluciVkwueXIz6aRLaPfVE1JCHVIcvjCpCeksScrT7AUqaKThGvxnd510q7vVZArRHpBvlV75vPLTsfgCc3KME +# eyJhbGciOiJQUzUxMiIsImtpZCI6InRpMVowS2tHMXVBWHZkMlhuV2I1d1d1OHNsR3lsdnN2ejNuT1NlN3l1QWMiLCJ0eXAiOiJzZWNldmVudCtqd3QifQ.eyJzdWIiOiJzdWJtaXNzaW9uOjg3OTI0NDkzLTgwYWYtNDNjMC1iNWI2LWQ3N2EyZGY5NzVjYyIsImp0aSI6IjE5MmQzYTNhLThmZDMtNGI5Yy04OTcyLWQwNDgzYzAyNDBmMSIsImlhdCI6MTY2MTIyODgyNCwiaXNzIjoiYWEzNzA0ZDYtOGJkNy00ZDQwLWE4YWYtNTAxODUxZjkzOTM0IiwiZXZlbnRzIjp7Imh0dHBzOi8vc2NoZW1hLmZpdGtvLmRlL2ZpdC1jb25uZWN0L2V2ZW50cy9hY2NlcHQtc3VibWlzc2lvbiI6e319LCJ0eG4iOiJjYXNlOmRhNTkwOGE5LTY3MWYtNGRhMi1iZDkyLWQyZDdiNjdlMTk0ZiJ9.G_7hJVnXAVxbToNTnFvAdAqUdLKuzt6Vpnx5nEBjqQrV4sW3YOBOsDS8l68U5P_k1PkYhh8TYqSCq7o-NwnekqcwR4aT3bRvk5nlY_ygTyR7SJco2c60g74T3ysQZveHSGPI5G1ipKR5ywU5DDQnPoPO57vLar_3tRJqtMON_5CmRHHO2QZwvyQUP_ot-RlY7dmiHVp_jHbIiRvLNXFLGE_V_8XbdXBJXRc9rJ5ij4cq2Ry0d1Sd8Vg3HoTHgcIRpDlzb0_rUXZ4KYiata-JbU8cTkgmFC-C_bnPRq_sCj5LW5paudBa5YupTFYxOgWuLi_7qYPVbB9-_Id4MHvyCjMTpN_tym6pmA_b_i-ChZlJtGJzxxw2PMq5DwuPSddmHgRspBdw8cnREz0tWrKZ3EeWkaM_tPTTcaGyHDTIzh4hiahb4cg5IhOUDZPRIvVAMI6bP-CK6EOcPIzYB07pBPb1HjzRDzQMLx3FwPCsxafCsdQPP9Ssfd0a0g7lvspzmC1nZsEoE6XeVEq-qa5j3SM1CmfKG1gCVqWd6gZ2WIXtNYJ0x9EPSZ07q9PdaDrHF7SzV2rdw5PaMcgrfMXOxzQf_IrPsyx5S20lFeEVDxelI-k4ZMAFaXJKQRHWxNH8zl96syVv7pN9E8hru9E8By87B2gZrKL5eCugtQDmSfU diff --git a/FitConnect/Encryption/JsonHelper.cs b/FitConnect/Encryption/JsonHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..2ea57d1e7ea879e5c1b1d0938b3bde20f3c1c73b --- /dev/null +++ b/FitConnect/Encryption/JsonHelper.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; + +namespace FitConnect.Encryption; + +public static class JsonHelper { + public static bool ValidateJsonSchema(string schema, string json, ILogger? logger = null) { + var jSchema = Newtonsoft.Json.Schema.JSchema.Parse(schema); + return JObject.Parse(json).IsValid(jSchema); + } + + /// <summary> + /// Verify the metadata hash and content to fit the schema + /// </summary> + /// <param name="metadataString"></param> + /// <param name="logger">Optional logger, to print errors to logger</param> + /// <returns></returns> + public static bool VerifyMetadata(string metadataString, ILogger? logger = null) { + var schemaString = LoadContentOfResource("metadata.schema.json"); + var schema = NJsonSchema.JsonSchema.FromJsonAsync(schemaString).Result; + var result = schema.Validate(metadataString).ToList(); + logger?.LogWarning("Validation failed with {Errors}", + result.Aggregate("", (a, b) => a + "\n\t" + b)); + return result.Count == 0; + } + + + private static string LoadContentOfResource(string resourceName) { + var assembly = Assembly.GetExecutingAssembly(); + var fullQualifiedName = $"{assembly.GetName().Name}.{resourceName}"; + var resourceStream = assembly.GetManifestResourceStream(fullQualifiedName); + if (resourceStream == null) + throw new Exception($"Resource {fullQualifiedName} not found"); + var reader = new StreamReader(resourceStream); + return reader.ReadToEnd(); + } +} diff --git a/FitConnect/Sender.cs b/FitConnect/Sender.cs index a2e75be3246f191a0445d88a8d1ebc1fc626c324..6841c6abe34ca33bd6e932f0e304b94c143d3324 100644 --- a/FitConnect/Sender.cs +++ b/FitConnect/Sender.cs @@ -86,10 +86,8 @@ public class Sender : FitConnectClient, ISender, ISenderWithDestination, var metadata = CreateMetadata(Submission); Logger?.LogTrace("MetaData: {Metadata}", metadata); - var validationErrors = Subscriber.VerifyMetadata(metadata); - if (validationErrors.Count != 0) { - validationErrors.ToList() - .ForEach(v => Logger?.LogError("Validation error: {Error}", v)); + var valid = JsonHelper.VerifyMetadata(metadata); + if (!valid) { Logger?.LogError("Sending submission aborted due to validation errors"); throw new InvalidOperationException("Submission is not ready"); } diff --git a/FitConnect/Services/SubmissionService.cs b/FitConnect/Services/SubmissionService.cs index 76a7497fd5212c68e0e463c3c64411e2b1047fa8..e8bd70073f58a664f5d554eefd48e6486cf88e53 100644 --- a/FitConnect/Services/SubmissionService.cs +++ b/FitConnect/Services/SubmissionService.cs @@ -190,28 +190,23 @@ internal class SubmissionService : RestCallService, ISubmissionService { try { var schemaRaw = await RestCallForString(new Uri(schema), HttpMethod.Get); + var schemaOk = JsonHelper.ValidateJsonSchema(schemaRaw, eventContent); - // @formatter:off - var _schemaRaw = schemaRaw - // .Replace("https://json-schema.org/draft/2020-12/schema", "https://json-schema.org/draft-07/schema") - .Replace("%3A", ":") - .Replace("~1", "/"); - // @formatter:on - - - var schemaData = await JsonSchema.FromUrlAsync(schema); - - var schemaOk = schemaData.Validate(eventContent); - if (schemaOk.Count == 0) continue; if (mandatory) valid = false; - foreach (var validationError in schemaOk) + + if (!schemaOk) { + _logger?.Log(mandatory ? LogLevel.Error : LogLevel.Warning, + "Error on validating JSON Schema"); + if (mandatory) - _logger?.Log(mandatory ? LogLevel.Error : LogLevel.Warning, - "Error on validating JSON Schema {Error}", - validationError.ToString()); + throw new ArgumentException("Invalid JSON Schema for SET event"); + } + else { + _logger?.LogDebug("SET event is valid"); + } } catch (Exception e) { _logger?.LogError(e, "Error validating event against schema {Exception}", e); diff --git a/FitConnect/Subscriber.cs b/FitConnect/Subscriber.cs index c02b90a36f1f66cdb9ac3510d00517b64b09f2a0..7d08e9d4d306bfe4a441362d12e13f91e9779c8b 100644 --- a/FitConnect/Subscriber.cs +++ b/FitConnect/Subscriber.cs @@ -83,12 +83,11 @@ public class Subscriber : FitConnectClient, var (metaDataString, _, metaHash) = Encryption.Decrypt(submission.EncryptedMetadata!); if (!skipSchemaTest) { - var errors = VerifyMetadata(metaDataString); - if (errors.Count > 0) { + var valid = JsonHelper.VerifyMetadata(metaDataString); + if (!valid) { Logger?.LogWarning("Invalid metadata: {MetaData}", metaDataString); - foreach (var error in errors) Logger?.LogError("Error: {Error}", error.ToString()); - throw new Exception($"Metadata validation failed: {string.Join(", ", errors)}"); + throw new Exception($"Metadata validation failed"); } } @@ -155,27 +154,6 @@ public class Subscriber : FitConnectClient, } - /// <summary> - /// Verify the metadata hash and content to fit the schema - /// </summary> - /// <param name="metadataString"></param> - /// <returns></returns> - public static ICollection<ValidationError> VerifyMetadata(string metadataString) { - var schemaString = LoadContentOfResource("metadata.schema.json"); - var schema = JsonSchema.FromJsonAsync(schemaString).Result; - return schema.Validate(metadataString); - } - - private static string LoadContentOfResource(string resourceName) { - var assembly = Assembly.GetExecutingAssembly(); - var fullQualifiedName = $"{assembly.GetName().Name}.{resourceName}"; - var resourceStream = assembly.GetManifestResourceStream(fullQualifiedName); - if (resourceStream == null) - throw new Exception($"Resource {fullQualifiedName} not found"); - var reader = new StreamReader(resourceStream); - return reader.ReadToEnd(); - } - private void CompleteSubmission(SubmissionForPickupDto submission, FinishSubmissionStatus status, Problems[]? problems = null) { diff --git a/IntegrationTests/IntegrationTests.csproj b/IntegrationTests/IntegrationTests.csproj index 259858453f5f2c01120d1e8004298d1fa9c7aa7b..03b4ca3e126b1295f920a3e256c2a8224df1816d 100644 --- a/IntegrationTests/IntegrationTests.csproj +++ b/IntegrationTests/IntegrationTests.csproj @@ -8,21 +8,22 @@ </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="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.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" /> </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/Subscriber/SubscriberTestUnHappyPath.cs b/IntegrationTests/Subscriber/SubscriberTestUnHappyPath.cs index 5e6bd68e2e1b78d1426c3ca070bccdc8a8da26ad..20c6ba8b62f990d96a0c70dccb6b607cff0a94e7 100644 --- a/IntegrationTests/Subscriber/SubscriberTestUnHappyPath.cs +++ b/IntegrationTests/Subscriber/SubscriberTestUnHappyPath.cs @@ -1,4 +1,5 @@ using System.Linq; +using FitConnect.Encryption; using FluentAssertions; using Microsoft.Extensions.Logging; using Newtonsoft.Json; @@ -14,11 +15,10 @@ public class SubscriberTestUnHappyPath : SubscriberTestBase { var wrongData = "{\"name\":\"value\"}"; // Act - var validationErrors = FitConnect.Subscriber.VerifyMetadata(wrongData); - validationErrors.ToList().ForEach(v => Logger.LogWarning("ERROR: {V}", v.ToString())); + var valid = JsonHelper.VerifyMetadata(wrongData); // Assert - validationErrors.Count.Should().BeGreaterThan(0); + valid.Should().BeFalse(); } [Test] @@ -28,8 +28,7 @@ public class SubscriberTestUnHappyPath : SubscriberTestBase { // Act && Assert Assert.Throws<JsonReaderException>(() => { - var validationErrors = FitConnect.Subscriber.VerifyMetadata(wrongData); - validationErrors.ToList().ForEach(v => Logger.LogWarning("ERROR: {V}", v.ToString())); + var valid = JsonHelper.VerifyMetadata(wrongData, Logger); }); } } diff --git a/MockContainer/MockContainer.cs b/MockContainer/MockContainer.cs index 41927b145caf834014f6f0918854547fbbd38fa0..364259bc889d4e7690cd01a555011d7c58fe40dc 100644 --- a/MockContainer/MockContainer.cs +++ b/MockContainer/MockContainer.cs @@ -40,7 +40,7 @@ public static class Container { builder.Register(c => Mock.Of<ICasesService>()).As<ICasesService>(); builder.Register(c => LoggerFactory.Create( b => { - b.AddSimpleConsole(); + b.AddConsole(); b.SetMinimumLevel(LogLevel.Information); }).CreateLogger("FluentSenderTests") ).As<ILogger>();