Add some sugar for AuthZ

- Register passthrough handler by default
- AddPolicy overload that takesAction<AuthorizationPolicyBuilder>
- Chaining policy overloads/methods
- More fluent apis for PolicyBuilder
Fixes #122, #114
This commit is contained in:
Hao Kung 2015-01-17 17:11:34 -08:00
parent c53394e847
commit 123065c0ae
5 changed files with 189 additions and 59 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // 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. // 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.Collections.Generic;
namespace Microsoft.AspNet.Security namespace Microsoft.AspNet.Security
@ -15,6 +16,13 @@ namespace Microsoft.AspNet.Security
PolicyMap[name] = policy; PolicyMap[name] = policy;
} }
public void AddPolicy([NotNull] string name, [NotNull] Action<AuthorizationPolicyBuilder> configurePolicy)
{
var policyBuilder = new AuthorizationPolicyBuilder();
configurePolicy(policyBuilder);
PolicyMap[name] = policyBuilder.Build();
}
public AuthorizationPolicy GetPolicy([NotNull] string name) public AuthorizationPolicy GetPolicy([NotNull] string name)
{ {
return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null; return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;

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.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Security.Claims; using System.Security.Claims;
namespace Microsoft.AspNet.Security namespace Microsoft.AspNet.Security
@ -10,14 +11,42 @@ namespace Microsoft.AspNet.Security
{ {
public AuthorizationPolicyBuilder(params string[] activeAuthenticationTypes) public AuthorizationPolicyBuilder(params string[] activeAuthenticationTypes)
{ {
foreach (var authType in activeAuthenticationTypes) { AddAuthenticationTypes(activeAuthenticationTypes);
ActiveAuthenticationTypes.Add(authType); }
}
public AuthorizationPolicyBuilder(AuthorizationPolicy policy)
{
Combine(policy);
} }
public IList<IAuthorizationRequirement> Requirements { get; set; } = new List<IAuthorizationRequirement>(); public IList<IAuthorizationRequirement> Requirements { get; set; } = new List<IAuthorizationRequirement>();
public IList<string> ActiveAuthenticationTypes { get; set; } = new List<string>(); public IList<string> ActiveAuthenticationTypes { get; set; } = new List<string>();
public AuthorizationPolicyBuilder AddAuthenticationTypes(params string[] activeAuthTypes)
{
foreach (var authType in activeAuthTypes)
{
ActiveAuthenticationTypes.Add(authType);
}
return this;
}
public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements)
{
foreach (var req in requirements)
{
Requirements.Add(req);
}
return this;
}
public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy)
{
AddAuthenticationTypes(policy.ActiveAuthenticationTypes.ToArray());
AddRequirements(policy.Requirements.ToArray());
return this;
}
public AuthorizationPolicyBuilder RequiresClaim([NotNull] string claimType, params string[] requiredValues) public AuthorizationPolicyBuilder RequiresClaim([NotNull] string claimType, params string[] requiredValues)
{ {
Requirements.Add(new ClaimsAuthorizationRequirement Requirements.Add(new ClaimsAuthorizationRequirement
@ -55,4 +84,4 @@ namespace Microsoft.AspNet.Security
return new AuthorizationPolicy(Requirements, ActiveAuthenticationTypes); return new AuthorizationPolicy(Requirements, ActiveAuthenticationTypes);
} }
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // 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. // 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.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -22,6 +22,7 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(describe.Transient<IAuthorizationService, DefaultAuthorizationService>()); services.TryAdd(describe.Transient<IAuthorizationService, DefaultAuthorizationService>());
services.Add(describe.Transient<IAuthorizationHandler, ClaimsAuthorizationHandler>()); services.Add(describe.Transient<IAuthorizationHandler, ClaimsAuthorizationHandler>());
services.Add(describe.Transient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>()); services.Add(describe.Transient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>());
services.Add(describe.Transient<IAuthorizationHandler, PassThroughAuthorizationHandler>());
if (configureOptions != null) if (configureOptions != null)
{ {
services.Configure(configureOptions); services.Configure(configureOptions);

View File

@ -9,7 +9,6 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback; using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -53,11 +52,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
options.AddPolicy("Basic", new AuthorizationPolicyBuilder() options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
.RequiresClaim("Permission", "CanViewPage")
.Build());
}); });
}); });
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic")); var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
@ -75,10 +72,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic")); var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
@ -96,10 +92,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -124,10 +119,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -151,10 +145,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -178,10 +171,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -205,10 +197,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -230,10 +221,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext(); var context = SetupContext();
@ -252,10 +242,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext(new ClaimsIdentity()); var context = SetupContext(new ClaimsIdentity());
@ -273,10 +262,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage"); options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -433,13 +421,11 @@ namespace Microsoft.AspNet.Security.Test
public async Task RolePolicyCanBlockNoRole() public async Task RolePolicyCanBlockNoRole()
{ {
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequiresRole("Admin", "Users"); options.AddPolicy("Basic", policy => policy.RequiresRole("Admin", "Users"));
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -462,10 +448,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder(); options.AddPolicy("Basic", policy => { });
options.AddPolicy("Basic", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -489,10 +474,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser(); options.AddPolicy("Any", policy => policy.RequireAuthenticatedUser());
options.AddPolicy("Any", policy.Build());
}); });
}); });
var context = SetupContext( var context = SetupContext(
@ -516,10 +500,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser(); options.AddPolicy("Any", policy => policy.RequireAuthenticatedUser());
options.AddPolicy("Any", policy.Build());
}); });
}); });
var context = SetupContext(new ClaimsIdentity()); var context = SetupContext(new ClaimsIdentity());
@ -546,11 +529,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange // Arrange
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder(); options.AddPolicy("Custom", policy => policy.Requirements.Add(new CustomRequirement()));
policy.Requirements.Add(new CustomRequirement());
options.AddPolicy("Custom", policy.Build());
}); });
}); });
var context = SetupContext(); var context = SetupContext();
@ -562,7 +543,6 @@ namespace Microsoft.AspNet.Security.Test
Assert.False(allowed); Assert.False(allowed);
} }
[Fact] [Fact]
public async Task CustomReqWithHandlerSucceeds() public async Task CustomReqWithHandlerSucceeds()
{ {
@ -570,11 +550,9 @@ namespace Microsoft.AspNet.Security.Test
var authorizationService = BuildAuthorizationService(services => var authorizationService = BuildAuthorizationService(services =>
{ {
services.AddTransient<IAuthorizationHandler, CustomHandler>(); services.AddTransient<IAuthorizationHandler, CustomHandler>();
services.Configure<AuthorizationOptions>(options => services.ConfigureAuthorization(options =>
{ {
var policy = new AuthorizationPolicyBuilder(); options.AddPolicy("Custom", policy => policy.Requirements.Add(new CustomRequirement()));
policy.Requirements.Add(new CustomRequirement());
options.AddPolicy("Custom", policy.Build());
}); });
}); });
var context = SetupContext(); var context = SetupContext();
@ -586,5 +564,120 @@ namespace Microsoft.AspNet.Security.Test
Assert.True(allowed); Assert.True(allowed);
} }
public class PassThroughRequirement : AuthorizationHandler<PassThroughRequirement>, IAuthorizationRequirement
{
public PassThroughRequirement(bool succeed)
{
Succeed = succeed;
}
public bool Succeed { get; set; }
public override Task<bool> CheckAsync(AuthorizationContext context, PassThroughRequirement requirement)
{
return Task.FromResult(Succeed);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task PassThroughRequirementWillSucceedWithoutCustomHandler(bool shouldSucceed)
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.ConfigureAuthorization(options =>
{
options.AddPolicy("Passthrough", policy => policy.Requirements.Add(new PassThroughRequirement(shouldSucceed)));
});
});
var context = SetupContext();
// Act
var allowed = await authorizationService.AuthorizeAsync("Passthrough", context.Object);
// Assert
Assert.Equal(shouldSucceed, allowed);
}
public async Task CanCombinePolicies()
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.ConfigureAuthorization(options =>
{
var basePolicy = new AuthorizationPolicyBuilder().RequiresClaim("Base", "Value").Build();
options.AddPolicy("Combineed", policy => policy.Combine(basePolicy).RequiresClaim("Claim", "Exists"));
});
});
var context = SetupContext(
new ClaimsIdentity(
new Claim[] {
new Claim("Base", "Value"),
new Claim("Claim", "Exists")
},
"AuthType")
);
// Act
var allowed = await authorizationService.AuthorizeAsync("Combined", context.Object);
// Assert
Assert.True(allowed);
}
public async Task CombinePoliciesWillFailIfBasePolicyFails()
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.ConfigureAuthorization(options =>
{
var basePolicy = new AuthorizationPolicyBuilder().RequiresClaim("Base", "Value").Build();
options.AddPolicy("Combined", policy => policy.Combine(basePolicy).RequiresClaim("Claim", "Exists"));
});
});
var context = SetupContext(
new ClaimsIdentity(
new Claim[] {
new Claim("Claim", "Exists")
},
"AuthType")
);
// Act
var allowed = await authorizationService.AuthorizeAsync("Combined", context.Object);
// Assert
Assert.False(allowed);
}
public async Task CombinedPoliciesWillFailIfExtraRequirementFails()
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.ConfigureAuthorization(options =>
{
var basePolicy = new AuthorizationPolicyBuilder().RequiresClaim("Base", "Value").Build();
options.AddPolicy("Combined", policy => policy.Combine(basePolicy).RequiresClaim("Claim", "Exists"));
});
});
var context = SetupContext(
new ClaimsIdentity(
new Claim[] {
new Claim("Base", "Value"),
},
"AuthType")
);
// Act
var allowed = await authorizationService.AuthorizeAsync("Combined", context.Object);
// Assert
Assert.False(allowed);
}
} }
} }