Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a new AuthorizationHandler/IAuthorizationRequirement that uses a service

I am trying to create a new Authorization requirement, but it must use one of the services that I declare in ConfigureServices, and I have no idea how to pass that service to the new requirement in the same method as declaring the service.

public class NewRequirement: AuthorizationHandler<NewRequirement>, IAuthorizationRequirement
{
    private IRepository _repository;

    public NewRequirement(IRepository repository)
    {
        _repository = repository;
    }

    protected override void Handle(AuthorizationContext context, NewRequirement requirement)
    {
        //code that uses _repository here
    }
}

This works fine, but when I try to add the policy in Startup.cs like this:

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddSingleton<RepositoryContext>();
        services.AddScoped<IRepository, Repository>();

        services.Configure<AuthorizationOptions>(options =>
        {
            options.AddPolicy("NewRequirement", policy => policy.Requirements.Add(new NewRequirement()));
        });
    }

I get this error

Error CS7036
There is no argument given that corresponds to the required formal parameter 'repository' of 'ExtraProjectRequirements.NewRequirement(IRepository)'
Reception.DNX 4.5.1

I think I need to pass the IRepository to the new policy but I have no idea how to do it in ConfigureServices.

like image 553
Matthew Avatar asked Jun 01 '16 20:06

Matthew


People also ask

Under Which method should a policy be registered for it to be a part of authorization service?

In this article Typically when using policy-based authorization, policies are registered by calling AuthorizationOptions. AddPolicy as part of authorization service configuration.

What is IAuthorizationRequirement?

IAuthorizationRequirement is a marker service with no methods, and the mechanism for tracking whether authorization is successful. Each IAuthorizationHandler is responsible for checking if requirements are met: C# Copy.

What does resource Authorisation do?

Often authorization depends upon the resource being accessed. For example a document may have an author property. Only the document author would be allowed to update it, so the resource must be loaded from the document repository before an authorization evaluation can be made.

What is resource based authorization?

Authorizing an action based on the roles assigned to a user. For example, some actions require an administrator role. Resource-based authorization. Authorizing an action based on a particular resource. For example, every resource has an owner.


1 Answers

You are passing the handler to the requirement, which is wrong. IAuthorizationRequirement and AuthorizationHandler<NewRequirement> need to be two distinct classes. Also IAuthorizationRequirement is only a marker interface w/o any mandatory properties or methods, just there to accidentally adding arbitrary classes to the Requirements collection ;)

The IAuthorizationRequirement will contain pure data (reads: No services, no dependencies that need to be injected) required for your requirement, the handler will validate it. See @blowdart example of an Over18Requirement and it's handler as well as the official documentation.

Handlers are allowed to have dependencies injected.

Examples from the documentation for future readers (in case link becomes unavailable).

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int age)
    {
        MinimumAge = age;
    }

    protected int MinimumAge { get; set; }
}

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override void Handle(AuthorizationContext context, MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                   c.Issuer == "http://contoso.com"))
        {
            return;
        }

        var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(
            c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }
    }
}
like image 178
Tseng Avatar answered Sep 22 '22 10:09

Tseng