“context.Resource as AuthorizationFilterContext” returning null in ASP.NET Core 3.0

前端 未结 6 530
情书的邮戳
情书的邮戳 2020-12-16 07:50

I am trying to implement a custom authorization requirement following a tutorial. It seems like context.Resource no longer contains AuthorizationFilterCo

相关标签:
6条回答
  • 2020-12-16 08:04
        private readonly IHttpContextAccessor httpContextAccessor;
        public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
        {
            this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
    
        }
    
    0 讨论(0)
  • 2020-12-16 08:11

    This is due to the new endpoint routing in .NET Core 3.0.

    To quote the below ticket.

    This is because when using endpoint routing in ASP.NET Core 3.0:

    Mvc will no longer add AuthorizeFilter to ActionDescriptor and ResourceInvoker will not call AuthorizeAsync() https://github.com/aspnet/AspNetCore/blob/90ab2cb965aeb8ada13bc4b936b3735ca8dd28df/src/Mvc/Mvc.Core/src/ApplicationModels/AuthorizationApplicationModelProvider.cs#L40

    Mvc will add all Filter as metadata to endpoint.Metadata https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Mvc/Mvc.Core/src/Routing/ActionEndpointFactory.cs#L348

    instead by AuthorizationMiddleware call the AuthorizeAsync() and resouorce is Endpoint https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs#L63

    New method.

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CookieOrTokenAuthorizationRequirement requirement)
    {
        if (context.Resource is Endpoint endpoint)
        {
            if (endpoint.Metadata.OfType<IFilterMetadata>().Any(filter => filter is MyFilter))
            {
                context.Succeed(requirement);
                return Task.CompletedTask;
            }
        }
    }
    

    https://github.com/dotnet/aspnetcore/issues/11075

    It's worth noting too that using the new context you won't be able to access route data as you were before with the AuthorizationFilterContext. You will need to inject an IHttpContextAccessor into the AuthorizationHandler.

    // Ensure your handler is registered as scoped
    services.AddScoped<IAuthorizationHandler, InvestorRequirementHandler>();
    
    
    public class InvestorRequirementHandler : AuthorizationHandler<InvestorRequirement>
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public InvestorRequirementHandler(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, InvestorRequirement requirement)
        {
            var tenant = httpContextAccessor.HttpContext.GetRouteData().Values[ExceptionHandlerMiddleware.TenantCodeKey].ToString();
        }
    }
    
    0 讨论(0)
  • 2020-12-16 08:12

    Changes in startup.cs if you are using Rout attribute in controllers You can replace this

      app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                   name: "home",
                   pattern: "{controller=Home}");
            });
    

    with this in Configure()

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "home",
                    template: "{controller=Home}");
            });
    

    and disable endpoint routing with this in ConfigureSevices()

       services.AddMvc().AddMvcOptions(mvcopt=> { mvcopt.EnableEndpointRouting = false;});
    

    Works in Asp .Net Core 5 as well

    0 讨论(0)
  • 2020-12-16 08:18

    public class CanEditOnlyOtherAdminRolesAndClaimsHandler : AuthorizationHandler { private readonly IHttpContextAccessor httpContextAccessor; public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));

        }
    
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       ManageAdminRolesAndClaimsRequirement requirement)
        {
    
            var loggedInAdminId = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value.ToString();
    
            var adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();
    
            if (context.User.IsInRole("Admin") &&
                context.User.HasClaim(claim =>
                claim.Type == "Edit Role" && claim.Value == "true") && adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
            {
                context.Succeed(requirement);
            }
    
            return Task.CompletedTask;
        }
    
    }
    
    0 讨论(0)
  • 2020-12-16 08:28
    public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
       AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
        {
           private readonly IHttpContextAccessor httpContextAccessor;
    
           public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
           {
                this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
           }
    
           protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
           ManageAdminRolesAndClaimsRequirement requirement)
            {
                if (context.User == null || !context.User.Identity.IsAuthenticated)
                {
                    context.Fail();
                    return Task.CompletedTask;
                }
    
                string loggedInAdminId =
                    context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
    
                string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();
    
                if (context.User.IsInRole("Admin") &&
                context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
                adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
                {
                    context.Succeed(requirement);
                }
    
                return Task.CompletedTask;
            }
        }
    }
    

    Then add the following services to ConfigureServices method in Startup class:

     public void ConfigureServices(IServiceCollection services)
            {           
              services.AddAuthorization(options =>
                {
                 options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
                }
    
              services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();       
          }
    

    If you want to handle a multiple custom authorization for a requirement:

    in CanEditOnlyOtherAdminRolesAndClaimsHandler class you check if the user is in Admin role and has Edit Role claim. Let's suppose that you require that the user must to be in Super Admin role, in this scenario, you can either:

    - edit the condition in CanEditOnlyOtherAdminRolesAndClaimsHandler class to be as a following:

    if (context.User.IsInRole("Admin") &&
                    context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
                    adminIdBeingEdited.ToLower() != loggedInUserId.ToLower() || 
                    context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
                    {
                        context.Succeed(requirement);
                    }
    

    - or customize another authorization handler for the new requirement which in this case is Super Admin role:

    Create a new class and name it ManageRolesAndClaimsSuperAdminHandler, the implementation of this class should be as follows

    public class ManageRolesAndClaimsSuperAdminHandler : AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
        {
            private readonly IHttpContextAccessor httpContextAccessor;
    
            public ManageUsersRolesSuperAdminHandler(IHttpContextAccessor httpContextAccessor)
            {
                this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
            }
    
            protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ManageAdminRolesAndClaimsRequirement  requirement)
            {
            if (context.User == null || !context.User.Identity.IsAuthenticated)
                {
                    context.Fail();
                    return Task.CompletedTask;
                }
    
                string loggedInAdminId = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
    
                string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();
    
                if (context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
                {
                    context.Succeed(requirement);
                }
                return Task.CompletedTask;
            }
        }
    

    Now register the new handler in ConfigureServices method in Startup class

    public void ConfigureServices(IServiceCollection services)
          {        
              services.AddAuthorization(options =>
                {
                 options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
                }
    
              services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();
              services.AddScoped<IAuthorizationHandler, ManageRolesAndClaimsSuperAdminHandler>();
          }
    
    0 讨论(0)
  • 2020-12-16 08:29

    The Resource property will only be an AuthorizationFilterContext in the context of an [Authorize] attribute.

    0 讨论(0)
提交回复
热议问题