React to Authorize changes

This commit is contained in:
Hao Kung 2015-02-16 15:40:57 -08:00
parent 3d30fd653e
commit 94ef6cf8fa
24 changed files with 524 additions and 368 deletions

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Security;
using MvcSample.Web.Filters;
using MvcSample.Web.Models;
@ -50,14 +51,12 @@ namespace MvcSample.Web
return new ChallengeResult();
}
[Authorize("CanViewPage")]
public ActionResult NotGrantedClaim(int age = 20, string userName = "SampleUser")
{
return Index(age, userName);
}
[FakeUser]
[Authorize("CanViewAnything")]
public ActionResult AllGranted(int age = 20, string userName = "SampleUser")
{
return Index(age, userName);

View File

@ -40,19 +40,6 @@ namespace MvcSample.Web
app.UseServices(services =>
{
services.ConfigureAuthorization(auth =>
{
auth.AddPolicy("CanViewPage",
new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewPage", "CanViewAnything").Build());
auth.AddPolicy("CanViewAnything",
new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewAnything").Build());
// This policy basically requires that the auth type is present
var basicPolicy = new AuthorizationPolicyBuilder("Basic").RequiresClaim(ClaimTypes.NameIdentifier);
auth.AddPolicy("RequireBasic", basicPolicy.Build());
});
services.AddMvc();
services.AddSingleton<PassThroughAttribute>();
services.AddSingleton<UserNameService>();
@ -131,4 +118,4 @@ namespace MvcSample.Web
});
}
}
}
}

View File

@ -8,6 +8,8 @@ using System.Reflection;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Security;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
@ -16,6 +18,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
/// </summary>
public class DefaultActionModelBuilder : IActionModelBuilder
{
private readonly AuthorizationOptions _authorizationOptions;
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> options)
{
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
}
/// <inheritdoc />
public IEnumerable<ActionModel> BuildActionModels([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo)
{
@ -256,6 +265,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
AddRange(actionModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
AddRange(actionModel.Filters, attributes.OfType<IFilter>());
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{
actionModel.Filters.Add(new AuthorizeFilter(policy));
}
var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
if (actionName?.Name != null)
{

View File

@ -8,7 +8,9 @@ using System.Reflection;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Security;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
@ -19,15 +21,20 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
private readonly IActionModelBuilder _actionModelBuilder;
private readonly ILogger _logger;
private readonly AuthorizationOptions _authorizationOptions;
/// <summary>
/// Creates a new <see cref="DefaultControllerModelBuilder"/>.
/// </summary>
/// <param name="actionModelBuilder">The <see cref="IActionModelBuilder"/> used to create actions.</param>
public DefaultControllerModelBuilder(IActionModelBuilder actionModelBuilder, ILoggerFactory loggerFactory)
public DefaultControllerModelBuilder(
IActionModelBuilder actionModelBuilder,
ILoggerFactory loggerFactory,
IOptions<AuthorizationOptions> options)
{
_actionModelBuilder = actionModelBuilder;
_logger = loggerFactory.Create<DefaultControllerModelBuilder>();
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
}
/// <inheritdoc />
@ -72,6 +79,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{
controllerModel.Filters.Add(new AuthorizeFilter(policy));
}
AddRange(
controllerModel.AttributeRoutes,
attributes.OfType<IRouteTemplateProvider>().Select(rtp => new AttributeRouteModel(rtp)));

View File

@ -1,88 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Security;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
public class AuthorizeAttribute : AuthorizationFilterAttribute
{
private string _roles;
private string[] _rolesSplit;
public AuthorizeAttribute() { }
public AuthorizeAttribute(string policy)
{
Policy = policy;
}
public string Policy { get; set; }
public string Roles
{
get { return _roles; }
set
{
_roles = value;
if (string.IsNullOrWhiteSpace(_roles))
{
_rolesSplit = null;
}
else
{
_rolesSplit = _roles.Split(',');
}
}
}
public override async Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
{
var httpContext = context.HttpContext;
// Allow Anonymous skips all authorization
if (HasAllowAnonymous(context))
{
return;
}
var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
// Build a policy for the requested roles if specified
if (_rolesSplit != null)
{
var rolesPolicy = new AuthorizationPolicyBuilder();
rolesPolicy.RequiresRole(_rolesSplit);
if (!await authService.AuthorizeAsync(rolesPolicy.Build(), httpContext, context))
{
Fail(context);
return;
}
}
var authorized = (Policy == null)
// [Authorize] with no policy just requires any authenticated user
? await authService.AuthorizeAsync(BuildAnyAuthorizedUserPolicy(), httpContext, context)
: await authService.AuthorizeAsync(Policy, httpContext, context);
if (!authorized)
{
Fail(context);
}
}
private static AuthorizationPolicy BuildAnyAuthorizedUserPolicy()
{
return new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
}
public sealed override void OnAuthorization([NotNull] AuthorizationContext context)
{
// The async filter will be called by the filter pipeline.
throw new NotImplementedException(Resources.AuthorizeAttribute_OnAuthorizationNotImplemented);
}
}
}

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Security;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An implementation of <see cref="IAsyncAuthorizationFilter"/>
/// </summary>
public class AuthorizeFilter : IAsyncAuthorizationFilter
{
/// <summary>
/// Authorize filter for a specific policy.
/// </summary>
/// <param name="policy"></param>
public AuthorizeFilter([NotNull] AuthorizationPolicy policy)
{
Policy = policy;
}
/// <summary>
/// Authorization policy to be used.
/// </summary>
public AuthorizationPolicy Policy { get; private set; }
/// <inheritdoc />
public virtual async Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
{
// Build a ClaimsPrincipal with the Policy's required authentication types
if (Policy.ActiveAuthenticationTypes != null && Policy.ActiveAuthenticationTypes.Any())
{
var results = await context.HttpContext.AuthenticateAsync(Policy.ActiveAuthenticationTypes);
if (results != null)
{
context.HttpContext.User = new ClaimsPrincipal(results.Where(r => r.Identity != null).Select(r => r.Identity));
}
}
// Allow Anonymous skips all authorization
if (context.Filters.Any(item => item is IAllowAnonymous))
{
return;
}
var httpContext = context.HttpContext;
var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
// Note: Default Anonymous User is new ClaimsPrincipal(new ClaimsIdentity())
if (httpContext.User == null ||
!httpContext.User.Identities.Any(i => i.IsAuthenticated) ||
!await authService.AuthorizeAsync(httpContext.User, context, Policy))
{
context.Result = new ChallengeResult(Policy.ActiveAuthenticationTypes.ToArray());
}
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Security;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
@ -43,14 +44,14 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
// Arrange
var action = new ActionModel(typeof(TestController).GetMethod("Edit"),
new List<object>() { new HttpGetAttribute() });
new List<object>() { new HttpGetAttribute(), new AuthorizeAttribute() });
action.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
action.ActionName = "Edit";
action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
new List<object>());
action.Filters.Add(new AuthorizeAttribute());
action.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().Build()));
action.HttpMethods.Add("GET");
action.RouteConstraints.Add(new AreaAttribute("Admin"));
action.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Security;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
@ -49,12 +50,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
// Arrange
var controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
new List<object>() { new HttpGetAttribute() });
new List<object>() { new HttpGetAttribute(), new AuthorizeAttribute() });
controller.ActionConstraints.Add(new HttpMethodConstraint(new string[] { "GET" }));
controller.Application = new ApplicationModel();
controller.ControllerName = "cool";
controller.Filters.Add(new AuthorizeAttribute());
controller.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().Build()));
controller.RouteConstraints.Add(new AreaAttribute("Admin"));
controller.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));

View File

@ -5,6 +5,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Security;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
@ -18,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_WithInheritedMethods(string methodName, bool expected)
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod(methodName);
Assert.NotNull(method);
@ -33,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_OverridenMethodControllerClass()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(BaseController).GetMethod(nameof(BaseController.Redirect));
Assert.NotNull(method);
@ -48,7 +51,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_PrivateMethod_FromUserDefinedController()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod(
"PrivateMethod",
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
@ -65,7 +68,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_OperatorOverloadingMethod_FromOperatorOverloadingController()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(OperatorOverloadingController).GetMethod("op_Addition");
Assert.NotNull(method);
Assert.True(method.IsSpecialName);
@ -81,7 +84,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_GenericMethod_FromUserDefinedController()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod("GenericMethod");
Assert.NotNull(method);
@ -96,7 +99,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_OverridenNonActionMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod("OverridenNonActionMethod");
Assert.NotNull(method);
@ -115,7 +118,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_OverriddenMethodsFromObjectClass(string methodName)
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod(
methodName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
@ -132,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_DerivedControllerIDisposableDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(DerivedController).GetTypeInfo();
var methodInfo =
typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0];
@ -150,7 +153,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_DerivedControllerDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(DerivedController).GetTypeInfo();
var methodInfo =
typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0];
@ -172,7 +175,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_OverriddenDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(DerivedOverriddenDisposeController).GetTypeInfo();
var method = typeInfo.GetDeclaredMethods("Dispose").SingleOrDefault();
Assert.NotNull(method);
@ -188,7 +191,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_NewDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(DerivedNewDisposeController).GetTypeInfo();
var method = typeInfo.GetDeclaredMethods("Dispose").SingleOrDefault();
Assert.NotNull(method);
@ -204,7 +207,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_PocoControllerIDisposableDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(IDisposablePocoController).GetTypeInfo();
var methodInfo =
typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0];
@ -222,7 +225,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_PocoControllerDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(IDisposablePocoController).GetTypeInfo();
var methodInfo =
typeInfo.GetRuntimeInterfaceMap(typeof(IDisposable)).TargetMethods[0];
@ -244,7 +247,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_SimplePocoControllerDisposeMethod()
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var typeInfo = typeof(SimplePocoController).GetTypeInfo();
var methods = typeInfo.GetMethods().Where(m => m.Name.Equals("Dispose"));
@ -267,7 +270,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void IsAction_StaticMethods(string methodName)
{
// Arrange
var builder = new AccessibleActionModelBuilder();
var builder = CreateTestAccessibleActionModelBuilder();
var method = typeof(DerivedController).GetMethod(
methodName,
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
@ -284,7 +287,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_ConventionallyRoutedAction_WithoutHttpConstraints()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo();
var actionName = nameof(ConventionallyRoutedController.Edit);
@ -303,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_ConventionallyRoutedAction_WithHttpConstraints()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo();
var actionName = nameof(ConventionallyRoutedController.Update);
@ -320,11 +323,34 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.IsType<CustomHttpMethodsAttribute>(Assert.Single(action.Attributes));
}
[Fact]
public void GetActions_BaseAuthorizeFiltersAreStillValidWhenOverriden()
{
// Arrange
var options = new AuthorizationOptions();
options.AddPolicy("Base", policy => policy.RequiresClaim("Basic").RequiresClaim("Basic2"));
options.AddPolicy("Derived", policy => policy.RequiresClaim("Derived"));
var builder = CreateTestDefaultActionModelBuilder(options);
var typeInfo = typeof(DerivedController).GetTypeInfo();
var actionName = nameof(DerivedController.Authorize);
// Act
var actions = builder.BuildActionModels(typeInfo, typeInfo.GetMethod(actionName));
// Assert
var action = Assert.Single(actions);
Assert.Equal("Authorize", action.ActionName);
Assert.Null(action.AttributeRouteModel);
var authorizeFilters = action.Filters.OfType<AuthorizeFilter>();
Assert.Single(authorizeFilters);
Assert.Equal(3, authorizeFilters.First().Policy.Requirements.Count);
}
[Fact]
public void GetActions_ConventionallyRoutedActionWithHttpConstraints_AndInvalidRouteTemplateProvider()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo();
var actionName = nameof(ConventionallyRoutedController.Delete);
@ -346,7 +372,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_ConventionallyRoutedAction_WithMultipleHttpConstraints()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo();
var actionName = nameof(ConventionallyRoutedController.Details);
@ -365,7 +391,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_ConventionallyRoutedAction_WithMultipleOverlappingHttpConstraints()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(ConventionallyRoutedController).GetTypeInfo();
var actionName = nameof(ConventionallyRoutedController.List);
@ -385,7 +411,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_AttributeRouteOnAction()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo();
var actionName = nameof(NoRouteAttributeOnControllerController.Edit);
@ -410,7 +436,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_AttributeRouteOnAction_RouteAttribute()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo();
var actionName = nameof(NoRouteAttributeOnControllerController.Update);
@ -434,7 +460,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_AttributeRouteOnAction_AcceptVerbsAttributeWithTemplate()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo();
var actionName = nameof(NoRouteAttributeOnControllerController.List);
@ -458,7 +484,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_AttributeRouteOnAction_CreatesOneActionInforPerRouteTemplate()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo();
var actionName = nameof(NoRouteAttributeOnControllerController.Index);
@ -489,7 +515,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_NoRouteOnController_AllowsConventionallyRoutedActions_OnTheSameController()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(NoRouteAttributeOnControllerController).GetTypeInfo();
var actionName = nameof(NoRouteAttributeOnControllerController.Remove);
@ -514,7 +540,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_RouteAttributeOnController_CreatesAttributeRoute_ForNonAttributedActions(Type controller)
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = controller.GetTypeInfo();
// Act
@ -538,7 +564,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_RouteOnController_CreatesOneActionInforPerRouteTemplateOnAction(Type controller)
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = controller.GetTypeInfo();
// Act
@ -567,7 +593,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_MixedHttpVerbsAndRoutes_EmptyVerbWithRoute()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo();
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.VerbAndRoute);
@ -584,7 +610,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyVerbsWithMultipleRoutes()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo();
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsAndRoutes);
@ -605,7 +631,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbsWithMultipleRoutes()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo();
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsWithAnyWithoutTemplateAndRoutes);
@ -629,7 +655,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void GetActions_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs()
{
// Arrange
var builder = new DefaultActionModelBuilder();
var builder = CreateTestDefaultActionModelBuilder();
var typeInfo = typeof(MixedHttpVerbsAndRouteAttributeController).GetTypeInfo();
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.Invalid);
@ -646,8 +672,25 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.Equal<string>(new string[] { "GET" }, action.HttpMethods);
}
private static DefaultActionModelBuilder CreateTestDefaultActionModelBuilder(
AuthorizationOptions authOptions = null)
{
var options = new Mock<IOptions<AuthorizationOptions>>();
options.Setup(o => o.Options).Returns(authOptions ?? new AuthorizationOptions());
return new DefaultActionModelBuilder(options.Object);
}
private static AccessibleActionModelBuilder CreateTestAccessibleActionModelBuilder()
{
var options = new Mock<IOptions<AuthorizationOptions>>();
options.Setup(o => o.Options).Returns(new AuthorizationOptions());
return new AccessibleActionModelBuilder(options.Object);
}
private class AccessibleActionModelBuilder : DefaultActionModelBuilder
{
public AccessibleActionModelBuilder(IOptions<AuthorizationOptions> options) : base(options) { }
public new bool IsAction([NotNull] TypeInfo typeInfo, [NotNull]MethodInfo methodInfo)
{
return base.IsAction(typeInfo, methodInfo);
@ -674,6 +717,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
return base.Redirect(url + "#RedirectOverride");
}
[Authorize(Policy = "Base")]
public virtual void Authorize()
{
}
}
private class DerivedController : BaseController
@ -691,6 +740,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
}
[Authorize(Policy = "Derived")]
public override void Authorize()
{
}
public void GenericMethod<T>()
{
}

View File

@ -2,9 +2,11 @@
// 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.AspNet.Mvc.Filters;
using Microsoft.AspNet.Security;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
@ -15,8 +17,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_DerivedFromControllerClass_HasFilter()
{
// Arrange
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var typeInfo = typeof(StoreController).GetTypeInfo();
// Act
@ -27,14 +30,31 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.IsType<ControllerActionFilter>(filter);
}
[Fact]
public void BuildControllerModel_AuthorizeAttributeAddsAuthorizeFilter()
{
// Arrange
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var typeInfo = typeof(AccountController).GetTypeInfo();
// Act
var model = builder.BuildControllerModel(typeInfo);
// Assert
Assert.True(model.Filters.Any(f => f is AuthorizeFilter));
}
// This class has a filter attribute, but doesn't implement any filter interfaces,
// so ControllerFilter is not present.
[Fact]
public void BuildControllerModel_ClassWithoutFilterInterfaces_HasNoControllerFilter()
{
// Arrange
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var typeInfo = typeof(NoFiltersController).GetTypeInfo();
// Act
@ -49,8 +69,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_ClassWithFilterInterfaces_HasFilter()
{
// Arrange
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var typeInfo = typeof(SomeFiltersController).GetTypeInfo();
// Act
@ -65,8 +86,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void BuildControllerModel_ClassWithFilterInterfaces_UnsupportedType()
{
// Arrange
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var typeInfo = typeof(UnsupportedFiltersController).GetTypeInfo();
// Act
@ -81,11 +103,16 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
}
[Produces("application/json")]
private class NoFiltersController
public class NoFiltersController
{
}
private class SomeFiltersController : IAsyncActionFilter, IResultFilter
[Authorize]
public class AccountController
{
}
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
{
public Task OnActionExecutionAsync(
[NotNull] ActionExecutingContext context,
@ -121,4 +148,4 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
}
}
}
}
}

View File

@ -1363,8 +1363,9 @@ namespace Microsoft.AspNet.Mvc.Test
IEnumerable<IFilter> filters = null)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { controllerTypeInfo });
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var provider = new ControllerActionDescriptorProvider(
controllerTypeProvider,
@ -1380,8 +1381,9 @@ namespace Microsoft.AspNet.Mvc.Test
params TypeInfo[] controllerTypeInfo)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfo);
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var controllerModelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var provider = new ControllerActionDescriptorProvider(
controllerTypeProvider,
@ -1398,8 +1400,9 @@ namespace Microsoft.AspNet.Mvc.Test
IApplicationModelConvention convention)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider(new[] { type });
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var options = new MockMvcOptionsAccessor();
options.Options.Conventions.Add(convention);
@ -1415,8 +1418,9 @@ namespace Microsoft.AspNet.Mvc.Test
private IEnumerable<ActionDescriptor> GetDescriptors(params TypeInfo[] controllerTypeInfos)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfos);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var provider = new ControllerActionDescriptorProvider(
controllerTypeProvider,

View File

@ -5,6 +5,7 @@ using System.ComponentModel.Design;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Security;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.NestedProviders;
using Microsoft.Framework.Logging;
@ -123,8 +124,9 @@ namespace Microsoft.AspNet.Mvc.Logging
ILoggerFactory loggerFactory, params TypeInfo[] controllerTypeInfo)
{
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypeInfo);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
loggerFactory);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
loggerFactory,
null);
var provider = new ControllerActionDescriptorProvider(
controllerTypeProvider,

View File

@ -765,8 +765,9 @@ namespace Microsoft.AspNet.Mvc
.ToList();
var controllerTypeProvider = new FixedSetControllerTypeProvider(controllerTypes);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
return new ControllerActionDescriptorProvider(
controllerTypeProvider,

View File

@ -1,65 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Claims;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Moq;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AuthorizeAttributeTestsBase
{
protected AuthorizationContext GetAuthorizationContext(Action<ServiceCollection> registerServices, bool anonymous = false)
{
var validUser = 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"));
validUser.AddIdentity(
new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CupBearer"),
new Claim(ClaimTypes.Role, "Token"),
new Claim(ClaimTypes.NameIdentifier, "John Bear")},
"Bearer"));
// ServiceProvider
var serviceCollection = new ServiceCollection();
if (registerServices != null)
{
registerServices(serviceCollection);
}
var serviceProvider = serviceCollection.BuildServiceProvider();
// HttpContext
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.User).Returns(anonymous ? null : validUser);
httpContext.SetupGet(c => c.RequestServices).Returns(serviceProvider);
// AuthorizationContext
var actionContext = new ActionContext(
httpContext: httpContext.Object,
routeData: new RouteData(),
actionDescriptor: null
);
var authorizationContext = new AuthorizationContext(
actionContext,
Enumerable.Empty<IFilter>().ToList()
);
return authorizationContext;
}
}
}

View File

@ -1,33 +1,39 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Security;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
namespace Microsoft.AspNet.Mvc.Test
{
public class AuthorizeAttributeTests : AuthorizeAttributeTestsBase
public class AuthorizeFilterTest
{
[Fact]
public void InvalidUser()
{
var authorizationContext = GetAuthorizationContext(services => services.AddAuthorization());
Assert.True(authorizationContext.HttpContext.User.Identities.Any(i => i.IsAuthenticated));
}
[Fact]
public async Task Invoke_ValidClaimShouldNotFail()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewPage");
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("CanViewPage", policy.Build());
});
});
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage").Build());
var authorizationContext = GetAuthorizationContext(services => services.AddAuthorization());
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
@ -38,13 +44,13 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var authorizationOptions = new AuthorizationOptions();
var authorizeAttribute = new AuthorizeAttribute();
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
var authorizationContext = GetAuthorizationContext(services =>
services.AddAuthorization(),
anonymous: true);
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -54,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_EmptyClaimsWithAllowAnonymousAttributeShouldNotRejectAnonymousUser()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute();
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
@ -65,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
authorizationContext.Filters.Add(new AllowAnonymousAttribute());
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
@ -75,7 +81,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_EmptyClaimsShouldAuthorizeAuthenticatedUser()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute();
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
@ -83,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
@ -93,19 +99,15 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_SingleValidClaimShouldSucceed()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewCommentOrPage");
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewComment", "CanViewPage").Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewComment", "CanViewPage");
options.AddPolicy("CanViewCommentOrPage", policy.Build());
});
services.AddAuthorization();
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
@ -115,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_RequireAdminRoleShouldFailWithNoHandlers()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute { Roles = "Administrator" };
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequiresRole("Administrator").Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddOptions();
@ -123,7 +125,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -133,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_RequireAdminAndUserRoleWithNoPolicyShouldSucceed()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute { Roles = "Administrator,User" };
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequiresRole("Administrator").Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
@ -141,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
@ -151,7 +153,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_RequireUnknownRoleShouldFail()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute { Roles = "Wut" };
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().RequiresRole("Wut").Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
@ -159,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -169,19 +171,18 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_RequireAdminRoleButFailPolicyShouldFail()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute { Roles = "Administrator", Policy = "Basic" };
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder()
.RequiresRole("Administrator")
.RequiresClaim("Permission", "CanViewComment")
.Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewComment");
options.AddPolicy("CanViewComment", policy.Build());
});
services.AddAuthorization();
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -191,19 +192,17 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_InvalidClaimShouldFail()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewComment");
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewComment")
.Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewComment");
options.AddPolicy("CanViewComment", policy.Build());
});
services.AddAuthorization();
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -216,14 +215,16 @@ namespace Microsoft.AspNet.Mvc.Core.Test
bool authorizationServiceIsCalled = false;
var authorizationService = new Mock<IAuthorizationService>();
authorizationService
.Setup(x => x.AuthorizeAsync("CanViewComment", null, null))
.Setup(x => x.AuthorizeAsync(null, null, "CanViewComment"))
.Returns(() =>
{
authorizationServiceIsCalled = true;
return Task.FromResult(true);
});
var authorizeAttribute = new AuthorizeAttribute("CanViewComment");
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewComment")
.Build());
var authorizationContext = GetAuthorizationContext(services =>
services.AddInstance(authorizationService.Object)
);
@ -231,7 +232,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
authorizationContext.Result = new HttpStatusCodeResult(StatusCodes.Status401Unauthorized);
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.False(authorizationServiceIsCalled);
@ -241,19 +242,17 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_FailWhenLookingForClaimInOtherIdentity()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewComment");
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewComment")
.Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder("Bearer").RequiresClaim("Permission", "CanViewComment");
options.AddPolicy("CanViewComment", policy.Build());
});
services.AddAuthorization();
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
@ -263,30 +262,10 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task Invoke_CanLookingForClaimsInMultipleIdentities()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewCommentCupBearer");
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization(null, options =>
{
var policy = new AuthorizationPolicyBuilder("Basic", "Bearer")
.RequiresClaim("Permission", "CanViewComment")
.RequiresClaim("Permission", "CupBearer");
options.AddPolicy("CanViewComment", policy.Build());
});
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
}
public async Task Invoke_NoPoliciesShouldNotFail()
{
// Arrange
var authorizeAttribute = new AuthorizeAttribute("CanViewPage");
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder("Basic", "Bearer")
.RequiresClaim("Permission", "CanViewComment")
.RequiresClaim("Permission", "CupBearer")
.Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
@ -294,10 +273,80 @@ namespace Microsoft.AspNet.Mvc.Core.Test
});
// Act
await authorizeAttribute.OnAuthorizationAsync(authorizationContext);
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Null(authorizationContext.Result);
Assert.NotNull(authorizationContext.Result);
}
[Fact]
public async Task Invoke_EmptyPolicyWillFail()
{
// Arrange
var authorizeFilter = new AuthorizeFilter(new AuthorizationPolicyBuilder().Build());
var authorizationContext = GetAuthorizationContext(services =>
{
services.AddAuthorization();
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
});
// Act
await authorizeFilter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.NotNull(authorizationContext.Result);
}
private AuthorizationContext GetAuthorizationContext(Action<ServiceCollection> registerServices, bool anonymous = false)
{
var validUser = 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"));
validUser.AddIdentity(
new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CupBearer"),
new Claim(ClaimTypes.Role, "Token"),
new Claim(ClaimTypes.NameIdentifier, "John Bear")},
"Bearer"));
// ServiceProvider
var serviceCollection = new ServiceCollection();
if (registerServices != null)
{
registerServices(serviceCollection);
}
var serviceProvider = serviceCollection.BuildServiceProvider();
// HttpContext
var httpContext = new Mock<HttpContext>();
httpContext.SetupProperty(c => c.User);
if (!anonymous)
{
httpContext.Object.User = validUser;
}
httpContext.SetupGet(c => c.RequestServices).Returns(serviceProvider);
// AuthorizationContext
var actionContext = new ActionContext(
httpContext: httpContext.Object,
routeData: new RouteData(),
actionDescriptor: null
);
var authorizationContext = new AuthorizationContext(
actionContext,
Enumerable.Empty<IFilter>().ToList()
);
return authorizationContext;
}
}
}

View File

@ -106,8 +106,12 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal("4", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task CanAuthorizeParticularUsers()
[Theory]
[InlineData("AdminRole")]
[InlineData("InteractiveUsers")]
[InlineData("ApiManagers")]
public async Task CanAuthorize(string testAction)
{
// Arrange
var server = TestServer.Create(_services, _app);
@ -115,7 +119,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Act
var response = await client.GetAsync(
"http://localhost/AuthorizeUser/ReturnHelloWorldOnlyForAuthorizedUser");
"http://localhost/AuthorizeUser/"+testAction);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -372,8 +372,9 @@ namespace System.Web.Http
var assemblyProvider = new FixedSetAssemblyProvider();
assemblyProvider.CandidateAssemblies.Add(GetType().GetTypeInfo().Assembly);
var controllerTypeProvider = new NamespaceFilteredControllerTypeProvider(assemblyProvider);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(),
NullLoggerFactory.Instance);
var modelBuilder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
null);
var filterProvider = new Mock<IGlobalFilterProvider>();
filterProvider

View File

@ -3,6 +3,7 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security;
@ -11,51 +12,21 @@ using Microsoft.Framework.OptionsModel;
namespace FiltersWebSite
{
public class BasicOptions : AuthenticationOptions
{
public BasicOptions()
{
AuthenticationType = "Basic";
AuthenticationMode = AuthenticationMode.Passive;
}
}
public class AuthorizeBasicMiddleware : AuthenticationMiddleware<BasicOptions>
{
public AuthorizeBasicMiddleware(
RequestDelegate next,
IServiceProvider services,
IOptions<BasicOptions> options) :
base(next, services, options, null)
{ }
IOptions<BasicOptions> options,
string authType) :
base(next, services, options,
new ConfigureOptions<BasicOptions>(o => o.AuthenticationType = authType) { Name = authType })
{
}
protected override AuthenticationHandler<BasicOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
public class BasicAuthenticationHandler : AuthenticationHandler<BasicOptions>
{
protected override void ApplyResponseChallenge()
{
}
protected override void ApplyResponseGrant()
{
}
protected override AuthenticationTicket AuthenticateCore()
{
var id = new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CanViewPage"),
new Claim(ClaimTypes.Role, "Administrator"),
new Claim(ClaimTypes.NameIdentifier, "John")},
"Basic");
return new AuthenticationTicket(id, new AuthenticationProperties());
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security;
using Microsoft.AspNet.Security.Infrastructure;
using Microsoft.Framework.OptionsModel;
namespace FiltersWebSite
{
public class BasicAuthenticationHandler : AuthenticationHandler<BasicOptions>
{
protected override void ApplyResponseChallenge()
{
}
protected override void ApplyResponseGrant()
{
}
protected override AuthenticationTicket AuthenticateCore()
{
var id = new ClaimsIdentity(
new Claim[] {
new Claim("Permission", "CanViewPage"),
new Claim("Manager", "yes"),
new Claim(ClaimTypes.Role, "Administrator"),
new Claim(ClaimTypes.NameIdentifier, "John")
},
Options.AuthenticationType);
return new AuthenticationTicket(id, new AuthenticationProperties());
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security;
using Microsoft.AspNet.Security.Infrastructure;
using Microsoft.Framework.OptionsModel;
namespace FiltersWebSite
{
public class BasicOptions : AuthenticationOptions
{
public BasicOptions()
{
AuthenticationMode = AuthenticationMode.Passive;
}
}
}

View File

@ -2,15 +2,27 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Security;
namespace FiltersWebSite
{
[AuthorizeUser]
[Authorize("RequireBasic")]
[Authorize("Api")]
public class AuthorizeUserController : Controller
{
[Authorize("CanViewPage")]
public string ReturnHelloWorldOnlyForAuthorizedUser()
[Authorize("Api-Manager")]
public string ApiManagers()
{
return "Hello World!";
}
[Authorize(Roles = "Administrator")]
public string AdminRole()
{
return "Hello World!";
}
[Authorize("Interactive")]
public string InteractiveUsers()
{
return "Hello World!";
}
@ -27,6 +39,5 @@ namespace FiltersWebSite
{
return "Hello World!";
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Security;
using Microsoft.Framework.DependencyInjection;
namespace FiltersWebSite
{
public class ManagerHandler : AuthorizationHandler<OperationAuthorizationRequirement>
{
public override void Handle(Microsoft.AspNet.Security.AuthorizationContext context, OperationAuthorizationRequirement requirement)
{
if (context.User.HasClaim("Manager", "yes"))
{
context.Succeed(requirement);
}
}
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Security;
namespace FiltersWebSite
{
public static class Operations
{
public static OperationAuthorizationRequirement Edit = new OperationAuthorizationRequirement { Name = "Edit" };
public static OperationAuthorizationRequirement Create = new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Delete = new OperationAuthorizationRequirement { Name = "Delete" };
}
}

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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Security;
@ -18,23 +19,32 @@ namespace FiltersWebSite
app.UseServices(services =>
{
services.AddMvc(configuration);
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
// This policy cannot succeed since it has no requirements
options.AddPolicy("Impossible",
new AuthorizationPolicyBuilder()
.Build());
options.AddPolicy("RequireBasic",
new AuthorizationPolicyBuilder("Basic")
.RequiresClaim(ClaimTypes.NameIdentifier)
.Build());
options.AddPolicy("CanViewPage",
new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewPage")
.Build());
options.AddPolicy("Impossible", policy => { });
options.AddPolicy("Api", policy =>
{
policy.ActiveAuthenticationTypes.Add("Api");
policy.RequiresClaim(ClaimTypes.NameIdentifier);
});
options.AddPolicy("Api-Manager", policy =>
{
policy.ActiveAuthenticationTypes.Add("Api");
policy.Requirements.Add(Operations.Edit);
});
options.AddPolicy("Interactive", policy =>
{
policy.ActiveAuthenticationTypes.Add("Interactive");
policy.RequiresClaim(ClaimTypes.NameIdentifier)
.RequiresClaim("Permission", "CanViewPage");
});
});
services.AddSingleton<RandomNumberFilter>();
services.AddSingleton<RandomNumberService>();
services.AddTransient<IAuthorizationHandler, ManagerHandler>();
services.Configure<BasicOptions>(o => o.AuthenticationType = "Api", "Api");
services.Configure<BasicOptions>(o => o.AuthenticationType = "Interactive", "Interactive");
services.Configure<MvcOptions>(options =>
{
@ -48,7 +58,8 @@ namespace FiltersWebSite
app.UseErrorReporter();
app.UseMiddleware<AuthorizeBasicMiddleware>();
app.UseMiddleware<AuthorizeBasicMiddleware>("Interactive");
app.UseMiddleware<AuthorizeBasicMiddleware>("Api");
app.UseMvc(routes =>
{