Skip to content

Commit

Permalink
Merge pull request #17 from hbiarge/improve_claims_serialization
Browse files Browse the repository at this point in the history
Improve claims serialization
  • Loading branch information
hbiarge authored Jan 31, 2019
2 parents a00cc13 + 5f0f3e0 commit 19df523
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 22 deletions.
6 changes: 3 additions & 3 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<MicrosoftAspNetCoreTestHostVersion>2.2.0</MicrosoftAspNetCoreTestHostVersion>
<FluentAssertionsVersion>5.6.0</FluentAssertionsVersion>
<MicrosoftAspNetCoreAllVersion>2.2.0</MicrosoftAspNetCoreAllVersion>
<MicrosoftWebCodeGenerationVersion>2.2.0</MicrosoftWebCodeGenerationVersion>
<MicrosoftWebCodeGenerationToolsVersion>2.2.0</MicrosoftWebCodeGenerationToolsVersion>
<MicrosoftWebCodeGenerationVersion>2.2.1</MicrosoftWebCodeGenerationVersion>
<MicrosoftWebCodeGenerationToolsVersion>2.2.1</MicrosoftWebCodeGenerationToolsVersion>
</PropertyGroup>

<PropertyGroup Label="CLI Tools Versions">
Expand All @@ -27,7 +27,7 @@
<RepositoryUrl>http://github.com/xabaril/Acheve.TestHost</RepositoryUrl>
<Authors>Xabaril Contributors</Authors>
<Company>Xabaril</Company>
<Version>2.0.0</Version>
<Version>2.1.0</Version>
<Description>Achve.TestHost is a nuget package to improve TestServer experiences.
For more information see http://github.com/Xabaril/Acheve.TestHost</Description>
<Tags>TestHost;TestServer</Tags>
Expand Down
25 changes: 24 additions & 1 deletion samples/Sample.Api/ApiConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System.Security.Claims;

namespace Sample.Api
{
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;
Expand Down
2 changes: 1 addition & 1 deletion samples/Sample.Api/Controllers/ValuesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Sample.Api.Controllers
[ApiController]
public class ValuesController : ControllerBase
{
[Authorize]
[Authorize(Policy = "ValidateClaims")]
[HttpGet]
public IActionResult Values()
{
Expand Down
9 changes: 7 additions & 2 deletions samples/Sample.IntegrationTests/Identities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ public static class Identities
{
public static readonly IEnumerable<Claim> 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<Claim> Empty = new Claim[0];
Expand Down
6 changes: 2 additions & 4 deletions samples/Sample.IntegrationTests/Specs/ValuesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ValuesController>(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]
Expand Down
33 changes: 22 additions & 11 deletions src/Acheve.TestHost/Security/DefaultClaimsEncoder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Microsoft.AspNetCore.Authentication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
Expand All @@ -10,8 +11,15 @@ internal static class DefautClaimsEncoder
{
public static string Encode(IEnumerable<Claim> 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<Claim> Decode(string encodedValue)
Expand All @@ -21,14 +29,17 @@ public static IEnumerable<Claim> Decode(string encodedValue)
return Enumerable.Empty<Claim>();
}

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<Claim>();
}
}
}
}

0 comments on commit 19df523

Please sign in to comment.