How to configure multiple UserManager with different DbContext - .net-core

I'm struggling with Net Core Identity, more specifically the UserManager API. I have two databases with the same logic structure, one of them is for testing and homologation purposes. I looked around for a good while and managed to somehow make the segregated access work under the same application, creating a "homologation ambient". Since I still have to perform user and role-related management, authentication, and other tasks, it is in my interest to keep the same functions of the default UserManager, but looking at a different database. And now I'm stuck with this: the GeneratePasswordResetTokenAsync() method from UserManager fails, depending on how I set the StartUp.cs:
StartUp.cs
services.Configure<IdentityOptions>(opt =>
{
opt.User.RequireUniqueEmail = true;
opt.Password.RequiredLength = 8;
opt.Lockout.AllowedForNewUsers = false;
opt.SignIn.RequireConfirmedAccount = true;
opt.SignIn.RequireConfirmedEmail = true;
opt.Tokens.PasswordResetTokenProvider = "passwordReset";
opt.Tokens.EmailConfirmationTokenProvider = "emailConfirmation";
});
services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<DNDriveContext>()
.AddTokenProvider<TwoFactorTokenProvider<User>>("passwordReset")
.AddTokenProvider<EmailConfirmationTokenProvider<User>>("emailConfirmation");
services.AddIdentityCore<UserHom>()
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<UserHom, IdentityRole>>()
.AddEntityFrameworkStores<DNDriveHomContext>()
.AddTokenProvider<TwoFactorTokenProvider<UserHom>>("passwordReset")
.AddTokenProvider<EmailConfirmationTokenProvider<UserHom>>("emailConfirmation");
Through my research on the topic, I came across the AddIdentityCore<TUser>() method above, and it works fine in creating a second UserManager<TUser> that I can access in the controller via DI. The roles work fine too, and I can perform token validations segregated from the "production" environment, but the moment I try to call GeneratePasswordResetTokenAsync(), I get the following error:
No IUserTwoFactorTokenProvider named 'passwordReset' is
registered.
I tried passing the IdentityOptions to each Identity individually instead of calling services.Configure<IdentityOptions>, but it didn't make a difference. I assume this is an issue of assignment since only one of the UserManagers works at a time. With the above configuration, UserManager<UserHom> will successfully generate the reset password token, but the main UserManager<User> will throw the above-mentioned error. Commenting the two AddTokenProvider<> at the end of AddIdentityCore<UserHom> fixes the main UserManager, but now the homologation one fails.
I'm aware of the possibility of making a custom UserManager, but as I said in the beginning, I would like to keep all the default functions of UserManager, and I have no idea of how to start implementing one of my own. What exactly is the explanation for what is happening and how do I fix this?

Related

Mocking a signed in Identity User

I am using Moq and xUnit to create unit tests. However, because of the way my action methods are set up, I'm having a hard time doing so. In my controller, I use
var user = await userManager.FindByNameAsync(User.Identity.Name);
to get the the signed in user information. I used this approach to avoid the number of parameters Is there a way to mock this in a unit test, or should the controller be set up differently?
The AppUser class extends IdentityUser for specific properties that are used within the controller.
For mocking above you can do something like following,
Mock<UserManager<User>> userManagerMock = new Mock<UserManager<User>>();
userManagerMock
.Setup(m => m.FindByNameAsync(It.IsAny<string>()))
.Returns(Task.FromResult(It.IsAny<User>()));
Please note I haven't compiled this code, but you should be able to get the idea.

Accessing ASP.NET Identity Data from DataAccess Layer

I have an n-tiered solution that uses MVC-WebApi, Owin, ASP.NET Identity and EntityFramework. I used the sample Identity code as a starter and added some custom properties to the ApplicationUser. I've also configured all of the classes to use Guids for Identity values. All working great so far.
I started having difficulty when I wanted to move the IdentityConfig and IdentityModel files from the sample code out of the MVC project and to the DataAccess project. These files define several key classes such as ApplicationUserManager, ApplicationRoleManager, etc. Within the ApplicationDbInitializer, there is code to get the ApplicationUserManager and ApplicationRoleManager using the current HttpContext for the purpose of seeding the database. It seems pretty unnecessary to require an HttpContext to seed the database. Also, there are other background process that I will be adding soon that will need to access Identity data from the business layer without an HttpContext being relevant, so I'd like to get that dependency removed now.
I saw the Q&A on this subject ASP Identity - Accessing HttpContext on a reference library, but the best answer was pretty thin. I tried replacing
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
with
var userManager = new ApplicationUserManager(new ApplicationUserStore(db));
var roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole, Guid, IdentityUserRole>(db));
but that REALLY lead down a rabbit hole. The code wouldn't compile unless I defined custom implementations of every Identity class and implemented interfaces for them, etc. It needed all of the following (possibly more, I didn't get it finished)
ApplicationUserRole: IdentityUserRole
ApplicationRole : IdentityRole, IRole
ApplicationUserClaim : IdentityUserClaim
ApplicationUserLogin : IdentityUserLogin
and IRole needed to be fully implemented, which I'm not sure how to do.
I stopped at this point because it feels like there should be an easier way or some sample code I can look at. I don't really need anything customized beyond ApplicationUser. Am I going down the right path here or is my gut correct and there's a better way?

Configure Autofac Container for background thread

I have an asp.net MVC site which has many components registered using an InstancePerHttpRequest scope, however I also have a "background task" which will run every few hours which will not have an httpcontext.
I would like to get an instance of my IRepository which has been registered like this
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerHttpRequest();
How do I do this from a non http context using Autofac? I think the IRepository should use the InstancePerLifetimeScope
There are several ways of how you can do that:
The best one in my opinion. You can register the repository as InstancePerLifetimeScope as you said. It works with HttpRequests and LifetimeScopes equally well.
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope();
Your registration for HttpRequest may differ from registration for LifetimeScope, then you can have two separate registrations:
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.WithParameter(...)
.InstancePerHttpRequest(); // will be resolved per HttpRequest
builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope(); // will be resolved per LifetimeScope
You can explicitly create "HttpRequest" scope using its tag. Exposed through MatchingScopeLifetimeTags.RequestLifetimeScopeTag property in new versions.
using (var httpRequestScope = container.BeginLifetimeScope("httpRequest")) // or "AutofacWebRequest" for MVC4/5 integrations
{
var repository = httpRequestScope.Resolve<IRepository<Entity>>();
}

Autofac Multi-tenant IoC Container in an ASP.NET Web API Application

Autofac 3.0 will have a MultitenantIntegration support and its preview release is out now. To try it out, I created an ASP.NET Web API application with the following configuration:
public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
var config = GlobalConfiguration.Configuration;
config.Routes.MapHttpRoute("Default", "api/{controller}");
RegisterDependencies(config);
}
public void RegisterDependencies(HttpConfiguration config) {
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// creates a logger instance per tenant
builder.RegisterType<LoggerService>().As<ILoggerService>().InstancePerTenant();
var mtc = new MultitenantContainer(
new RequestParameterTenantIdentificationStrategy("tenant"),
builder.Build());
config.DependencyResolver = new AutofacWebApiDependencyResolver(mtc);
}
}
It gets the job done and creates a LoggerService instance as ILoggerService per tenant. I have two problems at this stage which I wasn't able to solve:
I used out of the box provided RequestParameterTenantIdentificationStrategy here as the TenantIdentificationStrategy just for this demo application. I am able to create my custom TenantIdentificationStrategy by implementing ITenantIdentificationStrategy interface. However, TryIdentifyTenant method of the ITenantIdentificationStrategy makes you rely on a static instance such as HttpContext.Current which is something that I don't want in an ASP.NET Web API environment as I want my API to be hosting agnostic (I know that I can delegate this work to the hosting layer but I would rather not to). Is there another way to achieve this in a way that I won't rely on a static instance?
I also have a chance to register tenant specific instance as below:
mtc.ConfigureTenant("tenant1", cb => cb.RegisterType<Foo>()
.As<IFoo>().InstancePerApiRequest());
However, one of my situations requires me to pass the tenant name through the constructor parameter and I would love to have something like below:
mtc.ConfigureTenant((cb, tenantName) => cb.RegisterType<Foo>()
.As<IFoo>()
.WithParameter("tenantName", tenantName)
.InstancePerApiRequest());
Currently there is no such an API. Is there another way to achieve this or this kind of requirement doesn't make any sense?
Multitenant support has been available for a long time, it's just that 3.0 is the first time we've had a NuGet package for it. :)
The RequestParameterTenantIdentificationStrategy is, as documented, just a very simple example showing one possible (and not recommended) way to identify tenant. You will have to choose for yourself how to identify your tenant based on the operating context. It could be from a web.config value, an environment variable, or some other thing in the current environment. If you don't want to use HttpContext.Current, don't. It's up to you to pick where you get that info from.
(A note on the RPTIStrategy - the part that isn't recommended is using a querystring or request parameter as the tenant ID mechanism. I use HttpContext in my production apps and it works fine. There's only so much you can abstract out before you have to actually touch the bare metal.)
There is no way out of the box to provide the lambda registration syntax you're asking for, primarily because tenant is not passed through the resolution process. The resolution process is:
Identify the tenant with the strategy.
Find the tenant's configured lifetime scope.
Use standard Autofac Resolve style syntax.
It's intentionally simple and analogous to the existing operations. At the time of resolve, the sub-lifetime-scope belonging to the tenant is tagged with the tenant ID but the resolution operation doesn't know about the tenant ID... so the lambda wouldn't work (and probably won't anytime soon because it'd change the fundamental internals of the way Autofac works if it did).
To accomplish what you're looking for, you can use a combination of the InstancePerTenant extension when registering...
var builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<IFoo>().InstancePerTenant();
...and registering the ITenantIdentificationStrategy as a dependency in your container.
builder.Register(myIdStrategy).As<ITenantIdentificationStrategy>();
Then make your class take an ITenantIdentificationStrategy rather than the tenant ID directly. Use the strategy to get the tenant ID instead.
If you REALLY want to get fancy, you could register a keyed lambda that resolves the ID strategy, then gets the tenant ID. Then you could add a parameter registration to the object like you did but using a keyed service. (I'm going to go by memory now, so you'll have to double-check my syntax here, but it'll be something like this...)
builder.Register(c =>
{ var s = c.Resolve<ITenantIdentificationStrategy>();
object id;
s.TryIdentifyTenant(out id);
return id;
}).Keyed<object>("tenantId");
builder.RegisterType<Foo>()
.As<IFoo>()
.WithParameter(
(pi, c) => pi.Name == "tenantId",
(pi, c) => c.ResolveKeyed<object>("tenantId"))
.InstancePerApiRequest();
Again, you'll want to double-check me on that, but I'm pretty sure that (or a minor variation) should work to get you what you want.

Switch to SQL membership provider from AD membership provider runtime

In my asp.net application admin functionality, I am trying to combine AD authentication and form authorization for creating the users, roles and Assign users to roles etc. I have configured MembershipADProvider and AspNetSqlMembershipProvider in my web.config with MembershipADProvider as the default one. After user logs in using AD authentication, I need to switch/assign my membership object to use AspNetSqlMembershipProvider in order to get all the users from membership object (from dbo.aspnet_Users table). How do I switch the provider during run time? I have tried different approaches after searching for this issue and none of that seem to work for me so far.
Here are couple of approaches I tried:
1. foreach (MembershipProvider mp in Membership.Providers)
{
if (mp.Name == "MembershipADProvider")
{
Membership.Providers.Remove(MembershipADProvider");
MembershipUserCollection users = Membership.GetAllUsers();
ddlUsers.DataSource = users;
ddlUsers.DataBind();
break;
}
}
Membership.Providers.Remove(MembershipADProvider"); - doesn't work as it's not supported..
Also, tried to clear the Membership.Providers and then add only the type of AspNetSqlMembershipProvider which are also not supported.
I can't set Membership.Provider with value from
Membership.Providers["AspNetSqlMembershipProvider"] as Membership.Provider is a read only property.
I tried to swtich the connection string between 2 providers, which didn't swtich the provider, as both are different types of providers..if both were sqlserver providers this would have worked I believe.
Please let me know if anybody has successfully implemented or if at all this is a plausible approach. Thank You!
You would pass an explicit provider to your code, rather than taking a dependency on Memebership directly (which just wraps the one flagged as default in the config). There is no need to swap them in and out at runtime, think how this would affect thread safety.
So rather than saying Membership.GetAllUsers(); you would do something like (I don't have a compiler to hand):
public UserSerivce : IUserService
{
private MembershipProvider provider;
public UserService(MembershipProvider provider)
{
this.provider = provider;
}
public IEnumerable<MembershipUser> GetUsers()
{
return provider.GetAllUsers();
}
public void DoSomethingElseUseful()
{
...
}
}
And then to use it for a particular provider:
var service = new UserService(Membership.Providers["mySqlMembershipProvider"]);
var users = service.GetUsers();
Or if using AD specific code:
var service = new UserService(Membership.Providers["myADMembershipProvider"]);
var users = service.GetUsers();
Using DI in this way also helps keep code testable.
If all you need a list of users in the aspnet_Users table, just connect to your database with System.Data.SqlClient objects and query the table. There is no reason (that you mentioned) you need to use a membership provider to get that data.
Having said that, your membership/authentication scheme sounds like it may have some design issues, perhaps best tackled in a different question, but I think it might be useful to you if you sought comment on what you are trying to accomplish overall with the multiple membership providers.
Edit: I found some potentially useful posts on using multiple membership providers. It looks like the general idea is to implement custom code handling the Login.Authenticate event on your Login control, and use Membership.Providers["ProviderName"].ValidateUser to attempt authentication with each provider.
http://www.stevideter.com/2008/03/20/using-two-membership-providers-for-aspnet-logins/
http://forums.asp.net/p/1112089/1714276.aspx

Resources