diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationHandlerContext.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationHandlerContext.cs
index 5dc57c278a..b6378e4073 100644
--- a/src/Microsoft.AspNetCore.Authorization/AuthorizationHandlerContext.cs
+++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationHandlerContext.cs
@@ -42,32 +42,32 @@ namespace Microsoft.AspNetCore.Authorization
///
/// The collection of all the for the current authorization action.
///
- public IEnumerable Requirements { get; }
+ public virtual IEnumerable Requirements { get; }
///
/// The representing the current user.
///
- public ClaimsPrincipal User { get; }
+ public virtual ClaimsPrincipal User { get; }
///
/// The optional resource to evaluate the against.
///
- public object Resource { get; }
+ public virtual object Resource { get; }
///
/// Gets the requirements that have not yet been marked as succeeded.
///
- public IEnumerable PendingRequirements { get { return _pendingRequirements; } }
+ public virtual IEnumerable PendingRequirements { get { return _pendingRequirements; } }
///
/// Flag indicating whether the current authorization processing has failed.
///
- public bool HasFailed { get { return _failCalled; } }
+ public virtual bool HasFailed { get { return _failCalled; } }
///
/// Flag indicating whether the current authorization processing has succeeded.
///
- public bool HasSucceeded
+ public virtual bool HasSucceeded
{
get
{
@@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Authorization
/// Called to indicate will
/// never return true, even if all requirements are met.
///
- public void Fail()
+ public virtual void Fail()
{
_failCalled = true;
}
@@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Authorization
/// successfully evaluated.
///
/// The requirement whose evaluation has succeeded.
- public void Succeed(IAuthorizationRequirement requirement)
+ public virtual void Succeed(IAuthorizationRequirement requirement)
{
_succeedCalled = true;
_pendingRequirements.Remove(requirement);
diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs
index f56ea5c19e..a9961b69ef 100644
--- a/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs
+++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationServiceCollectionExtensions.cs
@@ -28,6 +28,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAdd(ServiceDescriptor.Transient());
services.TryAdd(ServiceDescriptor.Transient());
services.TryAdd(ServiceDescriptor.Transient());
+ services.TryAdd(ServiceDescriptor.Transient());
services.TryAddEnumerable(ServiceDescriptor.Transient());
return services;
}
diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerContextFactory.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerContextFactory.cs
new file mode 100644
index 0000000000..2dae5e5e73
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationHandlerContextFactory.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Security.Claims;
+
+namespace Microsoft.AspNetCore.Authorization
+{
+ ///
+ /// A type used to provide a used for authorization.
+ ///
+ public class DefaultAuthorizationHandlerContextFactory : IAuthorizationHandlerContextFactory
+ {
+ ///
+ /// Creates a used for authorization.
+ ///
+ /// The requirements to evaluate.
+ /// The user to evaluate the requirements against.
+ ///
+ /// An optional resource the policy should be checked with.
+ /// If a resource is not required for policy evaluation you may pass null as the value.
+ ///
+ /// The .
+ public virtual AuthorizationHandlerContext CreateContext(IEnumerable requirements, ClaimsPrincipal user, object resource)
+ {
+ return new AuthorizationHandlerContext(requirements, user, resource);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs
index 45ee4aa4a2..89777eab01 100644
--- a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs
+++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs
@@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Authorization
///
public class DefaultAuthorizationService : IAuthorizationService
{
+ private readonly IAuthorizationHandlerContextFactory _contextFactory;
private readonly IAuthorizationEvaluator _evaluator;
private readonly IAuthorizationPolicyProvider _policyProvider;
private readonly IList _handlers;
@@ -27,7 +28,7 @@ namespace Microsoft.AspNetCore.Authorization
/// The used to provide policies.
/// The handlers used to fulfill s.
/// The logger used to log messages, warnings and errors.
- public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger) : this(policyProvider, handlers, logger, new DefaultAuthorizationEvaluator()) { }
+ public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger) : this(policyProvider, handlers, logger, new DefaultAuthorizationHandlerContextFactory(), new DefaultAuthorizationEvaluator()) { }
///
/// Creates a new instance of .
@@ -35,8 +36,9 @@ namespace Microsoft.AspNetCore.Authorization
/// The used to provide policies.
/// The handlers used to fulfill s.
/// The logger used to log messages, warnings and errors.
+ /// The used to create the context to handle the authorization.
/// The used to determine if authorzation was successful.
- public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger, IAuthorizationEvaluator evaluator)
+ public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IEnumerable handlers, ILogger logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator)
{
if (policyProvider == null)
{
@@ -50,6 +52,10 @@ namespace Microsoft.AspNetCore.Authorization
{
throw new ArgumentNullException(nameof(logger));
}
+ if (contextFactory == null)
+ {
+ throw new ArgumentNullException(nameof(contextFactory));
+ }
if (evaluator == null)
{
throw new ArgumentNullException(nameof(evaluator));
@@ -59,6 +65,7 @@ namespace Microsoft.AspNetCore.Authorization
_policyProvider = policyProvider;
_logger = logger;
_evaluator = evaluator;
+ _contextFactory = contextFactory;
}
///
@@ -78,7 +85,7 @@ namespace Microsoft.AspNetCore.Authorization
throw new ArgumentNullException(nameof(requirements));
}
- var authContext = new AuthorizationHandlerContext(requirements, user, resource);
+ var authContext = _contextFactory.CreateContext(requirements, user, resource);
foreach (var handler in _handlers)
{
await handler.HandleAsync(authContext);
diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerContextFactory.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerContextFactory.cs
new file mode 100644
index 0000000000..272109eea9
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationHandlerContextFactory.cs
@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Security.Claims;
+
+namespace Microsoft.AspNetCore.Authorization
+{
+ ///
+ /// A type used to provide a used for authorization.
+ ///
+ public interface IAuthorizationHandlerContextFactory
+ {
+ ///
+ /// Creates a used for authorization.
+ ///
+ /// The requirements to evaluate.
+ /// The user to evaluate the requirements against.
+ ///
+ /// An optional resource the policy should be checked with.
+ /// If a resource is not required for policy evaluation you may pass null as the value.
+ ///
+ /// The .
+ AuthorizationHandlerContext CreateContext(IEnumerable requirements, ClaimsPrincipal user, object resource);
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs
index 749a11dc34..7f9e5642ae 100644
--- a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs
+++ b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs
@@ -1038,11 +1038,52 @@ namespace Microsoft.AspNetCore.Authorization.Test
{
var authorizationService = BuildAuthorizationService(services =>
{
- // This will ignore the policy options
services.AddSingleton();
services.AddAuthorization(options => options.AddPolicy("Fail", p => p.RequireAssertion(c => false)));
});
Assert.True(await authorizationService.AuthorizeAsync(null, "Fail"));
}
+
+
+ public class BadContextMaker : IAuthorizationHandlerContextFactory
+ {
+ public AuthorizationHandlerContext CreateContext(IEnumerable requirements, ClaimsPrincipal user, object resource)
+ {
+ return new BadContext();
+ }
+ }
+
+ public class BadContext : AuthorizationHandlerContext
+ {
+ public BadContext() : base(new List(), null, null) { }
+
+ public override bool HasFailed
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool HasSucceeded
+ {
+ get
+ {
+ return false;
+ }
+ }
+ }
+
+ [Fact]
+ public async Task CanUseCustomContextThatAlwaysFails()
+ {
+ var authorizationService = BuildAuthorizationService(services =>
+ {
+ services.AddSingleton();
+ services.AddAuthorization(options => options.AddPolicy("Success", p => p.RequireAssertion(c => true)));
+ });
+ Assert.False(await authorizationService.AuthorizeAsync(null, "Success"));
+ }
+
}
}
\ No newline at end of file