0
votes

I am new to aspnet core. we are using identity core 2.1. Now made a page from where admin can set the different configuration like idle-time lockout-time password-retries. Those settings are being saved into the database table. Now I want that my identity option will set from those values. I made a repository to get setting from database. but I am unable to call that repository function from startup.cs.

Can some please guide me? and Also tell me the best way to make identity options configurable from database.

I have made a service

public class SecuritySettingService : ISecuritySettingService
{
    private readonly ISecuritySettingRepository _securitySettingRepository;
    public SecuritySettingService(ISecuritySettingRepository securitySettingRepository)
    {
        _securitySettingRepository = securitySettingRepository;
    }
    public SecuritySetting GetSecuritySetting()
    {
       return  _securitySettingRepository.GetSecuritySetting();
    }
}

A repository to connect to database

public class SecuritySettingRepository : ISecuritySettingRepository
{
    private readonly IDbRepository _dapperWrapper;
    public SecuritySettingRepository(IDbRepository dapperWrapper)
    {
        _dapperWrapper = dapperWrapper;
    }
    public SecuritySetting GetSecuritySetting()
    {
        var response = _dapperWrapper.QuerySingleOrDefault<SecuritySetting>("security_setting_get", null, CommandType.StoredProcedure);

        return response;
    }
}

Made identity config class to clean up startup.cs

public static class IdentityConfig { public static void ConfigureIdentity(IServiceCollection services, ISecuritySettingService securitySettingService) {

        var securitySetting = securitySettingService.GetSecuritySetting();
        services.AddIdentity<ApplicationUser, ApplicationRole>(options => {
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = true;


        }).AddUserManager<CustomUserManager>().AddDefaultTokenProviders();





        services.Configure<IdentityOptions>(options =>
        {
            // Default User settings.
            options.User.AllowedUserNameCharacters =
                    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
            options.User.RequireUniqueEmail = true;


        });

        services.Configure<DataProtectionTokenProviderOptions>(options =>
        {
            options.TokenLifespan = TimeSpan.FromDays(30);
        });

        services.ConfigureApplicationCookie(options =>
        {
            options.Cookie.HttpOnly = true;
            options.ExpireTimeSpan = TimeSpan.FromHours(1);
            options.LoginPath = "/login";
            options.LogoutPath = "/logout";
            options.Cookie = new CookieBuilder
            {
                IsEssential = true // required for auth to work without explicit user consent; adjust to suit your privacy policy
            };
        });

    }
}

startup.cs file is like

public class Startup
{
    public ISecuritySettingService _securitySettingService;
    public Startup(IConfiguration configuration, ISecuritySettingService securitySettingService)
    {
        Configuration = configuration;
        _securitySettingService = securitySettingService;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {



        IdentityConfig.ConfigureIdentity(services, _securitySettingService);


        services.AddOptions();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

        services.AddHttpContextAccessor();

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {

    }
}

in startup.cs when I call IdentityConfig.ConfigureIdentity(services, _securitySettingService); the object _securitySettingService is not present so my code throws exception invalid operation

1
what is the error? what did you try so far? please provide Minimal, Complete, and Verifiable exampleDerviş Kayımbaşıoğlu

1 Answers

2
votes

For your current error, you did you register ISecuritySettingService and used it in Startup. For referencing ISecuritySettingService, you need to register it first.

For services.Configure<IdentityOptions>, it will not change automatically while changing the database. You need to update IdentityOptions by yourself.

Follow Steps below and modify it as needed.

  1. ISecuritySettingRepository

    public interface ISecuritySettingRepository
    {
        LockoutOption GetSecuritySetting();
        LockoutOption UpdateSecuritySetting(LockoutOption lockoutOption);
    }
    
  2. SecuritySettingRepository

    public class SecuritySettingRepository : ISecuritySettingRepository
    {
        private readonly DbConnection _dapperWrapper;
        private readonly IConfiguration _configuration;
    
        public SecuritySettingRepository(DbConnection dapperWrapper
            , IConfiguration configuration)
        {
            _dapperWrapper = dapperWrapper;
            _configuration = configuration;
        }
        public LockoutOption GetSecuritySetting()
        {
            using (var connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection")))
            {
                string sQuery = "SELECT top 1 * From LockoutOption Where Id = 1";
                var response = connection.QueryFirstOrDefault<LockoutOption>(sQuery);
                return response;
            }
        }
    
        public LockoutOption UpdateSecuritySetting(LockoutOption lockoutOption)
        {
            using (var connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection")))
            {
                string sQuery = $"Update LockoutOption Set MaxFailedAccessAttempts = {lockoutOption.MaxFailedAccessAttempts} Where Id = {lockoutOption.Id}";
                var result = connection.Execute(sQuery);
                string sQuery1 = "SELECT top 1 * From LockoutOption Where Id = 1";
                var response = connection.QueryFirstOrDefault<LockoutOption>(sQuery1);
    
                return response;
            }
        }
    }
    
  3. ISecuritySettingService

    public interface ISecuritySettingService
    {
        LockoutOption GetSecuritySetting();
        LockoutOption UpdateSecuritySetting(LockoutOption lockoutOption);
    }
    
  4. SecuritySettingService

    public class SecuritySettingService : ISecuritySettingService
    {
        private readonly ISecuritySettingRepository _securitySettingRepository;
        private readonly IdentityOptions _identityOptions;
    
        public SecuritySettingService(ISecuritySettingRepository securitySettingRepository
            , IOptions<IdentityOptions> identityOptions)
        {
            _securitySettingRepository = securitySettingRepository;
            _identityOptions = identityOptions.Value;
        }
        public LockoutOption GetSecuritySetting()
        {
            return _securitySettingRepository.GetSecuritySetting();
        }
    
        public LockoutOption UpdateSecuritySetting(LockoutOption lockoutOption)
        {
            var option = _securitySettingRepository.UpdateSecuritySetting(lockoutOption);
            //update identity options
            _identityOptions.Lockout.MaxFailedAccessAttempts = option.MaxFailedAccessAttempts;
            return option;
        }
    }
    
  5. Register in Startup

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<DbConnection>(serviceProvider => new DbConnection(Configuration.GetConnectionString("DefaultConnection")));
            services.AddMvc();
            // your rest configure services
    
            services.AddTransient<ISecuritySettingService, SecuritySettingService>();
            services.AddTransient<ISecuritySettingRepository, SecuritySettingRepository>();
            var _ecuritySettingService = services.BuildServiceProvider().GetRequiredService<ISecuritySettingService>();
            services.Configure<IdentityOptions>(options =>
            {
                options.Lockout.MaxFailedAccessAttempts = _ecuritySettingService.GetSecuritySetting()?.MaxFailedAccessAttempts ?? 3;
            });
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //your configure
        }
    }
    
  6. Useage

    namespace DapperPro.Controllers
    {
        public class LockoutOptionsController : Controller
        {
            private readonly ApplicationDbContext _context;
            private readonly IdentityOptions _identityOptions;
            private readonly ISecuritySettingService _securitySettingService;
            public LockoutOptionsController(ApplicationDbContext context
                , IOptions<IdentityOptions> identityOptions
                , ISecuritySettingService securitySettingService)
            {
                _context = context;
                _identityOptions = identityOptions.Value;
                _securitySettingService = securitySettingService;
            }        
    
            // POST: LockoutOptions/Edit/5
            // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
            // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
            [HttpPost]
            [ValidateAntiForgeryToken]
            public async Task<IActionResult> Edit(int id, [Bind("Id,AllowedForNewUsers,MaxFailedAccessAttempts,DefaultLockoutTimeSpan")] LockoutOption lockoutOption)
            {
                _securitySettingService.UpdateSecuritySetting(lockoutOption);
    
                return View(lockoutOption);
            }        
        }
    }