0

I have implemented the ClaimRequirementFilter, but the injection/ parameter handling doesn't work. The constructor of the AuthorizationFilter is called twice. The MyClaim object (parameter) Name property is null during the first call and in the second it is filled. The OnAuthorization is only called once, using the null value.

MyClaimAttribute.cs

public class MyClaimAttribute : TypeFilterAttribute
{
    public MyClaimAttribute(string claimName) : base(typeof(AuthorizationFilter))
    {
        MyClaim claim = new MyClaim
                                {
                                    Name = claimName
                                };

        Arguments = new object[] { claim };
    }
}

AuthorizationFilter.cs

public AuthorizationFilter(MyClaim claim)
{
    _claim = claim;
}

Startup.cs

   services.AddTransient<MyClaim>();
   services.AddMvc(options => options.Filters.Add(typeof(AuthorizationFilter)));

Controller method is decorated with: [MyClaim("CanDoSomething")]

Update 1:

Removing the 'services.AddMvc' line fixed the issue, but I would like to execute it for every action. If one of the methods isn't decorated, the user shouldn't be authorized.

Odrai
  • 1,793
  • 1
  • 23
  • 46

1 Answers1

0

For the reason that claimName is null with options.Filters.Add(typeof(AuthorizationFilter)) is that, you register MyClaim without passing any value for string claimName.

Try to passing the string claimName like below when registering MyClaim.

            services.AddTransient((serviceProvider)=> new Claim { Type = "T1", Value = "V1" });
        services.AddMvc(c =>
                        {
                            c.Filters.Add(typeof(RequestLoggerActionFilter));
                            c.Filters.Add(typeof(ClaimRequirementFilter));
                            //c.Filters.Add(new ClaimRequirementFilter(new Claim { Type = "T1", Value = "V1" }));
                        }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Note, the Claim is corresponding to MyClaim

Edward
  • 22,080
  • 7
  • 44
  • 80
  • I would like to use the attribute value of [MyClaim("CanDoSomething")] instead of hardcoding the value in the AddTransient. The ClaimRequirementFilter should use/ check the value of the attribute. – Odrai Sep 17 '18 at 09:09
  • @Odrai If there are multiple `[MyClaim("CanDoSomething")]`, how to know which value to use? Anyway, `c.Filters.Add` is used for all actions and you need to hardcoding the value just like you passing by ` [MyClaim("CanDoSomething")]` which is also hardcoding. – Edward Sep 17 '18 at 09:20
  • We only have one MyClaim attribute per action. E.g. action 1 requires the 'CanDoSomething' permission and action 2 'CanViewSomething', how should I implement services.AddTransient and c.Filters? How does the ClaimRequirementFilter know which hardcoded claim needs to be checked for a certain action? – Odrai Sep 17 '18 at 09:32
  • @Odrai It seems you missuse `ClaimRequirementFilter `. `ClaimRequirementFilter ` will be one instance with a specific `MyClaim` and will apply on all the MVC actions. – Edward Sep 17 '18 at 09:36
  • @ToaZhou Does asp.net core 2 provide another solution? I would like to decorate the actions with one permission and a certain filter needs to check all executed actions. If the action isn't decorated, return unauthorized, otherwise check the attribute value. – Odrai Sep 17 '18 at 09:52
  • @Odrai IMO, it is unreasonable to achieve this requirement. If you must, you may try to check the action from `AuthorizationFilterContext`, then check whether there is attribute on `Action`. – Edward Sep 18 '18 at 01:03