aspnetcore/test/Microsoft.AspNetCore.Author.../AuthorizationMiddlewareTest...

459 lines
18 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Authorization.Test
{
public class AuthorizationMiddlewareTests
{
[Fact]
public async Task NoEndpoint_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task NoEndpointWithRequired_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
}
[Fact]
public async Task HasEndpointWithoutAuth_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint());
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task HasEndpointWithRequiredWithoutAuth_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint());
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
}
[Fact]
public async Task HasEndpointWithAuth_AnonymousUser_Challenges()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.True(authenticationService.ChallengeCalled);
}
[Fact]
public async Task HasEndpointWithAuth_AnonymousUser_ChallengePerScheme()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().AddAuthenticationSchemes("schema1", "schema2").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.Equal(2, authenticationService.ChallengeCount);
}
[Fact]
public async Task OnAuthorizationAsync_WillCallPolicyProvider()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
var getPolicyCount = 0;
var getRequiredPolicyCount = 0;
policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny<string>())).ReturnsAsync(policy)
.Callback(() => getPolicyCount++);
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy)
.Callback(() => getRequiredPolicyCount++);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever")));
// Act & Assert
await middleware.Invoke(context);
Assert.Equal(1, getPolicyCount);
Assert.Equal(1, getRequiredPolicyCount);
Assert.Equal(1, next.CalledCount);
await middleware.Invoke(context);
Assert.Equal(2, getPolicyCount);
Assert.Equal(2, getRequiredPolicyCount);
Assert.Equal(2, next.CalledCount);
await middleware.Invoke(context);
Assert.Equal(3, getPolicyCount);
Assert.Equal(3, getRequiredPolicyCount);
Assert.Equal(3, next.CalledCount);
}
[Fact]
public async Task Invoke_ValidClaimShouldNotFail()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewPage").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()));
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task HasEndpointWithAuthAndAllowAnonymous_AnonymousUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute(), new AllowAnonymousAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
Assert.False(authenticationService.ChallengeCalled);
}
[Fact]
public async Task HasEndpointWithAuth_AuthenticatedUser_Allows()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
Assert.False(authenticationService.ChallengeCalled);
}
[Fact]
public async Task Invoke_AuthSchemesFailShouldSetEmptyPrincipalOnContext()
{
// Arrange
var policy = new AuthorizationPolicyBuilder("Fails").RequireAuthenticatedUser().Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.NotNull(context.User?.Identity);
Assert.True(authenticationService.AuthenticateCalled);
Assert.True(authenticationService.ChallengeCalled);
}
[Fact]
public async Task Invoke_SingleValidClaimShouldSucceed()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewComment", "CanViewPage").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()));
// Act
await middleware.Invoke(context);
// Assert
Assert.True(next.Called);
}
[Fact]
public async Task AuthZResourceShouldBeEndpoint()
{
// Arrange
object resource = null;
var policy = new AuthorizationPolicyBuilder().RequireAssertion(c =>
{
resource = c.Resource;
return true;
}).Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var endpoint = CreateEndpoint(new AuthorizeAttribute());
var context = GetHttpContext(endpoint: endpoint);
// Act
await middleware.Invoke(context);
// Assert
Assert.Equal(endpoint, resource);
}
[Fact]
public async Task Invoke_RequireUnknownRoleShouldForbid()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.False(authenticationService.ChallengeCalled);
Assert.True(authenticationService.ForbidCalled);
}
[Fact]
public async Task Invoke_RequireUnknownRole_ForbidPerScheme()
{
// Arrange
var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").AddAuthenticationSchemes("Basic", "Bearer").Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.Equal(2, authenticationService.ForbidCount);
}
[Fact]
public async Task Invoke_InvalidClaimShouldForbid()
{
// Arrange
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("Permission", "CanViewComment")
.Build();
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
var next = new TestRequestDelegate();
var authenticationService = new TestAuthenticationService();
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService);
// Act
await middleware.Invoke(context);
// Assert
Assert.False(next.Called);
Assert.False(authenticationService.ChallengeCalled);
Assert.True(authenticationService.ForbidCalled);
}
private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null)
{
requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask);
return new AuthorizationMiddleware(requestDelegate, policyProvider);
}
private Endpoint CreateEndpoint(params object[] metadata)
{
return new Endpoint(context => Task.CompletedTask, new EndpointMetadataCollection(metadata), "Test endpoint");
}
private HttpContext GetHttpContext(
bool anonymous = false,
Action<IServiceCollection> registerServices = null,
Endpoint endpoint = null,
IAuthenticationService authenticationService = null)
{
var basicPrincipal = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CanViewPage"),
new Claim(ClaimTypes.Role, "Administrator"),
new Claim(ClaimTypes.Role, "User"),
new Claim(ClaimTypes.NameIdentifier, "John")},
"Basic"));
var validUser = basicPrincipal;
var bearerIdentity = new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CupBearer"),
new Claim(ClaimTypes.Role, "Token"),
new Claim(ClaimTypes.NameIdentifier, "John Bear")},
"Bearer");
validUser.AddIdentity(bearerIdentity);
// ServiceProvider
var serviceCollection = new ServiceCollection();
authenticationService = authenticationService ?? Mock.Of<IAuthenticationService>();
serviceCollection.AddSingleton(authenticationService);
serviceCollection.AddOptions();
serviceCollection.AddLogging();
serviceCollection.AddAuthorization();
serviceCollection.AddAuthorizationPolicyEvaluator();
registerServices?.Invoke(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
//// HttpContext
var httpContext = new DefaultHttpContext();
if (endpoint != null)
{
httpContext.SetEndpoint(endpoint);
}
httpContext.RequestServices = serviceProvider;
if (!anonymous)
{
httpContext.User = validUser;
}
return httpContext;
}
private class TestRequestDelegate
{
private readonly int _statusCode;
public bool Called => CalledCount > 0;
public int CalledCount { get; private set; }
public TestRequestDelegate(int statusCode = 200)
{
_statusCode = statusCode;
}
public Task Invoke(HttpContext context)
{
CalledCount++;
context.Response.StatusCode = _statusCode;
return Task.CompletedTask;
}
}
}
}