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.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Security
@ -15,6 +16,13 @@ namespace Microsoft.AspNet.Security
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)
{
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.
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
namespace Microsoft.AspNet.Security
@ -10,14 +11,42 @@ namespace Microsoft.AspNet.Security
{
public AuthorizationPolicyBuilder(params string[] activeAuthenticationTypes)
{
foreach (var authType in activeAuthenticationTypes) {
ActiveAuthenticationTypes.Add(authType);
}
AddAuthenticationTypes(activeAuthenticationTypes);
}
public AuthorizationPolicyBuilder(AuthorizationPolicy policy)
{
Combine(policy);
}
public IList<IAuthorizationRequirement> Requirements { get; set; } = new List<IAuthorizationRequirement>();
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)
{
Requirements.Add(new ClaimsAuthorizationRequirement
@ -55,4 +84,4 @@ namespace Microsoft.AspNet.Security
return new AuthorizationPolicy(Requirements, ActiveAuthenticationTypes);
}
}
}
}

View File

@ -1,7 +1,6 @@
// 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.Threading.Tasks;

View File

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

View File

@ -9,7 +9,6 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
@ -53,11 +52,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
options.AddPolicy("Basic", new AuthorizationPolicyBuilder()
.RequiresClaim("Permission", "CanViewPage")
.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
@ -75,10 +72,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
@ -96,10 +92,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
});
});
var context = SetupContext(
@ -124,10 +119,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
});
});
var context = SetupContext(
@ -151,10 +145,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage", "CanViewAnything"));
});
});
var context = SetupContext(
@ -178,10 +171,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(
@ -205,10 +197,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(
@ -230,10 +221,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext();
@ -252,10 +242,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(new ClaimsIdentity());
@ -273,10 +262,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresClaim("Permission", "CanViewPage"));
});
});
var context = SetupContext(
@ -433,13 +421,11 @@ namespace Microsoft.AspNet.Security.Test
public async Task RolePolicyCanBlockNoRole()
{
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequiresRole("Admin", "Users");
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => policy.RequiresRole("Admin", "Users"));
});
});
var context = SetupContext(
@ -462,10 +448,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder();
options.AddPolicy("Basic", policy.Build());
options.AddPolicy("Basic", policy => { });
});
});
var context = SetupContext(
@ -489,10 +474,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
options.AddPolicy("Any", policy.Build());
options.AddPolicy("Any", policy => policy.RequireAuthenticatedUser());
});
});
var context = SetupContext(
@ -516,10 +500,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
options.AddPolicy("Any", policy.Build());
options.AddPolicy("Any", policy => policy.RequireAuthenticatedUser());
});
});
var context = SetupContext(new ClaimsIdentity());
@ -546,11 +529,9 @@ namespace Microsoft.AspNet.Security.Test
// Arrange
var authorizationService = BuildAuthorizationService(services =>
{
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder();
policy.Requirements.Add(new CustomRequirement());
options.AddPolicy("Custom", policy.Build());
options.AddPolicy("Custom", policy => policy.Requirements.Add(new CustomRequirement()));
});
});
var context = SetupContext();
@ -562,7 +543,6 @@ namespace Microsoft.AspNet.Security.Test
Assert.False(allowed);
}
[Fact]
public async Task CustomReqWithHandlerSucceeds()
{
@ -570,11 +550,9 @@ namespace Microsoft.AspNet.Security.Test
var authorizationService = BuildAuthorizationService(services =>
{
services.AddTransient<IAuthorizationHandler, CustomHandler>();
services.Configure<AuthorizationOptions>(options =>
services.ConfigureAuthorization(options =>
{
var policy = new AuthorizationPolicyBuilder();
policy.Requirements.Add(new CustomRequirement());
options.AddPolicy("Custom", policy.Build());
options.AddPolicy("Custom", policy => policy.Requirements.Add(new CustomRequirement()));
});
});
var context = SetupContext();
@ -586,5 +564,120 @@ namespace Microsoft.AspNet.Security.Test
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);
}
}
}