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.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@ -29,14 +30,44 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
{
throw new ArgumentNullException(nameof(policy));
}
Policy = policy;
}
/// <summary>
/// Gets the authorization policy to be used.
/// Initialize a new <see cref="AuthorizeFilter"/> instance.
/// </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 />
public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
@ -46,11 +77,17 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
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
if (Policy.AuthenticationSchemes != null && Policy.AuthenticationSchemes.Any())
if (effectivePolicy.AuthenticationSchemes != null && effectivePolicy.AuthenticationSchemes.Any())
{
ClaimsPrincipal newPrincipal = null;
foreach (var scheme in Policy.AuthenticationSchemes)
foreach (var scheme in effectivePolicy.AuthenticationSchemes)
{
var result = await context.HttpContext.Authentication.AuthenticateAsync(scheme);
if (result != null)
@ -76,9 +113,9 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
// 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.
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;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Internal
{
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; } }
@ -33,18 +34,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
throw new ArgumentNullException(nameof(context));
}
AuthorizationPolicy policy;
foreach (var controllerModel in context.Result.Controllers)
{
policy = AuthorizationPolicy.Combine(
_authorizationOptions,
controllerModel.Attributes.OfType<IAuthorizeData>());
if (policy != null)
{
controllerModel.Filters.Add(new AuthorizeFilter(policy));
}
controllerModel.Filters.Add(new AuthorizeFilter(_policyProvider, controllerModel.Attributes.OfType<IAuthorizeData>()));
foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>())
{
controllerModel.Filters.Add(new AllowAnonymousFilter());
@ -52,14 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
foreach (var actionModel in controllerModel.Actions)
{
policy = AuthorizationPolicy.Combine(
_authorizationOptions,
actionModel.Attributes.OfType<IAuthorizeData>());
if (policy != null)
{
actionModel.Filters.Add(new AuthorizeFilter(policy));
}
actionModel.Filters.Add(new AuthorizeFilter(_policyProvider, actionModel.Attributes.OfType<IAuthorizeData>()));
foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>())
{
actionModel.Filters.Add(new AllowAnonymousFilter());

View File

@ -41,6 +41,31 @@ namespace Microsoft.AspNetCore.Mvc.Authorization
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]
public async Task AuthorizeFilterCanAuthorizeNullUser()
{

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void CreateControllerModel_AuthorizeAttributeAddsAuthorizeFilter()
{
// 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 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("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 context = new ApplicationModelProviderContext(new[] { typeof(DerivedController).GetTypeInfo() });
@ -56,14 +56,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Empty(attributeRoutes);
var authorizeFilters = action.Filters.OfType<AuthorizeFilter>();
Assert.Single(authorizeFilters);
Assert.Equal(3, authorizeFilters.First().Policy.Requirements.Count);
Assert.NotNull(authorizeFilters.First().PolicyProvider);
Assert.Equal(3, authorizeFilters.First().AuthorizeData.Count());
}
[Fact]
public void CreateControllerModelAndActionModel_AllowAnonymousAttributeAddsAllowAnonymousFilter()
{
// 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 context = new ApplicationModelProviderContext(new[] { typeof(AnonymousController).GetTypeInfo() });