Is it possible to write a test that can test an Au

2019-07-18 20:23发布

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

4条回答
The star\"
2楼-- · 2019-07-18 20:47

See this test in the ASP.NET Core Security Unit Tests. I've taken the pattern from it and applied it 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>();
}
查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-07-18 20:54

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"));
    }
}
查看更多
Viruses.
4楼-- · 2019-07-18 21:03

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));
查看更多
The star\"
5楼-- · 2019-07-18 21:07

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.

查看更多
登录 后发表回答