How would you seed users? I am following their documents here, but they only show how to seed data that is inserted directly by the ApplicationDbContext.
In the Account controller, the UserManager is created through DI. How would I instantiate a UserManager in the Seed method?
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
Then in Startup.cs in the Configure method:
SeedData.Initialize(app.ApplicationServices);
In the startup.cs in the configure method, you can add the following code to get the UserManager and from there seed the users.
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>();
You would create a class in the lines of
public class UserSeed
{
private ApplicationDbContext _context;
private UserManager<ApplicationUser> _mgr;
public UserSeed(ApplicationDbContext context,UserManager<ApplicationUser> userManager)
{
_context = context;
_mgr = users;
}
public void UserSeedData()
{
var user = new ApplicationUser { UserName = "foo#foo.com", Email = "foo#foo.com" };
var result = _mgr.CreateAsync(user,"fooPassword");
......
......
}
}
In the configure method of your startup.cs take userSeed through DI like
public async void Configure(...., UserSeed userSeedData)
and then inside the method you call
userSeedData.UserSeedData();
Don't use this code as is, you would probably want to check if the user already exists before you create it.
Note: This method will run once every time your application starts.
Related
Need to query Persisted grant tabel to get subjectId of the user.
Created a new controller UserInfo in the Identity server project but not able to call from postman
UserInfoController
[SecurityHeaders]
[AllowAnonymous]
public class UserInfoController : Controller
{
private readonly AppDbContext _context;
public UserInfoController(AppDbContext context)
{
_context = context;
}
// GET: UserInfo
public async Task<IActionResult> Index()
{
return View(await _context.PersistentGrant.ToListAsync());
}
}
I'm useing endpoint routing in the startup file
Something I have struggled with is understanding the lifecycle of the Page Model class for my Razor Pages usages. I'm trying to think about how and when to deal with common data I pass to my business logic like the userId that is running the request. So many times I need to save this information with the results of the action.
So where is the best place to handle something over and over like geting User details that might be in the persistence model and not in the context of the page model's User from the HTTPContext?
I should mention I am using authorize tags with cookie backed authenication to a webservice.
For accessing Reuqest from other layers except the Razor Page, you could try IHttpContextAccessor.
For general way to handling user details from request, you could create a service like below:
public interface IUserService
{
IdentityUser GetUser();
}
public class UserService:IUserService
{
private readonly ApplicationDbContext _context;
private readonly HttpContext _httpContext;
public UserService(ApplicationDbContext context
, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_httpContext = httpContextAccessor.HttpContext;
}
public IdentityUser GetUser()
{
StringValues userId = "";
if (_httpContext.Request.Headers.TryGetValue("userId", out userId))
{
var user = _context.Users.FirstOrDefault(u => u.Id == userId);
return user;
}
else
{
return null;
}
}
}
And then register like
services.AddScoped<IUserService, UserService>();
Then, you could resolve IUserService from DI to use them when you want to access user info.
public class IndexModel : PageModel
{
private readonly IUserService _userService;
public IndexModel(IUserService userService)
{
_userService = userService;
}
public void OnGet()
{
var user = _userService.GetUser();
}
}
I have an app, with multi-tenancy. I want to create background job under user context, but I can't find good way to implement that.
I'll explain a bit my architecture. I'm using Interface ICurrentUser that contain UserID. In Startup class I register as scoped in IoC the class WebUser which implements ICurrentUser, this class getting HttpContext and extract user details from claims.
I'm executing background job and the ICurrentUser.UserID is null as expected because hangfire doesn't have any httpcontext.
I'm solving this problem by creating my background tasks with method which accept ICurrentUser as first argument, then inside method body,
I set my "CurrentUser" for UnitOfWork (and AppServices) and start executing task, the problem with this approach that I have to repeat this code with every background task and pass CurrentUser into it.
My question how can achieve next thing. Or maybe you can suggest other solutions for it.
How can I pass my CurrentUser into JobActivator, to order I can setup user context before all services is resolved.
For Example it may look like that:
BackgroundJob.Enqueue<MySvc>(UserContext, mysvc=>mysvc.Run());
I read sources and really didn't find any extension points to implement this.
Any help is greatly appreciated.
Finally, I finished up with almost the same solution that #jbl suggested.
I've created a filter which stores my current user into the job parameters.
public class BackgroundJobFilter : JobFilterAttribute, IClientFilter, IApplyStateFilter
{
private readonly IServiceProvider _serviceProvider;
public BackgroundJobFilter(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void OnCreating(CreatingContext filterContext)
{
var currentUser = _serviceProvider.GetRequiredService<ICurrentUser>();
filterContext.SetJobParameter(nameof(ICurrentUser), currentUser);
}
}
Then add filter into Hangfire
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
GlobalConfiguration.Configuration.UseFilter(new BackgroundJobFilter(app.ApplicationServices));
}
Then I've replaced current job activator
internal class ServiceJobActivatorScope : JobActivatorScope
{
private readonly IServiceScope _serviceScope;
public ServiceJobActivatorScope([NotNull] IServiceScope serviceScope)
{
if (serviceScope == null)
throw new ArgumentNullException(nameof(serviceScope));
_serviceScope = serviceScope;
}
public override object Resolve(Type type)
{
return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);
}
public override void DisposeScope()
{
_serviceScope.Dispose();
}
}
And finally, set current user details (which is null on the moment of running task)
public class CustomJobActivator : JobActivator
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IMapper _objectMapper;
public CustomJobActivator([NotNull] IServiceScopeFactory serviceScopeFactory, IMapper objectMapper)
{
if (serviceScopeFactory == null)
throw new ArgumentNullException(nameof(serviceScopeFactory));
_serviceScopeFactory = serviceScopeFactory;
_objectMapper = objectMapper;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
var user = context.GetJobParameter<WebUser>(nameof(ICurrentUser));
var serviceScope = _serviceScopeFactory.CreateScope();
var currentUser = serviceScope.ServiceProvider.GetRequiredService<ICurrentUser>();
//Copy value from user to currentUser
_objectMapper.Map(user, currentUser);
return new ServiceJobActivatorScope(serviceScope);
}
}
Then replace the existing JobActivator in container
services.Replace(new ServiceDescriptor(typeof(JobActivator), typeof(CustomJobActivator), ServiceLifetime.Scoped));
After that when services start resolving from this scope they will get user context and all filter in DbContext and other places when I use ICurrentUser works properly.
I want to change my connectionstring at runtime based on the code user enters at login screen. I did the following
ApplicationDbContext
public static ApplicationDbContext Create(string scCode){
return new ApplicationDbContext("name=GEContext_" + scCode);
}
And at login i change the connectionstring as follows
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
System.Web.HttpContext.Current.Session["SchoolCode"] = model.SchoolCode;
var appDbContext = ApplicationDbContext.Create(model.SchoolCode);
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
....
}
}
}
Now it is still referring to the original database...what am i missing?
P.S. For a history/detail consider this post
Hi I Got the Answer from here
The problem was in ApplicationDbContext where we need to specify a default database whereas in my scenario that default database had to change.
So i changed it using
var appDbContext = ApplicationDbContext.Create(System.Web.HttpContext.Current.Session["SchoolCode"].ToString());//new ApplicationDbContext("name=GEContext", System.Web.HttpContext.Current.Session["SchoolCode"].ToString());
HttpContext.GetOwinContext().Set<ApplicationDbContext>(appDbContext);
HttpContext.GetOwinContext().Set<ApplicationUserManager>(new ApplicationUserManager(new UserStore<ApplicationUser, Role, int, UserLogin, UserRole, UserClaim>(appDbContext)));
return HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
I'm trying to create my custom authorize attribute, but in asp.net vnext using the default dependency injection framework I don't how to get the injected object. I need to get the injected object in the default ctor.
public class CustomAttribute
{
private IDb _db;
public CustomAttribute()
{
_db = null; // get injected object
}
public CustomAttribute(IDb db)
{
_db = db;
}
// apply all authentication logic
}
You can use the ServiceFilterAttribute for this purpose. The service filter attribute lets the DI system take care of instantiating and maintaining the lifetime of the filter CustomAuthorizeFilter and its any required services.
Example:
// register with DI
services.AddScoped<ApplicationDbContext>();
services.AddTransient<CustomAuthorizeFilter>();
//------------------
public class CustomAuthorizeFilter : IAsyncAuthorizationFilter
{
private readonly ApplicationDbContext _db;
public CustomAuthorizeFilter(ApplicationDbContext db)
{
_db = db;
}
public Task OnAuthorizationAsync(AuthorizationContext context)
{
//do something here
}
}
//------------------
[ServiceFilter(typeof(CustomAuthorizeFilter))]
public class AdminController : Controller
{
// do something here
}