I have an authorization handler that needs to pull data from the database to complete the authorization logic. The idea is that users are only allowed to certain areas after posting a given number of blog posts.
Code as follows:
namespace MyProject.Authorisation
{
public class MinimumPostRequirement : IAuthorizationRequirement
{
public MinimumPostRequirement (int postCount)
{
PostCount = postCount;
}
public int PostCount { get; }
}
public class MinimumPostRequirement Handler : AuthorizationHandler<MinimumPostRequirement >
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext authContext, ApprovedUserRequirement requirement)
{
using (MyDbContext _context = new MyDbContext())
{
int? postCount = _context.Posts.Where(post => post.UserName == authContext.User.Identity.Name).Count();
if(postCount == null)
{
return Task.CompletedTask;
}
if(postCount >= requirement.PostCount)
{
authContext.Succeed(requirement);
}
return Task.CompletedTask;
}
}
}
}
Here is how I declare it in Program.cs:
//DB Connection
var connectionString = builder.Configuration.GetConnectionString("MyConnection");
builder.Services.AddDbContext<MyDbContext>(options => options.UseSqlServer(MyConnection));
//authorisation
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireMinimumPosts", policy => policy.Requirements.Add(new MinimumPostRequirement(3)));
});
builder.Services.AddSingleton<IAuthorizationHandler, MinimumPostRequirementHandler>();
I know there is a dependency injection issue when adding a singleton, so I have also tried using in Program.cs:
builder.Services.AddScoped<IAuthorizationHandler, MinimumPostRequirementHandler>();
And
builder.Services.AddTransient<IAuthorizationHandler, MinimumPostRequirementHandler>();
All result in the following error:
InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
The database works for all other site operations. The problem only arises when I add [Authorize(Policy = "RequireMinimumPosts")]
to the methods I want to restrict.
How would you write this code so that it works? How does dependency injection work in this context? Is there anything I am missing?
CodePudding user response:
Inject the DbContext
into the constructor of your MinimumPostRequirementHandler
so it will be resolved by the DI container.
public class MinimumPostRequirementHandler
: AuthorizationHandler<MinimumPostRequirement>
{
private readonly MyDbContext _dbContext;
public MinimumPostRequirementHandler( MyDbContext dbContext )
{
_dbContext = dbContext;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext authContext, ApprovedUserRequirement requirement)
{
// use _dbContext here
}
}
Register the handler as scoped.