Consider the following code in the ConfigureServices
method of the Startup
class -
services.AddTransient<IAuthorizationHandler, BlockUsersHandler>();
services.AddTransient<IAuthorizationHandler, BlockClaimHandler>();
services.AddAuthorization(option =>
{
option.AddPolicy("NotHacker", policy =>
{
policy.AddRequirements(new BlockUsersRequirement("Hacker"));
});
option.AddPolicy("NotThatClaim", policy =>
{
policy.AddRequirements(new BlockClaimRequirement(new Claim("ThatClaimType", "ThatClaim")));
});
});
and these are the custom class implementations -
public class BlockUsersRequirement : IAuthorizationRequirement
{
// Code goes here
}
public class BlockUsersHandler : AuthorizationHandler<BlockUsersRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockUsersRequirement requirement)
{
// Code goes here
}
}
public class BlockClaimRequirement : IAuthorizationRequirement
{
// Code goes here
}
public class BlockClaimHandler : AuthorizationHandler<BlockClaimRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockClaimRequirement requirement)
{
// Code goes here
}
}
My understanding was that whenever a dependency on a service is faced, the built-in dependency resolver provides the concrete implementation registered for that service and if I register multiple implementation of a service, then the last registration will take effect.
In the code above, two implementations are registered for IAuthorizationHandler
and the two authorization policies are working fine with that.
So, how is the dependency resolver deciding when to select which implementation? And based on what?
EDIT - 2019.07.28
So, as @Martin answered below, looks like the dependency resolver can infer the implementation from the IAuthorizationRequirement
in the AuthorizationHandler<TRequirement>
, from which the Handler implementations are deriving.
But you actually can create a Handler class by directly implementing the IAuthorizationHandler
interface without deriving from AuthorizationHandler<TRequirement>
-
public class DeletePermissionRequirement : IAuthorizationRequirement
{
// Nothing here
}
public class DeletePermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
// Code goes here
}
}
So, now there is no IAuthorizationRequirement
in the Handler's signature to infer from.
Also, you can add multiple Handler implementations for a single Requirement -
public class BuildingEntryRequirement : IAuthorizationRequirement
{
// Nothing here
}
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
// Code goes here
}
}
public class TemporaryPassHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
// Code goes here
}
}
Taking the these new implementations into account the code in the ConfigureServices
method looks like -
services.AddTransient<IAuthorizationHandler, BlockUsersHandler>();
services.AddTransient<IAuthorizationHandler, BlockClaimHandler>();
services.AddTransient<IAuthorizationHandler, DeletePermissionHandler>();
services.AddTransient<IAuthorizationHandler, BadgeEntryHandler>();
services.AddTransient<IAuthorizationHandler, TemporaryPassHandler>();
services.AddAuthorization(option =>
{
option.AddPolicy("NotHacker", policy =>
{
policy.AddRequirements(new BlockUsersRequirement("Hacker"));
});
option.AddPolicy("NotThatClaim", policy =>
{
policy.AddRequirements(new BlockClaimRequirement(new Claim("ThatClaimType", "ThatClaim")));
});
option.AddPolicy("CanDelete", policy =>
{
policy.AddRequirements(new DeletePermissionRequirement());
});
option.AddPolicy("BadgeEntry", policy =>
{
policy.AddRequirements(new BuildingEntryRequirement());
});
});
and, of course, all the authorization policies are working fine.
So again, how the dependency resolver is selecting the right implementation?
AuthorizationHandler<TRequirement>
and directly implementing theIAuthorizationHandler
interface, in which case there is no way to infer theRequirement
from the Handler's signature. I'll try to add an **Edit on that. Also, you can add multiple Handler implementations for a single Requirement. – Tritheism