5

I've got a policy that I want to test in C#

public class WorkflowCreatePolicy
{
    public AuthorizationPolicy AuthorizationPolicy =>
        new AuthorizationPolicyBuilder()
            .RequireClaim("scope", "WorkflowAdmin")
            .Build();
}

Does anyone know of a way to test the AuthorizationPolicy to confirm that the scope "WorkflowAdmin" is successful and all others aren't?

This is what I see when I inspect the object:

enter image description here

I've managed to find this website: Authorization Handler Unit Tests but its talking about testing handlers and has code that marks the auth attempt as successful.

i'm not sure if this is getting close or not. It currently doesn't pass

[Test]
public void GivenPolicyName_WhenICallPolicyChecks_ThenItPasses()
{
    ClaimsPrincipal user = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim(CustomClaims.Scope, "WorkflowAdmin") }));

    WorkflowCreatePolicy workflowCreatePolicy = new WorkflowCreatePolicy();

    AuthorizationHandlerContext authorizationHandlerContext = new AuthorizationHandlerContext(workflowCreatePolicy.AuthorizationPolicy.Requirements, user, null);

    Assert.That(authorizationHandlerContext.HasSucceeded, Is.EqualTo(true));
}
chris31389
  • 8,414
  • 7
  • 55
  • 66

4 Answers4

7

See this test in the ASP.NET Core Security Unit Tests. I've taken the pattern from it and applied it to your policy.

[Fact]
public async Task ShouldAllowIfScopeClaimWorkflowAdminIsPresent()
{
    // Arrange
    var authorizationService = BuildAuthorizationService(services =>
    {
        services.AddAuthorization(options =>
        {
            options.AddPolicy("SomePolicyName", new WorkflowCreatePolicy()
               .AuthorizationPolicy);
        });
    });
    var user = new ClaimsPrincipal(new ClaimsIdentity(
        new Claim[] { new Claim("scope", "WorkflowAdmin") }));

    // Act
    var allowed = await authorizationService.AuthorizeAsync(user, "SomePolicyName");

    // Assert
    Assert.True(allowed.Succeeded);
}
private IAuthorizationService BuildAuthorizationService(
    Action<IServiceCollection> setupServices = null)
{
    var services = new ServiceCollection();
    services.AddAuthorization();
    services.AddLogging();
    services.AddOptions();
    setupServices?.Invoke(services);
    return services.BuildServiceProvider().GetRequiredService<IAuthorizationService>();
}
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
3

Under the hood an AuthorizationPolicy is just a collection of authorization handlers. Methods like RequireClaim add handlers build by Microsoft to the collection. In this case the ClaimsAuthorizationRequirement which inherits from AuthorizationHandler.

To validate if a user passes an AuthorizationPolicy you need an AuthorizationService which will call all policies. The DefaultAuthorizationService will stop after the first handler has failed to authenticate the user. If you do not register another AuthorizationService this one will be used.

So you could build the AuthorizationService by yourself and call the AuthorizeAsync method on it. Just be aware that you need to register your custom AuthorizationHandler's too if you want to test against them.

private static async Task<bool> CanAuthorizeUserWithPolicyAsync(ClaimsPrincipal user, AuthorizationPolicy policy)
{
    var handlers = policy.Requirements.Select(x => x as IAuthorizationHandler).ToArray();
    // add your custom authorization handlers here to the `handlers` collection

    var authorizationOptions = Options.Create(new AuthorizationOptions());

    authorizationOptions.Value.AddPolicy(nameof(policy), policy);

    var policyProvider = new DefaultAuthorizationPolicyProvider(authorizationOptions);
    var handlerProvider = new DefaultAuthorizationHandlerProvider(handlers);
    var contextFactory = new DefaultAuthorizationHandlerContextFactory();

    var authorizationService = new DefaultAuthorizationService(
        policyProvider, 
        handlerProvider, 
        new NullLogger<DefaultAuthorizationService>(), 
        contextFactory, 
        new DefaultAuthorizationEvaluator(), 
        authorizationOptions);

    var result = await authorizationService.AuthorizeAsync(user, policy);
    return result.Succeeded;
}

You can use this method like the following.

var user = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> { new Claim("scope", "WorkflowAdmin") }));

var policy = new AuthorizationPolicyBuilder()
    .RequireClaim("scope", "WorkflowAdmin")
    .Build();

Assert.That(await CanAuthorizeUserWithPolicyAsync(user, policy), Is.EqualTo(true));
NtFreX
  • 10,379
  • 2
  • 43
  • 63
0

Found it :)

[TestFixture]
public class WorkflowCreatePolicyTests
{
    [Test]
    public void GivenAuthorizationPolicy_WhenICheckTheClaimScopes_ThenItHasUserAdmin()
    {
        AuthorizationPolicy authorizationPolicy = new WorkflowCreatePolicy().AuthorizationPolicy;
        ClaimsAuthorizationRequirement claimsAuthorizationRequirement = authorizationPolicy.Requirements
            .FirstOrDefault(x => (x as ClaimsAuthorizationRequirement)?.ClaimType == "scope")
            as ClaimsAuthorizationRequirement;

        Assert.That(claimsAuthorizationRequirement?.AllowedValues, Contains.Item("WorkflowAdmin"));
    }
}
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
chris31389
  • 8,414
  • 7
  • 55
  • 66
-2

IMHO that's something you would want to test with integration tests.

Unit testing it is covered by the ASP.NET Core and at the end of the day you want to make sure that access to your end-points are protected as you expect.

That you can do with TestServer.

It's the composition of the app that shall be tested at that point: Making sure that calling your end-points without the desired claim will result in a 403.

Bruno Garcia
  • 6,029
  • 3
  • 25
  • 38
  • 2
    Don't you think it is important to unit test the security policies? Why wouldn't I want unit tests of my custom handlers (and maybe requirements)? – spottedmahn Apr 19 '18 at 18:38