AuthPolicy should use IPolicyProvider

This commit is contained in:
Hao Kung 2016-05-31 13:47:58 -07:00
parent e63f094a5f
commit c7a46e4caf
4 changed files with 82 additions and 33 deletions

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -29,14 +30,44 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{ {
throw new ArgumentNullException(nameof(policy)); throw new ArgumentNullException(nameof(policy));
} }
Policy = policy; Policy = policy;
} }
/// <summary> /// <summary>
/// Gets the authorization policy to be used. /// Initialize a new <see cref="AuthorizeFilter"/> instance.
/// </summary> /// </summary>
public AuthorizationPolicy Policy { get; } /// <param name="policyProvider">The <see cref="IAuthorizationPolicyProvider"/> to use to resolve policy names.</param>
/// <param name="authorizeData">The <see cref="IAuthorizeData"/> to combine into an <see cref="IAuthorizeData"/>.</param>
public AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData)
{
if (policyProvider == null)
{
throw new ArgumentNullException(nameof(policyProvider));
}
if (authorizeData == null)
{
throw new ArgumentNullException(nameof(authorizeData));
}
PolicyProvider = policyProvider;
AuthorizeData = authorizeData;
}
/// <summary>
/// The <see cref="IAuthorizationPolicyProvider"/> to use to resolve policy names.
/// </summary>
public IAuthorizationPolicyProvider PolicyProvider { get; }
/// <summary>
/// The <see cref="IAuthorizeData"/> to combine into an <see cref="IAuthorizeData"/>.
/// </summary>
public IEnumerable<IAuthorizeData> AuthorizeData { get; }
/// <summary>
/// Gets the authorization policy to be used. If null, the policy will be constructed via
/// AuthorizePolicy.CombineAsync(PolicyProvider, AuthorizeData)
/// </summary>
public AuthorizationPolicy Policy { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context) public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
@ -46,11 +77,17 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
var effectivePolicy = Policy ?? await AuthorizationPolicy.CombineAsync(PolicyProvider, AuthorizeData);
if (effectivePolicy == null)
{
return;
}
// Build a ClaimsPrincipal with the Policy's required authentication types // Build a ClaimsPrincipal with the Policy's required authentication types
if (Policy.AuthenticationSchemes != null && Policy.AuthenticationSchemes.Any()) if (effectivePolicy.AuthenticationSchemes != null && effectivePolicy.AuthenticationSchemes.Any())
{ {
ClaimsPrincipal newPrincipal = null; ClaimsPrincipal newPrincipal = null;
foreach (var scheme in Policy.AuthenticationSchemes) foreach (var scheme in effectivePolicy.AuthenticationSchemes)
{ {
var result = await context.HttpContext.Authentication.AuthenticateAsync(scheme); var result = await context.HttpContext.Authentication.AuthenticateAsync(scheme);
if (result != null) if (result != null)
@ -76,9 +113,9 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>(); var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
// Note: Default Anonymous User is new ClaimsPrincipal(new ClaimsIdentity()) // Note: Default Anonymous User is new ClaimsPrincipal(new ClaimsIdentity())
if (!await authService.AuthorizeAsync(httpContext.User, context, Policy)) if (!await authService.AuthorizeAsync(httpContext.User, context, effectivePolicy))
{ {
context.Result = new ChallengeResult(Policy.AuthenticationSchemes.ToArray()); context.Result = new ChallengeResult(effectivePolicy.AuthenticationSchemes.ToArray());
} }
} }
} }

View File

@ -2,21 +2,22 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Internal namespace Microsoft.AspNetCore.Mvc.Internal
{ {
public class AuthorizationApplicationModelProvider : IApplicationModelProvider public class AuthorizationApplicationModelProvider : IApplicationModelProvider
{ {
private readonly AuthorizationOptions _authorizationOptions; private readonly IAuthorizationPolicyProvider _policyProvider;
public AuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor) public AuthorizationApplicationModelProvider(IAuthorizationPolicyProvider policyProvider)
{ {
_authorizationOptions = authorizationOptionsAccessor.Value; _policyProvider = policyProvider;
} }
public int Order { get { return -1000 + 10; } } public int Order { get { return -1000 + 10; } }
@ -33,18 +34,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
AuthorizationPolicy policy;
foreach (var controllerModel in context.Result.Controllers) foreach (var controllerModel in context.Result.Controllers)
{ {
policy = AuthorizationPolicy.Combine( controllerModel.Filters.Add(new AuthorizeFilter(_policyProvider, controllerModel.Attributes.OfType<IAuthorizeData>()));
_authorizationOptions,
controllerModel.Attributes.OfType<IAuthorizeData>());
if (policy != null)
{
controllerModel.Filters.Add(new AuthorizeFilter(policy));
}
foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>()) foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>())
{ {
controllerModel.Filters.Add(new AllowAnonymousFilter()); controllerModel.Filters.Add(new AllowAnonymousFilter());
@ -52,14 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
foreach (var actionModel in controllerModel.Actions) foreach (var actionModel in controllerModel.Actions)
{ {
policy = AuthorizationPolicy.Combine( actionModel.Filters.Add(new AuthorizeFilter(_policyProvider, actionModel.Attributes.OfType<IAuthorizeData>()));
_authorizationOptions,
actionModel.Attributes.OfType<IAuthorizeData>());
if (policy != null)
{
actionModel.Filters.Add(new AuthorizeFilter(policy));
}
foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>()) foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>())
{ {
actionModel.Filters.Add(new AllowAnonymousFilter()); actionModel.Filters.Add(new AllowAnonymousFilter());

View File

@ -41,6 +41,31 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
Assert.Null(authorizationContext.Result); Assert.Null(authorizationContext.Result);
} }
[Fact]
public async Task AuthorizeFilterWillCallPolicyProviderOnAuthorization()
{
// Arrange
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
var getPolicyCount = 0;
policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny<string>())).ReturnsAsync(new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build())
.Callback(() => getPolicyCount++);
var authorizeFilter = new AuthorizeFilter(policyProvider.Object, new AuthorizeAttribute[] { new AuthorizeAttribute("whatever") });
var authorizationContext = GetAuthorizationContext(services => services.AddAuthorization());
// Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
Assert.Equal(1, getPolicyCount);
Assert.Null(authorizationContext.Result);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
Assert.Equal(2, getPolicyCount);
Assert.Null(authorizationContext.Result);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
Assert.Equal(3, getPolicyCount);
Assert.Null(authorizationContext.Result);
}
[Fact] [Fact]
public async Task AuthorizeFilterCanAuthorizeNullUser() public async Task AuthorizeFilterCanAuthorizeNullUser()
{ {

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void CreateControllerModel_AuthorizeAttributeAddsAuthorizeFilter() public void CreateControllerModel_AuthorizeAttributeAddsAuthorizeFilter()
{ {
// Arrange // Arrange
var provider = new AuthorizationApplicationModelProvider(new TestOptionsManager<AuthorizationOptions>()); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(new TestOptionsManager<AuthorizationOptions>()));
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>()); var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
var context = new ApplicationModelProviderContext(new[] { typeof(AccountController).GetTypeInfo() }); var context = new ApplicationModelProviderContext(new[] { typeof(AccountController).GetTypeInfo() });
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
options.Value.AddPolicy("Base", policy => policy.RequireClaim("Basic").RequireClaim("Basic2")); options.Value.AddPolicy("Base", policy => policy.RequireClaim("Basic").RequireClaim("Basic2"));
options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived")); options.Value.AddPolicy("Derived", policy => policy.RequireClaim("Derived"));
var provider = new AuthorizationApplicationModelProvider(options); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(options));
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>()); var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
var context = new ApplicationModelProviderContext(new[] { typeof(DerivedController).GetTypeInfo() }); var context = new ApplicationModelProviderContext(new[] { typeof(DerivedController).GetTypeInfo() });
@ -56,14 +56,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Empty(attributeRoutes); Assert.Empty(attributeRoutes);
var authorizeFilters = action.Filters.OfType<AuthorizeFilter>(); var authorizeFilters = action.Filters.OfType<AuthorizeFilter>();
Assert.Single(authorizeFilters); Assert.Single(authorizeFilters);
Assert.Equal(3, authorizeFilters.First().Policy.Requirements.Count);
Assert.NotNull(authorizeFilters.First().PolicyProvider);
Assert.Equal(3, authorizeFilters.First().AuthorizeData.Count());
} }
[Fact] [Fact]
public void CreateControllerModelAndActionModel_AllowAnonymousAttributeAddsAllowAnonymousFilter() public void CreateControllerModelAndActionModel_AllowAnonymousAttributeAddsAllowAnonymousFilter()
{ {
// Arrange // Arrange
var provider = new AuthorizationApplicationModelProvider(new TestOptionsManager<AuthorizationOptions>()); var provider = new AuthorizationApplicationModelProvider(new DefaultAuthorizationPolicyProvider(new TestOptionsManager<AuthorizationOptions>()));
var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>()); var defaultProvider = new DefaultApplicationModelProvider(new TestOptionsManager<MvcOptions>());
var context = new ApplicationModelProviderContext(new[] { typeof(AnonymousController).GetTypeInfo() }); var context = new ApplicationModelProviderContext(new[] { typeof(AnonymousController).GetTypeInfo() });