Support custom name and role claims
This commit is contained in:
parent
ce48c1fc7d
commit
434d158c76
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
public static AuthorizationPolicy Combine([NotNull] AuthorizationOptions options, [NotNull] IEnumerable<AuthorizeAttribute> attributes)
|
||||
{
|
||||
var policyBuilder = new AuthorizationPolicyBuilder();
|
||||
bool any = false;
|
||||
var any = false;
|
||||
foreach (var authorizeAttribute in attributes.OfType<AuthorizeAttribute>())
|
||||
{
|
||||
any = true;
|
||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
policyBuilder.RequireRole(rolesSplit);
|
||||
requireAnyAuthenticated = false;
|
||||
}
|
||||
string[] authTypesSplit = authorizeAttribute.ActiveAuthenticationSchemes?.Split(',');
|
||||
var authTypesSplit = authorizeAttribute.ActiveAuthenticationSchemes?.Split(',');
|
||||
if (authTypesSplit != null && authTypesSplit.Any())
|
||||
{
|
||||
foreach (var authType in authTypesSplit)
|
||||
|
|
|
|||
|
|
@ -55,21 +55,13 @@ namespace Microsoft.AspNet.Authorization
|
|||
|
||||
public AuthorizationPolicyBuilder RequireClaim([NotNull] string claimType, IEnumerable<string> requiredValues)
|
||||
{
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement
|
||||
{
|
||||
ClaimType = claimType,
|
||||
AllowedValues = requiredValues
|
||||
});
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement(claimType, requiredValues));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequireClaim([NotNull] string claimType)
|
||||
{
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement
|
||||
{
|
||||
ClaimType = claimType,
|
||||
AllowedValues = null
|
||||
});
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues: null));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -80,13 +72,13 @@ namespace Microsoft.AspNet.Authorization
|
|||
|
||||
public AuthorizationPolicyBuilder RequireRole([NotNull] IEnumerable<string> roles)
|
||||
{
|
||||
RequireClaim(ClaimTypes.Role, roles);
|
||||
Requirements.Add(new RolesAuthorizationRequirement(roles));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequireUserName([NotNull] string userName)
|
||||
{
|
||||
RequireClaim(ClaimTypes.Name, userName);
|
||||
Requirements.Add(new NameAuthorizationRequirement(userName));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@ namespace Microsoft.AspNet.Authorization
|
|||
/// <returns><value>true</value> when the user fulfills the policy, <value>false</value> otherwise.</returns>
|
||||
public static Task<bool> AuthorizeAsync([NotNull] this IAuthorizationService service, ClaimsPrincipal user, object resource, [NotNull] AuthorizationPolicy policy)
|
||||
{
|
||||
// TODO RENABLE
|
||||
//if (policy.ActiveAuthenticationSchemes != null && policy.ActiveAuthenticationSchemes.Any() && user != null)
|
||||
//{
|
||||
// // Filter the user to only contain the active authentication types
|
||||
// user = new ClaimsPrincipal(user.Identities.Where(i => policy.ActiveAuthenticationSchemes.Contains(i.AuthenticationScheme)));
|
||||
//}
|
||||
return service.AuthorizeAsync(user, resource, policy.Requirements.ToArray());
|
||||
}
|
||||
|
||||
|
|
@ -39,12 +33,6 @@ namespace Microsoft.AspNet.Authorization
|
|||
/// <returns><value>true</value> when the user fulfills the policy, <value>false</value> otherwise.</returns>
|
||||
public static bool Authorize([NotNull] this IAuthorizationService service, ClaimsPrincipal user, object resource, [NotNull] AuthorizationPolicy policy)
|
||||
{
|
||||
// TODO: REeanble
|
||||
//if (policy.ActiveAuthenticationSchemes != null && policy.ActiveAuthenticationSchemes.Any() && user != null)
|
||||
//{
|
||||
// // Filter the user to only contain the active authentication types
|
||||
// user = new ClaimsPrincipal(user.Identities.Where(i => policy.ActiveAuthenticationSchemes.Contains(i.AuthenticationScheme)));
|
||||
//}
|
||||
return service.Authorize(user, resource, policy.Requirements.ToArray());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
// Must contain a claim with the specified name, and at least one of the required values
|
||||
// If AllowedValues is null or empty, that means any claim is valid
|
||||
public class ClaimsAuthorizationRequirement : IAuthorizationRequirement
|
||||
public class ClaimsAuthorizationRequirement : AuthorizationHandler<ClaimsAuthorizationRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public string ClaimType { get; set; }
|
||||
public IEnumerable<string> AllowedValues { get; set; }
|
||||
public ClaimsAuthorizationRequirement([NotNull] string claimType, IEnumerable<string> allowedValues)
|
||||
{
|
||||
ClaimType = claimType;
|
||||
AllowedValues = allowedValues;
|
||||
}
|
||||
|
||||
public string ClaimType { get; }
|
||||
public IEnumerable<string> AllowedValues { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, ClaimsAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
var found = false;
|
||||
if (requirement.AllowedValues == null || !requirement.AllowedValues.Any())
|
||||
{
|
||||
found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase)
|
||||
&& requirement.AllowedValues.Contains(c.Value, StringComparer.Ordinal));
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Authorization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
public class DenyAnonymousAuthorizationRequirement : IAuthorizationRequirement { }
|
||||
public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public override void Handle(AuthorizationContext context, DenyAnonymousAuthorizationRequirement requirement)
|
||||
{
|
||||
var user = context.User;
|
||||
var userIsAnonymous =
|
||||
user?.Identity == null ||
|
||||
!user.Identities.Any(i => i.IsAuthenticated);
|
||||
if (!userIsAnonymous)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Requirement that ensures a specific Name
|
||||
/// </summary>
|
||||
public class NameAuthorizationRequirement : AuthorizationHandler<NameAuthorizationRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public NameAuthorizationRequirement([NotNull] string requiredName)
|
||||
{
|
||||
RequiredName = requiredName;
|
||||
}
|
||||
|
||||
public string RequiredName { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, NameAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
// REVIEW: Do we need to do normalization? casing/loc?
|
||||
if (context.User.Identities.Any(i => string.Equals(i.Name, requirement.RequiredName)))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,54 +10,6 @@ namespace Microsoft.AspNet.Authorization
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.Authorization.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key.
|
||||
/// </summary>
|
||||
internal static string Exception_DefaultDpapiRequiresAppNameKey
|
||||
{
|
||||
get { return GetString("Exception_DefaultDpapiRequiresAppNameKey"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key.
|
||||
/// </summary>
|
||||
internal static string FormatException_DefaultDpapiRequiresAppNameKey()
|
||||
{
|
||||
return GetString("Exception_DefaultDpapiRequiresAppNameKey");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state passed to UnhookAuthentication may only be the return value from HookAuthentication.
|
||||
/// </summary>
|
||||
internal static string Exception_UnhookAuthenticationStateType
|
||||
{
|
||||
get { return GetString("Exception_UnhookAuthenticationStateType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state passed to UnhookAuthentication may only be the return value from HookAuthentication.
|
||||
/// </summary>
|
||||
internal static string FormatException_UnhookAuthenticationStateType()
|
||||
{
|
||||
return GetString("Exception_UnhookAuthenticationStateType");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AuthenticationTokenProvider's required synchronous events have not been registered.
|
||||
/// </summary>
|
||||
internal static string Exception_AuthenticationTokenDoesNotProvideSyncMethods
|
||||
{
|
||||
get { return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AuthenticationTokenProvider's required synchronous events have not been registered.
|
||||
/// </summary>
|
||||
internal static string FormatException_AuthenticationTokenDoesNotProvideSyncMethods()
|
||||
{
|
||||
return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AuthorizationPolicy named: '{0}' was not found.
|
||||
/// </summary>
|
||||
|
|
@ -74,6 +26,22 @@ namespace Microsoft.AspNet.Authorization
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("Exception_AuthorizationPolicyNotFound"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// At least one role must be specified.
|
||||
/// </summary>
|
||||
internal static string Exception_RoleRequirementEmpty
|
||||
{
|
||||
get { return GetString("Exception_RoleRequirementEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// At least one role must be specified.
|
||||
/// </summary>
|
||||
internal static string FormatException_RoleRequirementEmpty()
|
||||
{
|
||||
return GetString("Exception_RoleRequirementEmpty");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -117,16 +117,10 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Exception_DefaultDpapiRequiresAppNameKey" xml:space="preserve">
|
||||
<value>The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key.</value>
|
||||
</data>
|
||||
<data name="Exception_UnhookAuthenticationStateType" xml:space="preserve">
|
||||
<value>The state passed to UnhookAuthentication may only be the return value from HookAuthentication.</value>
|
||||
</data>
|
||||
<data name="Exception_AuthenticationTokenDoesNotProvideSyncMethods" xml:space="preserve">
|
||||
<value>The AuthenticationTokenProvider's required synchronous events have not been registered.</value>
|
||||
</data>
|
||||
<data name="Exception_AuthorizationPolicyNotFound" xml:space="preserve">
|
||||
<value>The AuthorizationPolicy named: '{0}' was not found.</value>
|
||||
</data>
|
||||
<data name="Exception_RoleRequirementEmpty" xml:space="preserve">
|
||||
<value>At least one role must be specified.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) .NET Foundation. 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;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
// Must belong to with one of specified roles
|
||||
// If AllowedRoles is null or empty, that means any role is valid
|
||||
public class RolesAuthorizationRequirement : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public RolesAuthorizationRequirement([NotNull] IEnumerable<string> allowedRoles)
|
||||
{
|
||||
if (allowedRoles.Count() == 0)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.Exception_RoleRequirementEmpty);
|
||||
}
|
||||
AllowedRoles = allowedRoles;
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllowedRoles { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, RolesAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
bool found = false;
|
||||
if (requirement.AllowedRoles == null || !requirement.AllowedRoles.Any())
|
||||
{
|
||||
// Review: What do we want to do here? No roles requested is auto success?
|
||||
}
|
||||
else
|
||||
{
|
||||
found = requirement.AllowedRoles.Any(r => context.User.IsInRole(r));
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -23,8 +23,6 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
{
|
||||
services.AddOptions();
|
||||
services.TryAdd(ServiceDescriptor.Transient<IAuthorizationService, DefaultAuthorizationService>());
|
||||
services.AddTransient<IAuthorizationHandler, ClaimsAuthorizationHandler>();
|
||||
services.AddTransient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>();
|
||||
services.AddTransient<IAuthorizationHandler, PassThroughAuthorizationHandler>();
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. 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 Microsoft.AspNet.Authorization;
|
||||
using Xunit;
|
||||
|
|
@ -9,6 +10,12 @@ namespace Microsoft.AspNet.Authroization.Test
|
|||
{
|
||||
public class AuthorizationPolicyFacts
|
||||
{
|
||||
[Fact]
|
||||
public void RequireRoleThrowsIfEmpty()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => new AuthorizationPolicyBuilder().RequireRole());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCombineAuthorizeAttributes()
|
||||
{
|
||||
|
|
@ -32,7 +39,8 @@ namespace Microsoft.AspNet.Authroization.Test
|
|||
Assert.True(combined.ActiveAuthenticationSchemes.Contains("roles"));
|
||||
Assert.Equal(4, combined.Requirements.Count());
|
||||
Assert.True(combined.Requirements.Any(r => r is DenyAnonymousAuthorizationRequirement));
|
||||
Assert.Equal(3, combined.Requirements.OfType<ClaimsAuthorizationRequirement>().Count());
|
||||
Assert.Equal(2, combined.Requirements.OfType<ClaimsAuthorizationRequirement>().Count());
|
||||
Assert.Equal(1, combined.Requirements.OfType<RolesAuthorizationRequirement>().Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -504,6 +505,50 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanRequireUserNameWithDiffClaimType()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.ConfigureAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("Hao", policy => policy.RequireUserName("Hao"));
|
||||
});
|
||||
});
|
||||
var identity = new ClaimsIdentity("AuthType", "Name", "Role");
|
||||
identity.AddClaim(new Claim("Name", "Hao"));
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(user, null, "Hao");
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanRequireRoleWithDiffClaimType()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.ConfigureAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("Hao", policy => policy.RequireRole("Hao"));
|
||||
});
|
||||
});
|
||||
var identity = new ClaimsIdentity("AuthType", "Name", "Role");
|
||||
identity.AddClaim(new Claim("Role", "Hao"));
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(user, null, "Hao");
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanApproveAnyAuthenticatedUser()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue