1

I want to add some policies to ASP.Net Core authorization, which are stored in my database (EF Core with MySQL). The goal is to limit users' access based on their authorities. As far as I know, I should do it in the ConfigureServices method in the Startup class like this:

services.AddAuthorizationCore(options =>
{
  options.AddPolicy("CanEditUserGroups", builder =>
  {
    builder.RequireAuthenticatedUser();
    builder.RequireClaim("Permission", "Value");
  });
});

But, the problem is that I want to have several policies and read their "Value" from the database. Is it possible to instantiate my DbContext service and read data from that? Am I doing it the right way?

Amir Fakhim Babaei
  • 1,014
  • 1
  • 5
  • 13

2 Answers2

3

I found the solution. For those who might face this problem in the future, here is one way to do that:

var serviceProvider = services.BuildServiceProvider();
var permissionRepository =
    (IPermissionRepository)serviceProvider.GetService<IPermissionRepository>();
var permissions = permissionRepository.GetAll();

services.AddAuthorizationCore(options =>
{
    foreach (var permission in permissions)
    {
        options.AddPolicy("permission.Title", builder =>
        {
            builder.RequireAuthenticatedUser();
            builder.RequireClaim("Permission", permission.Title);
        });
    }
});
Amir Fakhim Babaei
  • 1,014
  • 1
  • 5
  • 13
  • warning: BuildServiceProvider() results in additional copies of all Singleton services being created. Check for full explaination:https://stackoverflow.com/questions/56042989/what-are-the-costs-and-possible-side-effects-of-calling-buildserviceprovider-i – Micheal Choudhary Oct 31 '20 at 02:43
3

You can use Custom Configuration Provider to get the data from database and use it in ConfigureServices method. See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-2.2#custom-configuration-provider

public class EFConfigurationValue
{
    public string Id { get; set; } = String.Empty;
    public string Value { get; set; } = String.Empty;
}

public class EFConfigurationContext : DbContext
{
    public EFConfigurationContext(DbContextOptions<EFConfigurationContext> options) : base(options)
    {
    }

    public DbSet<EFConfigurationValue> Values => Set<EFConfigurationValue>();
}

public class EFConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _optionsAction;

    public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction) => _optionsAction = optionsAction;

    public IConfigurationProvider Build(IConfigurationBuilder builder) => new EFConfigurationProvider(_optionsAction);
}

public class EFConfigurationProvider : ConfigurationProvider
{
    public EFConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction)
    {
        OptionsAction = optionsAction;
    }

    private Action<DbContextOptionsBuilder> OptionsAction { get; }

    public override void Load()
    {
        var builder = new DbContextOptionsBuilder<EFConfigurationContext>();

        OptionsAction(builder);

        using (var dbContext = new EFConfigurationContext(builder.Options))
        {
            if (dbContext == null || dbContext.Values == null)
            {
                throw new Exception("Null DB context");
            }
            dbContext.Database.EnsureCreated();

            Data = !dbContext.Values.Any()
                ? CreateAndSaveDefaultValues(dbContext)
                : dbContext.Values.ToDictionary(c => c.Id, c => c.Value);
        }
    }

    private static IDictionary<string, string> CreateAndSaveDefaultValues(
        EFConfigurationContext dbContext)
    {
        // Quotes (c)2005 Universal Pictures: Serenity
        // https://www.uphe.com/movies/serenity-2005
        var configValues =
            new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                { "quote1", "I aim to misbehave." },
                { "quote2", "I swallowed a bug." },
                { "quote3", "You can't stop the signal, Mal." }
            };

        if (dbContext == null || dbContext.Values == null)
        {
            throw new Exception("Null DB context");
        }

        dbContext.Values.AddRange(configValues
            .Select(kvp => new EFConfigurationValue
            {
                Id = kvp.Key,
                Value = kvp.Value
            })
            .ToArray());

        dbContext.SaveChanges();

        return configValues;
    }
}
public static class EntityFrameworkExtensions
{
    public static IConfigurationBuilder AddEFConfiguration(
               this IConfigurationBuilder builder,
               Action<DbContextOptionsBuilder> optionsAction)
    {
        return builder.Add(new EFConfigurationSource(optionsAction));
    }
}

Then add AddEFConfiguration in Program.cs

  public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                 config.AddEFConfiguration(opt=>opt.UseSqlServer("Server= ;Database= ;Trusted_Connection=true;"));
            }) 
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

ConfigureServices method > use below syntax to get the values from Configuration

Configuration["Key"]
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Mary
  • 564
  • 3
  • 14