Initial iteration of new Authorization Service

This commit is contained in:
Hao Kung 2015-01-15 23:37:35 -08:00
parent f7c502a9e6
commit 4a635835af
20 changed files with 857 additions and 521 deletions

View File

@ -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);
}
}
}

View File

@ -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);
//}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -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)
)
);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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 { }
}

View File

@ -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);
}
}

View File

@ -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
{
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;}
}
}

View File

@ -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"; }
}
}
}