Only create filters for models that need them (#5044)
This commit is contained in:
parent
7a3f24d49d
commit
27a641f4bf
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
|
|
@ -36,7 +35,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
foreach (var controllerModel in context.Result.Controllers)
|
||||
{
|
||||
controllerModel.Filters.Add(new AuthorizeFilter(_policyProvider, controllerModel.Attributes.OfType<IAuthorizeData>()));
|
||||
var controllerModelAuthData = controllerModel.Attributes.OfType<IAuthorizeData>().ToArray();
|
||||
if (controllerModelAuthData.Length > 0)
|
||||
{
|
||||
controllerModel.Filters.Add(GetFilter(controllerModelAuthData));
|
||||
}
|
||||
foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>())
|
||||
{
|
||||
controllerModel.Filters.Add(new AllowAnonymousFilter());
|
||||
|
|
@ -44,7 +47,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
foreach (var actionModel in controllerModel.Actions)
|
||||
{
|
||||
actionModel.Filters.Add(new AuthorizeFilter(_policyProvider, actionModel.Attributes.OfType<IAuthorizeData>()));
|
||||
var actionModelAuthData = actionModel.Attributes.OfType<IAuthorizeData>().ToArray();
|
||||
if (actionModelAuthData.Length > 0)
|
||||
{
|
||||
actionModel.Filters.Add(GetFilter(actionModelAuthData));
|
||||
}
|
||||
|
||||
foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>())
|
||||
{
|
||||
actionModel.Filters.Add(new AllowAnonymousFilter());
|
||||
|
|
@ -52,5 +60,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AuthorizeFilter GetFilter(IEnumerable<IAuthorizeData> authData)
|
||||
{
|
||||
// The default policy provider will make the same policy for given input, so make it only once.
|
||||
// This will always execute syncronously.
|
||||
if (_policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider))
|
||||
{
|
||||
var policy = AuthorizationPolicy.CombineAsync(_policyProvider, authData).GetAwaiter().GetResult();
|
||||
return new AuthorizeFilter(policy);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AuthorizeFilter(_policyProvider, authData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
|
|||
var authorizeFilter = new AuthorizeFilter(policyProvider.Object, new AuthorizeAttribute[] { new AuthorizeAttribute("whatever") });
|
||||
var authorizationContext = GetAuthorizationContext(services => services.AddAuthorization());
|
||||
|
||||
// Act
|
||||
// Act & Assert
|
||||
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
|
||||
Assert.Equal(1, getPolicyCount);
|
||||
Assert.Null(authorizationContext.Result);
|
||||
|
|
@ -64,6 +64,9 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
|
|||
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
|
||||
Assert.Equal(3, getPolicyCount);
|
||||
Assert.Null(authorizationContext.Result);
|
||||
|
||||
// Make sure we don't cache the policy
|
||||
Assert.Null(authorizeFilter.Policy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -98,7 +101,6 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
|
|||
public async Task Invoke_EmptyClaimsShouldRejectAnonymousUser()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationOptions = new AuthorizationOptions();
|
||||
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
|
||||
var authorizationContext = GetAuthorizationContext(services =>
|
||||
services.AddAuthorization(),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
|
|
@ -57,8 +61,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var authorizeFilters = action.Filters.OfType<AuthorizeFilter>();
|
||||
Assert.Single(authorizeFilters);
|
||||
|
||||
Assert.NotNull(authorizeFilters.First().PolicyProvider);
|
||||
Assert.Equal(2, authorizeFilters.First().AuthorizeData.Count()); // Base + Derived authorize
|
||||
Assert.NotNull(authorizeFilters.First().Policy);
|
||||
Assert.Equal(3, authorizeFilters.First().Policy.Requirements.Count()); // Basic + Basic2 + Derived authorize
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -81,6 +85,103 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Assert.Single(action.Filters, f => f is AllowAnonymousFilter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DefaultPolicyProvider_NoAuthorizationData_NoFilterCreated()
|
||||
{
|
||||
// Arrange
|
||||
var requirements = new IAuthorizationRequirement[]
|
||||
{
|
||||
new AssertionRequirement((con) => { return true; })
|
||||
};
|
||||
var authorizationPolicy = new AuthorizationPolicy(requirements, new string[] { "dingos" });
|
||||
var authOptions = new TestOptionsManager<AuthorizationOptions>();
|
||||
authOptions.Value.AddPolicy("Base", authorizationPolicy);
|
||||
var policyProvider = new DefaultAuthorizationPolicyProvider(authOptions);
|
||||
|
||||
var provider = new AuthorizationApplicationModelProvider(policyProvider);
|
||||
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
|
||||
|
||||
// Act
|
||||
var action = GetBaseControllerActionModel(provider, defaultProvider);
|
||||
|
||||
// Assert
|
||||
var authorizationFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters));
|
||||
Assert.NotNull(authorizationFilter.Policy);
|
||||
Assert.Null(authorizationFilter.AuthorizeData);
|
||||
Assert.Null(authorizationFilter.PolicyProvider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_NonDefaultPolicyProvider_HasNoPolicy_HasPolicyProviderAndAuthorizeData()
|
||||
{
|
||||
// Arrange
|
||||
var requirements = new IAuthorizationRequirement[]
|
||||
{
|
||||
new AssertionRequirement((con) => { return true; })
|
||||
};
|
||||
var authorizationPolicy = new AuthorizationPolicy(requirements, new string[] { "dingos" });
|
||||
var authorizationPolicyProviderMock = new Mock<IAuthorizationPolicyProvider>();
|
||||
authorizationPolicyProviderMock
|
||||
.Setup(s => s.GetPolicyAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(authorizationPolicy))
|
||||
.Verifiable();
|
||||
|
||||
var provider = new AuthorizationApplicationModelProvider(authorizationPolicyProviderMock.Object);
|
||||
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
|
||||
|
||||
// Act
|
||||
var action = GetBaseControllerActionModel(provider, defaultProvider);
|
||||
|
||||
// Assert
|
||||
var actionFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(action.Filters));
|
||||
Assert.Null(actionFilter.Policy);
|
||||
Assert.NotNull(actionFilter.AuthorizeData);
|
||||
Assert.NotNull(actionFilter.PolicyProvider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateControllerModelAndActionModel_NoAuthNoFilter()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new AuthorizationApplicationModelProvider(
|
||||
new DefaultAuthorizationPolicyProvider(
|
||||
new TestOptionsManager<AuthorizationOptions>()
|
||||
));
|
||||
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
|
||||
|
||||
var context = new ApplicationModelProviderContext(new[] { typeof(NoAuthController).GetTypeInfo() });
|
||||
defaultProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var controller = Assert.Single(context.Result.Controllers);
|
||||
Assert.Empty(controller.Filters);
|
||||
var action = Assert.Single(controller.Actions);
|
||||
Assert.Empty(action.Filters);
|
||||
}
|
||||
|
||||
private ActionModel GetBaseControllerActionModel(
|
||||
IApplicationModelProvider authorizationApplicationModelProvider,
|
||||
IApplicationModelProvider applicationModelProvider)
|
||||
{
|
||||
var context = new ApplicationModelProviderContext(new[] { typeof(BaseController).GetTypeInfo() });
|
||||
applicationModelProvider.OnProvidersExecuting(context);
|
||||
var authorizeData = new List<IAuthorizeData>
|
||||
{
|
||||
new AuthorizeAttribute("POLICY")
|
||||
};
|
||||
|
||||
authorizationApplicationModelProvider.OnProvidersExecuting(context);
|
||||
|
||||
var controller = Assert.Single(context.Result.Controllers);
|
||||
Assert.Empty(controller.Filters);
|
||||
var action = Assert.Single(controller.Actions);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
private class BaseController
|
||||
{
|
||||
[Authorize(Policy = "Base")]
|
||||
|
|
@ -102,6 +203,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
}
|
||||
|
||||
public class NoAuthController
|
||||
{
|
||||
public void NoAuthAction()
|
||||
{ }
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public class AnonymousController
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||
{
|
||||
public class AuthorizeFilterIntegrationTest
|
||||
{
|
||||
// This is a test for security, because we can't assume that any IAuthorizationPolicyProvider other than
|
||||
// DefaultAuthorizationPolicyProvider will return the same result for the same input. So a cache could cause
|
||||
// undesired access.
|
||||
[Fact]
|
||||
public async Task AuthorizeFilter_CalledTwiceWithNonDefaultProvider()
|
||||
{
|
||||
// Arrange
|
||||
var applicationModelProviderContext = new ApplicationModelProviderContext(
|
||||
new[] { typeof(AuthorizeController).GetTypeInfo() });
|
||||
|
||||
var policyProvider = new TestAuthorizationPolicyProvider();
|
||||
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
|
||||
|
||||
defaultProvider.OnProvidersExecuting(applicationModelProviderContext);
|
||||
|
||||
var controller = Assert.Single(applicationModelProviderContext.Result.Controllers);
|
||||
var action = Assert.Single(controller.Actions);
|
||||
var authorizeData = action.Attributes.OfType<AuthorizeAttribute>();
|
||||
var authorizeFilter = new AuthorizeFilter(policyProvider, authorizeData);
|
||||
|
||||
var actionContext = new ActionContext(GetHttpContext(), new RouteData(), new ControllerActionDescriptor());
|
||||
|
||||
var authorizationFilterContext = new AuthorizationFilterContext(actionContext, action.Filters);
|
||||
|
||||
// Act
|
||||
await authorizeFilter.OnAuthorizationAsync(authorizationFilterContext);
|
||||
await authorizeFilter.OnAuthorizationAsync(authorizationFilterContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, policyProvider.GetPolicyCount);
|
||||
}
|
||||
|
||||
private HttpContext GetHttpContext()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
httpContext.RequestServices = GetServices();
|
||||
return httpContext;
|
||||
}
|
||||
|
||||
private static IServiceProvider GetServices()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAuthorization();
|
||||
serviceCollection.AddMvc();
|
||||
serviceCollection
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>();
|
||||
|
||||
return serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public class TestAuthorizationPolicyProvider : IAuthorizationPolicyProvider
|
||||
{
|
||||
public int GetPolicyCount = 0;
|
||||
|
||||
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
|
||||
{
|
||||
GetPolicyCount++;
|
||||
|
||||
var requirements = new IAuthorizationRequirement[]
|
||||
{
|
||||
new AssertionRequirement((con) => { return true; })
|
||||
};
|
||||
return Task.FromResult(new AuthorizationPolicy(requirements, new string[] { }));
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthorizeController
|
||||
{
|
||||
[Authorize(Policy = "Base")]
|
||||
public virtual void Authorize()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
|
@ -57,7 +58,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
}
|
||||
|
||||
public static DefaultControllerArgumentBinder GetArgumentBinder(
|
||||
IModelMetadataProvider metadataProvider,
|
||||
IModelMetadataProvider metadataProvider,
|
||||
IModelBinderProvider binderProvider = null)
|
||||
{
|
||||
var services = GetServices();
|
||||
|
|
@ -109,11 +110,13 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
private static IServiceProvider GetServices(Action<MvcOptions> updateOptions = null)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddAuthorization();
|
||||
serviceCollection.AddSingleton(new ApplicationPartManager());
|
||||
serviceCollection.AddMvc();
|
||||
serviceCollection
|
||||
.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>();
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>();
|
||||
|
||||
if (updateOptions != null)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue