Initial iteration of new Authorization Service
This commit is contained in:
parent
f7c502a9e6
commit
4a635835af
|
|
@ -0,0 +1,61 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains authorization information used by <see cref="IAuthorizationPolicyHandler"/>.
|
||||
/// </summary>
|
||||
public class AuthorizationContext
|
||||
{
|
||||
private HashSet<IAuthorizationRequirement> _pendingRequirements = new HashSet<IAuthorizationRequirement>();
|
||||
private bool _failCalled;
|
||||
private bool _succeedCalled;
|
||||
|
||||
public AuthorizationContext(
|
||||
[NotNull] AuthorizationPolicy policy,
|
||||
HttpContext context,
|
||||
object resource)
|
||||
{
|
||||
Policy = policy;
|
||||
Context = context;
|
||||
Resource = resource;
|
||||
foreach (var req in Policy.Requirements)
|
||||
{
|
||||
_pendingRequirements.Add(req);
|
||||
}
|
||||
}
|
||||
|
||||
public AuthorizationPolicy Policy { get; private set; }
|
||||
public ClaimsPrincipal User { get { return Context.User; } }
|
||||
public HttpContext Context { get; private set; }
|
||||
public object Resource { get; private set; }
|
||||
|
||||
public IEnumerable<IAuthorizationRequirement> PendingRequirements { get { return _pendingRequirements; } }
|
||||
|
||||
public bool HasFailed { get { return _failCalled; } }
|
||||
|
||||
public bool HasSucceeded {
|
||||
get
|
||||
{
|
||||
return !_failCalled && _succeedCalled && !PendingRequirements.Any();
|
||||
}
|
||||
}
|
||||
|
||||
public void Fail()
|
||||
{
|
||||
_failCalled = true;
|
||||
}
|
||||
|
||||
public void Succeed(IAuthorizationRequirement requirement)
|
||||
{
|
||||
_succeedCalled = true;
|
||||
_pendingRequirements.Remove(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
// Music store use case
|
||||
|
||||
// await AuthorizeAsync<Album>(user, "Edit", albumInstance);
|
||||
|
||||
// No policy name needed because this is auto based on resource (operation is the policy name)
|
||||
//RegisterOperation which auto generates the policy for Authorize<T>
|
||||
//bool AuthorizeAsync<TResource>(ClaimsPrincipal, string operation, TResource instance)
|
||||
//bool AuthorizeAsync<TResource>(IAuthorization, ClaimsPrincipal, string operation, TResource instance)
|
||||
public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler
|
||||
where TRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public async Task HandleAsync(AuthorizationContext context)
|
||||
{
|
||||
foreach (var req in context.Policy.Requirements.OfType<TRequirement>())
|
||||
{
|
||||
if (await CheckAsync(context, req))
|
||||
{
|
||||
context.Succeed(req);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Task<bool> CheckAsync(AuthorizationContext context, TRequirement requirement);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
//public abstract class AuthorizationHandler<TRequirement, TResource> : AuthorizationHandler<TRequirement>
|
||||
// where TResource : class
|
||||
// where TRequirement : IAuthorizationRequirement
|
||||
//{
|
||||
// public override Task HandleAsync(AuthorizationContext context)
|
||||
// {
|
||||
// var resource = context.Resource as TResource;
|
||||
// if (resource != null)
|
||||
// {
|
||||
// return HandleAsync(context, resource);
|
||||
// }
|
||||
|
||||
// return Task.FromResult(0);
|
||||
|
||||
// }
|
||||
|
||||
// public abstract Task HandleAsync(AuthorizationContext context, TResource resource);
|
||||
//}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class AuthorizationOptions
|
||||
{
|
||||
// TODO: make this case insensitive
|
||||
private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>();
|
||||
|
||||
public void AddPolicy([NotNull] string name, [NotNull] AuthorizationPolicy policy)
|
||||
{
|
||||
PolicyMap[name] = policy;
|
||||
}
|
||||
|
||||
public AuthorizationPolicy GetPolicy([NotNull] string name)
|
||||
{
|
||||
return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,31 +2,18 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides a base implementation for <see cref="IAuthorizationPolicy" />
|
||||
/// </summary>
|
||||
public abstract class AuthorizationPolicy : IAuthorizationPolicy
|
||||
public class AuthorizationPolicy
|
||||
{
|
||||
public int Order { get; set; }
|
||||
|
||||
public virtual Task ApplyingAsync(AuthorizationPolicyContext context)
|
||||
public AuthorizationPolicy(IEnumerable<IAuthorizationRequirement> requirements, IEnumerable<string> activeAuthenticationTypes)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
Requirements = requirements;
|
||||
ActiveAuthenticationTypes = activeAuthenticationTypes;
|
||||
}
|
||||
|
||||
public virtual Task ApplyAsync(AuthorizationPolicyContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public virtual Task AppliedAsync(AuthorizationPolicyContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public IEnumerable<IAuthorizationRequirement> Requirements { get; private set; }
|
||||
public IEnumerable<string> ActiveAuthenticationTypes { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class AuthorizationPolicyBuilder
|
||||
{
|
||||
public AuthorizationPolicyBuilder(params string[] activeAuthenticationTypes)
|
||||
{
|
||||
foreach (var authType in activeAuthenticationTypes) {
|
||||
ActiveAuthenticationTypes.Add(authType);
|
||||
}
|
||||
}
|
||||
|
||||
public IList<IAuthorizationRequirement> Requirements { get; set; } = new List<IAuthorizationRequirement>();
|
||||
public IList<string> ActiveAuthenticationTypes { get; set; } = new List<string>();
|
||||
|
||||
public AuthorizationPolicyBuilder RequiresClaim([NotNull] string claimType, params string[] requiredValues)
|
||||
{
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement
|
||||
{
|
||||
ClaimType = claimType,
|
||||
AllowedValues = requiredValues
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequiresClaim([NotNull] string claimType)
|
||||
{
|
||||
Requirements.Add(new ClaimsAuthorizationRequirement
|
||||
{
|
||||
ClaimType = claimType,
|
||||
AllowedValues = null
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequiresRole([NotNull] params string[] roles)
|
||||
{
|
||||
RequiresClaim(ClaimTypes.Role, roles);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequireAuthenticatedUser()
|
||||
{
|
||||
Requirements.Add(new DenyAnonymousAuthorizationRequirement());
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicy Build()
|
||||
{
|
||||
return new AuthorizationPolicy(Requirements, ActiveAuthenticationTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +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.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains authorization information used by <see cref="IAuthorizationPolicy"/>.
|
||||
/// </summary>
|
||||
public class AuthorizationPolicyContext
|
||||
{
|
||||
public AuthorizationPolicyContext(IEnumerable<Claim> claims, ClaimsPrincipal user, object resource )
|
||||
{
|
||||
Claims = (claims ?? Enumerable.Empty<Claim>()).ToList();
|
||||
User = user;
|
||||
Resource = resource;
|
||||
|
||||
// user claims are copied to a new and mutable list
|
||||
UserClaims = user != null
|
||||
? user.Claims.ToList()
|
||||
: new List<Claim>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of claims the <see cref="IAuthorizationService"/> is checking.
|
||||
/// </summary>
|
||||
public IList<Claim> Claims { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user to check the claims against.
|
||||
/// </summary>
|
||||
public ClaimsPrincipal User { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The claims of the user.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This list can be modified by policies for retries.
|
||||
/// </remarks>
|
||||
public IList<Claim> UserClaims { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional resource associated to the check.
|
||||
/// </summary>
|
||||
public object Resource { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set whether the permission will be granted to the user.
|
||||
/// </summary>
|
||||
public bool Authorized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set to <value>true</value>, the authorization check will be processed again.
|
||||
/// </summary>
|
||||
public bool Retry { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public static class AuthorizationServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static Task<bool> AuthorizeAsync([NotNull] this IAuthorizationService service, Claim claim, ClaimsPrincipal user)
|
||||
{
|
||||
return service.AuthorizeAsync(new Claim[] { claim }, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static bool Authorize([NotNull] this IAuthorizationService service, Claim claim, ClaimsPrincipal user)
|
||||
{
|
||||
return service.Authorize(new Claim[] { claim }, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims for a specific context obj.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <param name="resource">The resource the claims should be check with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static Task<bool> AuthorizeAsync([NotNull] this IAuthorizationService service, Claim claim, ClaimsPrincipal user, object resource)
|
||||
{
|
||||
return service.AuthorizeAsync(new Claim[] { claim }, user, resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims for a specific context obj.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claimsto check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <param name="resource">The resource the claims should be check with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static bool Authorize([NotNull] this IAuthorizationService service, Claim claim, ClaimsPrincipal user, object resource)
|
||||
{
|
||||
return service.Authorize(new Claim[] { claim }, user, resource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims.
|
||||
/// </summary>
|
||||
/// <param name="claims">The claims to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static Task<bool> AuthorizeAsync([NotNull] this IAuthorizationService service, IEnumerable<Claim> claims, ClaimsPrincipal user)
|
||||
{
|
||||
return service.AuthorizeAsync(claims, user, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims.
|
||||
/// </summary>
|
||||
/// <param name="claims">The claims to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
public static bool Authorize([NotNull] this IAuthorizationService service, IEnumerable<Claim> claims, ClaimsPrincipal user)
|
||||
{
|
||||
return service.Authorize(claims, user, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class ClaimsAuthorizationHandler : AuthorizationHandler<ClaimsAuthorizationRequirement>
|
||||
{
|
||||
public override Task<bool> CheckAsync(AuthorizationContext context, ClaimsAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.Context.User == null)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
if (requirement.AllowedValues == null || !requirement.AllowedValues.Any())
|
||||
{
|
||||
found = context.Context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
found = context.Context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase)
|
||||
&& requirement.AllowedValues.Contains(c.Value, StringComparer.Ordinal));
|
||||
}
|
||||
return Task.FromResult(found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
// 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 string ClaimType { get; set; }
|
||||
public IEnumerable<string> AllowedValues { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +1,67 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class DefaultAuthorizationService : IAuthorizationService
|
||||
{
|
||||
private readonly IList<IAuthorizationPolicy> _policies;
|
||||
public int MaxRetries = 99;
|
||||
private readonly IList<IAuthorizationHandler> _handlers;
|
||||
private readonly AuthorizationOptions _options;
|
||||
|
||||
public DefaultAuthorizationService(IEnumerable<IAuthorizationPolicy> policies)
|
||||
public DefaultAuthorizationService(IOptions<AuthorizationOptions> options, IEnumerable<IAuthorizationHandler> handlers)
|
||||
{
|
||||
if (policies == null)
|
||||
{
|
||||
_policies = Enumerable.Empty<IAuthorizationPolicy>().ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
_policies = policies.OrderBy(x => x.Order).ToArray();
|
||||
}
|
||||
_handlers = handlers.ToArray();
|
||||
_options = options.Options;
|
||||
}
|
||||
|
||||
public async Task<bool> AuthorizeAsync(IEnumerable<Claim> claims, ClaimsPrincipal user, object resource)
|
||||
public Task<bool> AuthorizeAsync([NotNull] string policyName, HttpContext context, object resource = null)
|
||||
{
|
||||
var context = new AuthorizationPolicyContext(claims, user, resource);
|
||||
|
||||
foreach (var policy in _policies)
|
||||
var policy = _options.GetPolicy(policyName);
|
||||
if (policy == null)
|
||||
{
|
||||
await policy.ApplyingAsync(context);
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
return AuthorizeAsync(policy, context, resource);
|
||||
}
|
||||
|
||||
// we only apply the policies for a limited number of times to prevent
|
||||
// infinite loops
|
||||
|
||||
int retries;
|
||||
for (retries = 0; retries < MaxRetries; retries++)
|
||||
public async Task<bool> AuthorizeAsync([NotNull] AuthorizationPolicy policy, [NotNull] HttpContext context, object resource = null)
|
||||
{
|
||||
var user = context.User;
|
||||
try
|
||||
{
|
||||
// we don't need to check for owned claims if the permission is already granted
|
||||
if (!context.Authorized)
|
||||
// Generate the user identities if policy specified the AuthTypes
|
||||
if (policy.ActiveAuthenticationTypes != null && policy.ActiveAuthenticationTypes.Any() )
|
||||
{
|
||||
if (context.User != null)
|
||||
var principal = new ClaimsPrincipal();
|
||||
|
||||
var results = await context.AuthenticateAsync(policy.ActiveAuthenticationTypes);
|
||||
// REVIEW: re requesting the identities fails for MVC currently, so we only request if not found
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (ClaimsMatch(context.Claims, context.UserClaims))
|
||||
{
|
||||
context.Authorized = true;
|
||||
}
|
||||
principal.AddIdentity(result.Identity);
|
||||
}
|
||||
context.User = principal;
|
||||
}
|
||||
|
||||
// reset the retry flag
|
||||
context.Retry = false;
|
||||
var authContext = new AuthorizationContext(policy, context, resource);
|
||||
|
||||
// give a chance for policies to change claims or the grant
|
||||
foreach (var policy in _policies)
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
await policy.ApplyAsync(context);
|
||||
}
|
||||
|
||||
// if no policies have changed the context, stop checking
|
||||
if (!context.Retry)
|
||||
{
|
||||
break;
|
||||
await handler.HandleAsync(authContext);
|
||||
}
|
||||
return authContext.HasSucceeded;
|
||||
}
|
||||
|
||||
if (retries == MaxRetries)
|
||||
finally
|
||||
{
|
||||
throw new InvalidOperationException("Too many authorization retries.");
|
||||
context.User = user;
|
||||
}
|
||||
|
||||
foreach (var policy in _policies)
|
||||
{
|
||||
await policy.AppliedAsync(context);
|
||||
}
|
||||
|
||||
return context.Authorized;
|
||||
}
|
||||
|
||||
public bool Authorize(IEnumerable<Claim> claims, ClaimsPrincipal user, object resource)
|
||||
{
|
||||
return AuthorizeAsync(claims, user, resource).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private bool ClaimsMatch([NotNull] IEnumerable<Claim> x, [NotNull] IEnumerable<Claim> y)
|
||||
{
|
||||
return x.Any(claim =>
|
||||
y.Any(userClaim =>
|
||||
string.Equals(claim.Type, userClaim.Type, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(claim.Value, userClaim.Value, StringComparison.Ordinal)
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class DenyAnonymousAuthorizationHandler : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>
|
||||
{
|
||||
public override Task<bool> CheckAsync(AuthorizationContext context, DenyAnonymousAuthorizationRequirement requirement)
|
||||
{
|
||||
var user = context.User;
|
||||
var userIsAnonymous =
|
||||
user == null ||
|
||||
user.Identity == null ||
|
||||
!user.Identity.IsAuthenticated;
|
||||
return Task.FromResult(!userIsAnonymous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// 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 Microsoft.AspNet.Security
|
||||
{
|
||||
public class DenyAnonymousAuthorizationRequirement : IAuthorizationRequirement { }
|
||||
}
|
||||
|
|
@ -5,11 +5,9 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public interface IAuthorizationPolicy
|
||||
public interface IAuthorizationHandler
|
||||
{
|
||||
int Order { get; set; }
|
||||
Task ApplyingAsync(AuthorizationPolicyContext context);
|
||||
Task ApplyAsync(AuthorizationPolicyContext context);
|
||||
Task AppliedAsync(AuthorizationPolicyContext context);
|
||||
Task HandleAsync(AuthorizationContext context);
|
||||
//void Handle(AuthorizationContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public interface IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +1,32 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks claims based permissions for a user.
|
||||
/// Checks policy based permissions for a user
|
||||
/// </summary>
|
||||
public interface IAuthorizationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims for a specific context obj.
|
||||
/// Checks if a user meets a specific authorization policy
|
||||
/// </summary>
|
||||
/// <param name="claims">The claims to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <param name="resource">The resource the claims should be check with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
Task<bool> AuthorizeAsync(IEnumerable<Claim> claims, ClaimsPrincipal user, object resource);
|
||||
/// <param name="policy">The policy to check against a specific context.</param>
|
||||
/// <param name="context">The HttpContext to check the policy against.</param>
|
||||
/// <param name="resource">The resource the policy should be checked with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills the policy, <value>false</value> otherwise.</returns>
|
||||
Task<bool> AuthorizeAsync(string policyName, HttpContext context, object resource = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a user has specific claims for a specific context obj.
|
||||
/// Checks if a user meets a specific authorization policy
|
||||
/// </summary>
|
||||
/// <param name="claims">The claims to check against a specific user.</param>
|
||||
/// <param name="user">The user to check claims against.</param>
|
||||
/// <param name="resource">The resource the claims should be check with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills one of the claims, <value>false</value> otherwise.</returns>
|
||||
bool Authorize(IEnumerable<Claim> claims, ClaimsPrincipal user, object resource);
|
||||
|
||||
/// <param name="policy">The policy to check against a specific context.</param>
|
||||
/// <param name="context">The HttpContext to check the policy against.</param>
|
||||
/// <param name="resource">The resource the policy should be checked with.</param>
|
||||
/// <returns><value>true</value> when the user fulfills the policy, <value>false</value> otherwise.</returns>
|
||||
Task<bool> AuthorizeAsync(AuthorizationPolicy policy, HttpContext context, object resource = null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class PassThroughAuthorizationHandler : IAuthorizationHandler
|
||||
{
|
||||
public async Task HandleAsync(AuthorizationContext context)
|
||||
{
|
||||
foreach (var handler in context.Policy.Requirements.OfType<IAuthorizationHandler>())
|
||||
{
|
||||
await handler.HandleAsync(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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 Microsoft.AspNet.Security;
|
||||
using Microsoft.Framework.ConfigurationModel;
|
||||
|
||||
namespace Microsoft.Framework.DependencyInjection
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection ConfigureAuthorization([NotNull] this IServiceCollection services, [NotNull] Action<AuthorizationOptions> configure)
|
||||
{
|
||||
return services.Configure(configure);
|
||||
}
|
||||
|
||||
// Review: Need UseDefaultSubkey parameter?
|
||||
public static IServiceCollection AddAuthorization([NotNull] this IServiceCollection services, IConfiguration config = null, Action<AuthorizationOptions> configureOptions = null)
|
||||
{
|
||||
var describe = new ServiceDescriber(config);
|
||||
services.AddOptions(config);
|
||||
services.TryAdd(describe.Transient<IAuthorizationService, DefaultAuthorizationService>());
|
||||
services.Add(describe.Transient<IAuthorizationHandler, ClaimsAuthorizationHandler>());
|
||||
services.Add(describe.Transient<IAuthorizationHandler, DenyAnonymousAuthorizationHandler>());
|
||||
if (configureOptions != null)
|
||||
{
|
||||
services.Configure(configureOptions);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,307 +3,588 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Security;
|
||||
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;
|
||||
|
||||
namespace Microsoft.AspNet.Security.Test
|
||||
{
|
||||
public class DefaultAuthorizationServiceTests
|
||||
{
|
||||
private IAuthorizationService BuildAuthorizationService(Action<IServiceCollection> setupServices = null)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddAuthorization();
|
||||
if (setupServices != null)
|
||||
{
|
||||
setupServices(services);
|
||||
}
|
||||
return services.BuildServiceProvider().GetRequiredService<IAuthorizationService>();
|
||||
}
|
||||
|
||||
private Mock<HttpContext> SetupContext(params ClaimsIdentity[] ids)
|
||||
{
|
||||
var context = new Mock<HttpContext>();
|
||||
context.SetupProperty(c => c.User);
|
||||
var user = new ClaimsPrincipal();
|
||||
user.AddIdentities(ids);
|
||||
context.Object.User = user;
|
||||
if (ids != null)
|
||||
{
|
||||
var results = new List<AuthenticationResult>();
|
||||
foreach (var id in ids)
|
||||
{
|
||||
results.Add(new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription()));
|
||||
}
|
||||
context.Setup(c => c.AuthenticateAsync(It.IsAny<IEnumerable<string>>())).ReturnsAsync(results).Verifiable();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldAllowIfClaimIsPresent()
|
||||
public async Task Authorize_ShouldAllowIfClaimIsPresent()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
new ClaimsIdentity( new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic")
|
||||
);
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
options.AddPolicy("Basic", new AuthorizationPolicyBuilder()
|
||||
.RequiresClaim("Permission", "CanViewPage")
|
||||
.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldAllowIfClaimIsAmongValues()
|
||||
public async Task Authorize_ShouldAllowIfClaimIsPresentWithSpecifiedAuthType()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(new ClaimsIdentity(new Claim[] { new Claim("Permission", "CanViewPage") }, "Basic"));
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authorize_ShouldAllowIfClaimIsAmongValues()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewPage"),
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewPage"),
|
||||
new Claim("Permission", "CanViewAnything")
|
||||
},
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldNotAllowIfClaimTypeIsNotPresent()
|
||||
public async Task Authorize_ShouldFailWhenAllRequirementsNotHandled()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("SomethingElse", "CanViewPage"),
|
||||
new Claim[] {
|
||||
new Claim("SomethingElse", "CanViewPage"),
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldNotAllowIfClaimValueIsNotPresent()
|
||||
public async Task Authorize_ShouldNotAllowIfClaimTypeIsNotPresent()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage", "CanViewAnything");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewComment"),
|
||||
new Claim[] {
|
||||
new Claim("SomethingElse", "CanViewPage"),
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldNotAllowIfNoClaims()
|
||||
public async Task Authorize_ShouldNotAllowIfClaimValueIsNotPresent()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewComment"),
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authorize_ShouldNotAllowIfNoClaims()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[0],
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldNotAllowIfUserIsNull()
|
||||
public async Task Authorize_ShouldNotAllowIfUserIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
ClaimsPrincipal user = null;
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext();
|
||||
context.Object.User = null;
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldNotAllowIfUserIsNotAuthenticated()
|
||||
public async Task Authorize_ShouldNotAllowIfNotCorrectAuthType()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = new DefaultAuthorizationService(Enumerable.Empty<IAuthorizationPolicy>());
|
||||
var user = new ClaimsPrincipal(
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder("Basic").RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(new ClaimsIdentity());
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authorize_ShouldAllowWithNoAuthType()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewComment"),
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewPage"),
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authorize_ShouldNotAllowIfUnknownPolicy()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim("Permission", "CanViewComment"),
|
||||
},
|
||||
null)
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim[] { new Claim("Permission", "CanViewPage") }, user);
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldApplyPoliciesInOrder()
|
||||
public async Task Authorize_CustomRolePolicy()
|
||||
{
|
||||
// Arrange
|
||||
string result = "";
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
Order = 20,
|
||||
ApplyingAsyncAction = (context) => { result += "20"; }
|
||||
},
|
||||
new FakePolicy() {
|
||||
Order = -1,
|
||||
ApplyingAsyncAction = (context) => { result += "-1"; }
|
||||
},
|
||||
new FakePolicy() {
|
||||
Order = 30,
|
||||
ApplyingAsyncAction = (context) => { result += "30"; }
|
||||
},
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(Enumerable.Empty<Claim>(), null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("-12030", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldInvokeApplyingApplyAppliedInOrder()
|
||||
{
|
||||
// Arrange
|
||||
string result = "";
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
Order = 20,
|
||||
ApplyingAsyncAction = (context) => { result += "Applying20"; },
|
||||
ApplyAsyncAction = (context) => { result += "Apply20"; },
|
||||
AppliedAsyncAction = (context) => { result += "Applied20"; }
|
||||
},
|
||||
new FakePolicy() {
|
||||
Order = -1,
|
||||
ApplyingAsyncAction = (context) => { result += "Applying-1"; },
|
||||
ApplyAsyncAction = (context) => { result += "Apply-1"; },
|
||||
AppliedAsyncAction = (context) => { result += "Applied-1"; }
|
||||
},
|
||||
new FakePolicy() {
|
||||
Order = 30,
|
||||
ApplyingAsyncAction = (context) => { result += "Applying30"; },
|
||||
ApplyAsyncAction = (context) => { result += "Apply30"; },
|
||||
AppliedAsyncAction = (context) => { result += "Applied30"; }
|
||||
},
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(Enumerable.Empty<Claim>(), null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Applying-1Applying20Applying30Apply-1Apply20Apply30Applied-1Applied20Applied30", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldConvertNullClaimsToEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
IList<Claim> claims = null;
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
Order = 20,
|
||||
ApplyingAsyncAction = (context) => { claims = context.Claims; }
|
||||
}
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(Enumerable.Empty<Claim>(), null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(claims);
|
||||
Assert.Equal(0, claims.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ShouldThrowWhenPoliciesDontStop()
|
||||
{
|
||||
// Arrange
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
ApplyAsyncAction = (context) => { context.Retry = true; }
|
||||
}
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
Exception ex = Assert.Throws<InvalidOperationException>(() => authorizationService.Authorize(Enumerable.Empty<Claim>(), null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_ApplyCanMutateCheckedClaims()
|
||||
{
|
||||
|
||||
// Arrange
|
||||
var user = new ClaimsPrincipal(
|
||||
new ClaimsIdentity( new Claim[] { new Claim("Permission", "CanDeleteComments") }, "Basic")
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresRole("Administrator")
|
||||
.RequiresClaim(ClaimTypes.Role, "User");
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim(ClaimTypes.Role, "User"),
|
||||
new Claim(ClaimTypes.Role, "Administrator")
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
ApplyAsyncAction = (context) => {
|
||||
// for instance, if user owns the comment
|
||||
if(!context.Claims.Any(claim => claim.Type == "Permission" && claim.Value == "CanDeleteComments"))
|
||||
{
|
||||
context.Claims.Add(new Claim("Permission", "CanDeleteComments"));
|
||||
context.Retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(Enumerable.Empty<Claim>(), user);
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Check_PoliciesCanMutateUsersClaims()
|
||||
public async Task Authorize_HasAnyClaimOfTypePolicy()
|
||||
{
|
||||
|
||||
// Arrange
|
||||
var user = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(new Claim[0], "Basic")
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim(ClaimTypes.Role);
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim(ClaimTypes.Role, ""),
|
||||
},
|
||||
"Basic")
|
||||
);
|
||||
|
||||
var policies = new IAuthorizationPolicy[] {
|
||||
new FakePolicy() {
|
||||
ApplyAsyncAction = (context) => {
|
||||
if (!context.Authorized)
|
||||
{
|
||||
context.UserClaims.Add(new Claim("Permission", "CanDeleteComments"));
|
||||
context.Retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var authorizationService = new DefaultAuthorizationService(policies);
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(new Claim("Permission", "CanDeleteComments"), user);
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Authorize_PolicyCanAuthenticationTypeWithNameClaim()
|
||||
{
|
||||
// Arrange
|
||||
var policy = new AuthorizationPolicyBuilder("AuthType").RequiresClaim(ClaimTypes.Name);
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, "Name") }, "AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RolePolicyCanRequireSingleRole()
|
||||
{
|
||||
// Arrange
|
||||
var policy = new AuthorizationPolicyBuilder("AuthType").RequiresRole("Admin");
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Role, "Admin") }, "AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RolePolicyCanRequireOneOfManyRoles()
|
||||
{
|
||||
// Arrange
|
||||
var policy = new AuthorizationPolicyBuilder("AuthType").RequiresRole("Admin", "Users");
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Role, "Users") }, "AuthType"));
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RolePolicyCanBlockWrongRole()
|
||||
{
|
||||
// Arrange
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresClaim("Permission", "CanViewPage");
|
||||
var authorizationService = BuildAuthorizationService();
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim(ClaimTypes.Role, "Nope"),
|
||||
},
|
||||
"AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync(policy.Build(), context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RolePolicyCanBlockNoRole()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequiresRole("Admin", "Users");
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
},
|
||||
"AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PolicyFailsWithNoRequirements()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder();
|
||||
options.AddPolicy("Basic", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim(ClaimTypes.Name, "Name"),
|
||||
},
|
||||
"AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Basic", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanApproveAnyAuthenticatedUser()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
|
||||
options.AddPolicy("Any", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(
|
||||
new ClaimsIdentity(
|
||||
new Claim[] {
|
||||
new Claim(ClaimTypes.Name, "Name"),
|
||||
},
|
||||
"AuthType")
|
||||
);
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Any", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBlockNonAuthenticatedUser()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser();
|
||||
options.AddPolicy("Any", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext(new ClaimsIdentity());
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Any", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
public class CustomRequirement : IAuthorizationRequirement { }
|
||||
public class CustomHandler : AuthorizationHandler<CustomRequirement>
|
||||
{
|
||||
public override Task<bool> CheckAsync(AuthorizationContext context, CustomRequirement requirement)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CustomReqWithNoHandlerFails()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder();
|
||||
policy.Requirements.Add(new CustomRequirement());
|
||||
options.AddPolicy("Custom", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext();
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Custom", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CustomReqWithHandlerSucceeds()
|
||||
{
|
||||
// Arrange
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.AddTransient<IAuthorizationHandler, CustomHandler>();
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder();
|
||||
policy.Requirements.Add(new CustomRequirement());
|
||||
options.AddPolicy("Custom", policy.Build());
|
||||
});
|
||||
});
|
||||
var context = SetupContext();
|
||||
|
||||
// Act
|
||||
var allowed = await authorizationService.AuthorizeAsync("Custom", context.Object);
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +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.Security;
|
||||
|
||||
namespace Microsoft.AspNet.Security.Test
|
||||
{
|
||||
public class FakePolicy : IAuthorizationPolicy
|
||||
{
|
||||
|
||||
public int Order { get; set; }
|
||||
|
||||
public Task ApplyingAsync(AuthorizationPolicyContext context)
|
||||
{
|
||||
if (ApplyingAsyncAction != null)
|
||||
{
|
||||
ApplyingAsyncAction(context);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task ApplyAsync(AuthorizationPolicyContext context)
|
||||
{
|
||||
if (ApplyAsyncAction != null)
|
||||
{
|
||||
ApplyAsyncAction(context);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
|
||||
}
|
||||
|
||||
public Task AppliedAsync(AuthorizationPolicyContext context)
|
||||
{
|
||||
if (AppliedAsyncAction != null)
|
||||
{
|
||||
AppliedAsyncAction(context);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Action<AuthorizationPolicyContext> ApplyingAsyncAction { get; set;}
|
||||
|
||||
public Action<AuthorizationPolicyContext> ApplyAsyncAction { get; set;}
|
||||
|
||||
public Action<AuthorizationPolicyContext> AppliedAsyncAction { get; set;}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +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.Runtime.Versioning;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.Security
|
||||
{
|
||||
public class TestApplicationEnvironment : IApplicationEnvironment
|
||||
{
|
||||
public string ApplicationBasePath
|
||||
{
|
||||
get { return Environment.CurrentDirectory; }
|
||||
}
|
||||
|
||||
public string ApplicationName
|
||||
{
|
||||
get { return "Test App environment"; }
|
||||
}
|
||||
|
||||
public string Configuration
|
||||
{
|
||||
get { return "Test"; }
|
||||
}
|
||||
|
||||
public FrameworkName RuntimeFramework
|
||||
{
|
||||
get { return new FrameworkName(".NETFramework", new Version(4, 5)); }
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get { return "1.0.0"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue