Browse Source

Authorization Failure Reasons (#35668)

Kahbazi 4 years ago
parent
commit
8ba6ea4d56

+ 2 - 2
src/Security/Authorization/Core/src/AuthorizationFailure.cs

@@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Authorization
         /// <summary>
         /// Allows <see cref="IAuthorizationHandler"/> to flow more detailed reasons for why authorization failed.
         /// </summary>
-        public IEnumerable<AuthorizationFailureReason> Reasons { get; private set; } = Array.Empty<AuthorizationFailureReason>();
+        public IEnumerable<AuthorizationFailureReason> FailureReasons { get; private set; } = Array.Empty<AuthorizationFailureReason>();
 
         /// <summary>
         /// Return a failure due to <see cref="AuthorizationHandlerContext.Fail()"/> being called.
@@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Authorization
             => new AuthorizationFailure
             {
                 FailCalled = true,
-                Reasons = reasons
+                FailureReasons = reasons
             };
 
         /// <summary>

+ 2 - 2
src/Security/Authorization/Core/src/AuthorizationFailureReason.cs

@@ -22,11 +22,11 @@ namespace Microsoft.AspNetCore.Authorization
         /// <summary>
         /// A message describing the failure reason.
         /// </summary>
-        public string Message { get; set; }
+        public string Message { get; }
 
         /// <summary>
         /// The <see cref="IAuthorizationHandler"/> responsible for this failure reason.
         /// </summary>
-        public IAuthorizationHandler Handler { get; set; }
+        public IAuthorizationHandler Handler { get; }
     }
 }

+ 8 - 3
src/Security/Authorization/Core/src/AuthorizationHandlerContext.cs

@@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Authorization
     public class AuthorizationHandlerContext
     {
         private readonly HashSet<IAuthorizationRequirement> _pendingRequirements;
-        private readonly List<AuthorizationFailureReason> _failedReasons;
+        private List<AuthorizationFailureReason>? _failedReasons;
         private bool _failCalled;
         private bool _succeedCalled;
 
@@ -38,7 +38,6 @@ namespace Microsoft.AspNetCore.Authorization
             User = user;
             Resource = resource;
             _pendingRequirements = new HashSet<IAuthorizationRequirement>(requirements);
-            _failedReasons = new List<AuthorizationFailureReason>();
         }
 
         /// <summary>
@@ -64,7 +63,8 @@ namespace Microsoft.AspNetCore.Authorization
         /// <summary>
         /// Gets the reasons why authorization has failed.
         /// </summary>
-        public virtual IEnumerable<AuthorizationFailureReason> FailureReasons { get { return _failedReasons; } }
+        public virtual IEnumerable<AuthorizationFailureReason> FailureReasons
+            => (IEnumerable<AuthorizationFailureReason>?)_failedReasons ?? Array.Empty<AuthorizationFailureReason>();
 
         /// <summary>
         /// Flag indicating whether the current authorization processing has failed.
@@ -101,6 +101,11 @@ namespace Microsoft.AspNetCore.Authorization
             Fail();
             if (reason != null)
             {
+                if (_failedReasons == null)
+                {
+                    _failedReasons = new List<AuthorizationFailureReason>();
+                }
+
                 _failedReasons.Add(reason);
             }
         }

+ 1 - 3
src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt

@@ -1,12 +1,10 @@
 #nullable enable
 *REMOVED*static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object! resource, Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationResult!>!
-Microsoft.AspNetCore.Authorization.AuthorizationFailure.Reasons.get -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationFailureReason!>!
+Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailureReasons.get -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationFailureReason!>!
 Microsoft.AspNetCore.Authorization.AuthorizationFailureReason
 Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.AuthorizationFailureReason(Microsoft.AspNetCore.Authorization.IAuthorizationHandler! handler, string! message) -> void
 Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Handler.get -> Microsoft.AspNetCore.Authorization.IAuthorizationHandler!
-Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Handler.set -> void
 Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Message.get -> string!
-Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Message.set -> void
 static Microsoft.AspNetCore.Authorization.AuthorizationFailure.Failed(System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationFailureReason!>! reasons) -> Microsoft.AspNetCore.Authorization.AuthorizationFailure!
 static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object? resource, Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationResult!>!
 virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Fail(Microsoft.AspNetCore.Authorization.AuthorizationFailureReason! reason) -> void

+ 3 - 3
src/Security/Authorization/test/DefaultAuthorizationServiceTests.cs

@@ -213,11 +213,11 @@ namespace Microsoft.AspNetCore.Authorization.Test
             // Assert
             Assert.False(allowed.Succeeded);
             Assert.NotNull(allowed.Failure);
-            Assert.Equal(2, allowed.Failure.Reasons.Count());
-            var first = allowed.Failure.Reasons.First();
+            Assert.Equal(2, allowed.Failure.FailureReasons.Count());
+            var first = allowed.Failure.FailureReasons.First();
             Assert.Equal("1", first.Message);
             Assert.Equal(handler1, first.Handler);
-            var second = allowed.Failure.Reasons.Last();
+            var second = allowed.Failure.FailureReasons.Last();
             Assert.Equal("3", second.Message);
             Assert.Equal(handler3, second.Handler);
         }

+ 2 - 2
src/Security/samples/CustomAuthorizationFailureResponse/Authorization/SampleAuthorizationMiddlewareResultHandler.cs

@@ -31,9 +31,9 @@ namespace CustomAuthorizationFailureResponse.Authorization
             // if the authorization was forbidden, let's use custom logic to handle that.
             if (policyAuthorizationResult.Forbidden && policyAuthorizationResult.AuthorizationFailure != null)
             {
-                if (policyAuthorizationResult.AuthorizationFailure.Reasons.Any())
+                if (policyAuthorizationResult.AuthorizationFailure.FailureReasons.Any())
                 {
-                    await httpContext.Response.WriteAsync(policyAuthorizationResult.AuthorizationFailure.Reasons.First().Message);
+                    await httpContext.Response.WriteAsync(policyAuthorizationResult.AuthorizationFailure.FailureReasons.First().Message);
 
                     // return right away as the default implementation would overwrite the status code
                     return;

+ 4 - 4
src/Security/samples/CustomAuthorizationFailureResponse/Startup.cs

@@ -35,13 +35,13 @@ namespace CustomAuthorizationFailureResponse
 
             services.AddAuthorization(options =>
             {
-                options.AddPolicy(SamplePolicyNames.CustomPolicy, policy => 
+                options.AddPolicy(SamplePolicyNames.CustomPolicy, policy =>
                     policy.AddRequirements(new SampleRequirement()));
 
-                options.AddPolicy(SamplePolicyNames.FailureReasonPolicy, policy => 
+                options.AddPolicy(SamplePolicyNames.FailureReasonPolicy, policy =>
                     policy.AddRequirements(new SampleFailReasonRequirement()));
-                
-                options.AddPolicy(SamplePolicyNames.CustomPolicyWithCustomForbiddenMessage, policy => 
+
+                options.AddPolicy(SamplePolicyNames.CustomPolicyWithCustomForbiddenMessage, policy =>
                     policy.AddRequirements(new SampleWithCustomMessageRequirement()));
             });