How to access token's data in dbcontext - c#

I have web api application .net framework 4.6.2 and we store user credentials in the token. I override dbcontext.savechanges to track database changes. I want to access userId which is stored in token and in api I can access that by httpContext. But the HTTP context is not available in dbcontext.

Does System.Web.HttpContext.Current get the current Request Context?
I should point out that adding stuff that references this directly into a DbContext is poor design though... You should probably use Dependency Injection and manage this via some service with a lifetime scoped to the request - or at least that's how I'd do it...

As #GPW said that's a poor design decision.
To solve that situation you need to use IoC to do registrations such as:
// Autofac
builder.Register(c => new HttpContextWrapper(HttpContext.Current))
.As<HttpContextBase>()
.InstancePerRequest();
// Again Autofac
builder.RegisterModule(new AutofacWebTypesModule());
// Castle Windsor
container.Register(Component.For<HttpContextBase()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => new HttpContextWrapper(HttpContext.Current)));
With controllers using contructor injection:
public class HomeController : Controller
{
private readonly HttpContextBase _httpContext;
public HomeController(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
}
So you inject HttpContextBase in order to access the context
public class EntitiesContext : DbContext
{
private readonly HttpContextBase _httpContext;
public EntitiesContext(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
}

Related

Dependency injecting an ASP.NET Identity user (and unit testing)

I don't fully understand how to refactor my app to make it more testable.
I am using ASP.NET Core 3.0 (Razor Pages, if that matters).
Current State:
I have razor pages models that inject in my context (inherited from IdentityDbContext) and userManager:
public class DashboardModel : PageModel
{
private readonly ApplicationDbContext _context;
private readonly UserManager<AVUser> _userManager;
public DashboardModel(ApplicationDbContext context, UserManager<AVUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> OnGetAsync()
{
AVUser = await _userManager.GetUserAsync(User);
MyRepository myRepository = new MyRepository(_context, AVUser);
// omitted - just getting data from the repository for use on the page
return Page();
}
I have repositories created who have constructors as the following:
public MyRepository(ApplicationDbContext context, AVUser user)
I never use the context directly in my Model classes and instead create repositories in the Model classes using the context passed in. In this case ApplicationDbContext is my EF Core context, and AVUser is my type of Identity User, defined as:
public class AVUser : IdentityUser
My DI is set up as:
// DB Setup
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
// Identity Setup
services.AddDefaultIdentity<AVUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
What I don't understand
I want to make my razor pages model classes unit testable without relying on the database or repository classes, mocking up the repository class, so I expect I have to remove the context from the constructor and instead created interfaces for my repositories and DI them.
However, I don't know how to DI in the user parameter for the Repository - how do I set up my service in Startup.cs so this can get passed in? Today to get the value for this, I run:
AVUser = await _userManager.GetUserAsync(User);
If you inject your repositories in the DI container, IoC will automatically inject their dependencies like controllers and pages. so you just need to have a constructor like this in your repository class:
private readonly ApplicationDbContext _context;
private readonly UserManager<AVUser> _userManager;
public MyRepository(ApplicationDbContext context, UserManager<AVUser> userManager)
{
_context = context;
_userManager = userManager;
}
and then inject it to the container:
services.AddTransient<IMyRepository,MyRepository>();
and that's it.
remember to create an interface for your repository so you can inject different instances for either production or testing. Nonetheless, You shouldn't rely on DI for testing. If you are writing unit tests then create a test implementation for your MyRepository and simply instantiate it. If you are writing integration tests then you have to use a different sturtup class.

Mediatr unable to resolve UserManager in ASP.Net Core

I am building ASP.Net Core App depending on this clean architecture example which is using MediatR to execute commands.
And i want to use ASP.Net Core Identity in my app, so in my CreateUserCommandHandler i want to use UserManager to add new user, but when i add UserManager to Command contractor MediatR unable to create the handler and fail with this exception:
System.InvalidOperationException: Error constructing handler for request of type MediatR.IRequestHandler`2[GoGYM.Application.Identity.Commands.CreateUser.CreateUserCommand,MediatR.Unit]. Register your handlers with the container. See the samples in GitHub for examples. ---> System.InvalidOperationException: Unable to resolve service for type 'GoGYM.Persistence.GoGYMDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[GoGYM.Domain.Entities.ApplicationUser,GoGYM.Domain.Entities.ApplicationRole,GoGYM.Persistence.GoGYMDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserRole`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin`1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken`1[System.String],Microsoft.AspNetCore.Identity.IdentityRoleClaim`1[System.String]]'.
In Configure services i register my DBContext and MediatR like this:
// Add AutoMapper
services.AddAutoMapper(new Assembly[] { typeof(AutoMapperProfile).GetTypeInfo().Assembly });
// Add MediatR
services.AddMediatR(typeof(GetUsersListQueryHandler).GetTypeInfo().Assembly);
// Add DbContext using SQL Server Provider
services.AddDbContext<IGoGYMDbContext, GoGYMDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("NorthwindDatabase")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<GoGYMDbContext>();
services.AddMvc();
....
And this my command handler code:
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Unit>
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IGoGYMDbContext _context;
public CreateUserCommandHandler(IGoGYMDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public Task<Unit> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
And my controller
[HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesDefaultResponseType]
public async Task<IActionResult> Create(string values)
{
await Mediator.Send(new CreateUserCommand(values));
return NoContent();
}
I have tried a lot of things and nothing work, only if i remove UserManager from command handler it gets executed then.
You register IGoGYMDbContext with DI but pass in GoGYMDbContext to AddEntityFrameworkStores. GoGYMDbContext isn't registered with DI, so it can't be resolved when requested by the ASP.NET Core Identity framework.
The following changes allow you to register both the interface and the implementation, but using the same implementation instance whether requested via the interface or the implementation:
Remove the interface from the call to AddDbContext:
services.AddDbContext<GoGYMDbContext>(...);
Add a passthrough from the interface to the GoGYMDbContext implementation:
services.AddScoped<IGoGYMDbContext>(sp => sp.GetRequiredService<GoGYMDbContext>());

WebAPI DI Framework that understands HttpContext.User

I am looking for a DI framework that can satisfy this scenario:
Every Controller has a constructor like this
public ThisController( ThatRepository repo)
Every Repository has a controller like this:
public ThatRepository (DataSource ds)
There is one master DataSource, but that is never passed to the repository. Instead it needs:
MasterDataSource.WithUser ( httpContext?.User?.Identity?.Name )
Are there any DI frameworks for WebAPI that would support this out of the box?
I believe that you could use almost every DI container for such a purpose, by registering current HttpContext (or even the current user Identity) inside the container and then injecting it for constructing your DataSource instance.
Here it is a simple solution using HttpContext in SimpleInjector (you may easily port the code to any DI framework you like):
container.Register<HttpContextBase>(() =>
new HttpContextWrapper(HttpContext.Current),
Lifestyle.Scoped);
container.Register<DataSource>(() =>
{
var httpContext = container.GetInstance<HttpContextBase>();
return MasterDataSource.WithUser(httpContext?.User?.Identity?.Name);
},
Lifestyle.Scoped);
Abstract the HttpContext behind an interface you control and have your DI container of choice resolve that when needed.
public interface ICurrentUser {
string Name{get;}
}
A concrete implementation of that interface can look like...
public class UserProvider : ICurrentUser {
HttoContextBase httpContext;
UserProvider(){
httpContext = HttpContext.Current;
}
public string Name {
get{ return httpContext?.User?.Identity?.Name ?? string.Empty; }
}
}
You register your interface/contract with your DI container and resolve it when needed for your DataSource.
public void SomeSetupMethod(ICurrentUser user) {
MasterDataSource.WithUser(user?.Name);
}

How to mock out the UserManager in ASP.NET 5

I am writing a UI for managing users in an ASP.NET 5 app. I need to show any errors returned by the UserManager in the UI. I have the IdentityResult errors being passed back in the view model but I am a touch adrift when it comes to testing my code.
What is the best way to Mock the UserManager in ASP.NET 5?
Should I be inheriting from UserManager and overriding all the methods I am using and then injecting my version of UserManager into an instance of the Controller in my test project?
I have managed it with the help of the MVC Music Store sample application.
In my Unit Test class, I set up the database context and UserManager like this:
public class DatabaseSetupTests : IDisposable
{
private MyDbContext Context { get; }
private UserManager<ApplicationUser> UserManager { get; }
public DatabaseSetupTests()
{
var services = new ServiceCollection();
services.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<MyDbContext>(options => options.UseInMemoryDatabase());
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MyDbContext>();
// Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
// IHttpContextAccessor is required for SignInManager, and UserManager
var context = new DefaultHttpContext();
context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
var serviceProvider = services.BuildServiceProvider();
Context = serviceProvider.GetRequiredService<MyDbContext>();
UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
}
....
}
Then I can use the UserManager in my unit tests, for example:
[Fact]
public async Task DontCreateAdminUserWhenOtherAdminsPresent()
{
await UserManager.CreateAsync(new ApplicationUser { UserName = "some#user.com" }, "IDoComplyWithTheRules2016!");
...
}
If your Dependency Injector is not able to resolve an IHttpContextAccessor then you will not be able to create a UserManager instance due to it being dependent on it.
I think (and this is just an assumption), that with Asp.Net 5, the UserManager does take care of refreshing cookie based claims when you change them (claims, roles...) for a user and therefore requires some HttpContext for login / logout actions and cookie access.

How to injection dependencies into custom Identity UserStore implementation?

I have an ASP.NET Web API project using ASP.NET Identity for authentication/authorization. I've inserted my own custom Identity implementation, namely my own UserStore to talk with Azure Tables, and removed the EF/SQL Server stuff. However, most of the docs I see out there recommend creating an instance of the UserManagerFactory within the Startup.Auth.cs class as follows:
public static Func<UserManager<CustomIdentityModel>> UserManagerFactory { get; set; }
UserManagerFactory = () => new UserManager<CustomIdentityModel>(new CustomUserStore<CustomIdentityModel>());
My CustomUserStore has a dependency on a repository in another project, which I dependency inject into the constructor.
private readonly IRepository _repo;
public CustomUserStore(IRepository repo)
{
_repo = repo;
}
However, creating a "new CustomUserStore()" in the Startup.Auth.cs is an anti-pattern and cannot resolve the dependency. What is the right way to resolve this dependency when creating a UserManager? I'd like to either not have to "new up" a UserManagerFactory, or somehow resolve the dependency inline:
new UserManager<CustomUserModel>(container.Resolve<IRepository>());
Then the question becomes, how to get the single IOC container instance...
Create an static field in your global.asax or owin startup class to hold a reference for ioc container, in your container bootstrap setup set the static field value.
after that you can access the ioc container from somewhere, in your Startup.Auth.cs class you can read the ioc static field container and initialize inline UserStore.
Owin Class:
public class Startup {
public static IUnityContainer Container; //Static field for hold ioc container reference.
public void Configuration(IAppBuilder app)
{
//Code Omitted for brevity
Container = YourIoCContainerReference:
}
}
}
So you can do an inline initialization:
var repository = Startup.Container.Resolve<IRepository>();
and get your dependency.

Categories

Resources