From b9d59fb81f7327dddc02f2e41ef90cc15a0b2c21 Mon Sep 17 00:00:00 2001 From: Hugo Biarge Date: Thu, 31 Jan 2019 10:49:56 +0100 Subject: [PATCH 1/2] Improve claims serialization --- samples/Sample.Api/ApiConfiguration.cs | 25 +++++++++++++- .../Controllers/ValuesController.cs | 2 +- samples/Sample.IntegrationTests/Identities.cs | 9 +++-- .../Specs/ValuesTests.cs | 6 ++-- .../Security/DefaultClaimsEncoder.cs | 33 ++++++++++++------- 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/samples/Sample.Api/ApiConfiguration.cs b/samples/Sample.Api/ApiConfiguration.cs index 0223bca..f5344ce 100644 --- a/samples/Sample.Api/ApiConfiguration.cs +++ b/samples/Sample.Api/ApiConfiguration.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using System.Security.Claims; namespace Sample.Api { @@ -7,7 +8,29 @@ public static class ApiConfiguration { public static void ConfigureCoreMvc(IMvcCoreBuilder builder) { - builder.AddAuthorization(); + builder.AddAuthorization(options => + { + options.AddPolicy("ValidateClaims", policyBuilder => + { + policyBuilder.RequireAuthenticatedUser(); + policyBuilder.RequireAssertion(context => + { + var principal = context.User; + var nameIdentifierClaim = principal.FindFirst(ClaimTypes.NameIdentifier); + + if (nameIdentifierClaim == null + || nameIdentifierClaim.Value != "1" + || nameIdentifierClaim.ValueType != ClaimValueTypes.Integer32 + || nameIdentifierClaim.Issuer != "TestIssuer" + || nameIdentifierClaim.OriginalIssuer != "OriginalTestIssuer") + { + return false; + } + + return true; + }); + }); + }); builder.AddJsonFormatters(options => { options.NullValueHandling = NullValueHandling.Ignore; diff --git a/samples/Sample.Api/Controllers/ValuesController.cs b/samples/Sample.Api/Controllers/ValuesController.cs index 7ed82b1..511ccbc 100644 --- a/samples/Sample.Api/Controllers/ValuesController.cs +++ b/samples/Sample.Api/Controllers/ValuesController.cs @@ -7,7 +7,7 @@ namespace Sample.Api.Controllers [ApiController] public class ValuesController : ControllerBase { - [Authorize] + [Authorize(Policy = "ValidateClaims")] [HttpGet] public IActionResult Values() { diff --git a/samples/Sample.IntegrationTests/Identities.cs b/samples/Sample.IntegrationTests/Identities.cs index eefc53b..03eb85a 100644 --- a/samples/Sample.IntegrationTests/Identities.cs +++ b/samples/Sample.IntegrationTests/Identities.cs @@ -7,8 +7,13 @@ public static class Identities { public static readonly IEnumerable User = new[] { - new Claim(ClaimTypes.NameIdentifier, "1"), - new Claim(ClaimTypes.Name, "User"), + new Claim( + type: ClaimTypes.NameIdentifier, + value: "1", + valueType: ClaimValueTypes.Integer32, + issuer: "TestIssuer", + originalIssuer: "OriginalTestIssuer"), + new Claim(type: ClaimTypes.Name, value: "User"), }; public static readonly IEnumerable Empty = new Claim[0]; diff --git a/samples/Sample.IntegrationTests/Specs/ValuesTests.cs b/samples/Sample.IntegrationTests/Specs/ValuesTests.cs index 6843580..7686b00 100644 --- a/samples/Sample.IntegrationTests/Specs/ValuesTests.cs +++ b/samples/Sample.IntegrationTests/Specs/ValuesTests.cs @@ -31,15 +31,13 @@ public async Task Authorized_User_Should_Get_200() } [Fact] - public async Task User_With_No_Claims_Is_Unauthorized() + public async Task User_With_No_Claims_Is_Forbidden() { var response = await _fixture.Server.CreateHttpApiRequest(controller => controller.Values()) .WithIdentity(Identities.Empty) .GetAsync(); - response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); - response.Headers.WwwAuthenticate.Count.Should().Be(1); - response.Headers.WwwAuthenticate.First().Scheme.Should().Be("TestServer"); + response.StatusCode.Should().Be(HttpStatusCode.Forbidden); } [Fact] diff --git a/src/Acheve.TestHost/Security/DefaultClaimsEncoder.cs b/src/Acheve.TestHost/Security/DefaultClaimsEncoder.cs index 30ff8e0..d76db3a 100644 --- a/src/Acheve.TestHost/Security/DefaultClaimsEncoder.cs +++ b/src/Acheve.TestHost/Security/DefaultClaimsEncoder.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Authentication; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; @@ -10,8 +11,15 @@ internal static class DefautClaimsEncoder { public static string Encode(IEnumerable claims) { - var sourceString = string.Join("&", claims.Select(c => $"{c.Type}={c.Value}")); - return Convert.ToBase64String(Encoding.UTF8.GetBytes(sourceString)); + var ticket = new AuthenticationTicket( + principal: new ClaimsPrincipal( + new ClaimsIdentity(claims)), + authenticationScheme: "TestServer"); + + var serializer = new TicketSerializer(); + var bytes = serializer.Serialize(ticket); + + return Convert.ToBase64String(bytes); } public static IEnumerable Decode(string encodedValue) @@ -21,14 +29,17 @@ public static IEnumerable Decode(string encodedValue) return Enumerable.Empty(); } - var decodedString = Encoding.UTF8.GetString(Convert.FromBase64String(encodedValue)); - return decodedString.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries) - .DefaultIfEmpty() - .Select(x => - { - var values = x.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); - return new Claim(values[0], values[1]); - }); + var serializer = new TicketSerializer(); + try + { + var ticket = serializer.Deserialize(Convert.FromBase64String(encodedValue)); + + return ticket.Principal.Claims; + } + catch (Exception) + { + return Enumerable.Empty(); + } } } } From 5f0f3e0df0bbe77945d1d0f675639fed6c413701 Mon Sep 17 00:00:00 2001 From: Hugo Biarge Date: Thu, 31 Jan 2019 10:52:35 +0100 Subject: [PATCH 2/2] Update version --- build/dependencies.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/dependencies.props b/build/dependencies.props index 36d926a..8c02f26 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -13,8 +13,8 @@ 2.2.0 5.6.0 2.2.0 - 2.2.0 - 2.2.0 + 2.2.1 + 2.2.1 @@ -27,7 +27,7 @@ http://github.com/xabaril/Acheve.TestHost Xabaril Contributors Xabaril - 2.0.0 + 2.1.0 Achve.TestHost is a nuget package to improve TestServer experiences. For more information see http://github.com/Xabaril/Acheve.TestHost TestHost;TestServer