I have a problem implementing forms authentication with an IOC container in my ASP.NET MVC 3 project. We have stored our user information in the database and has a lot of custom properties.
I have an interface of my user definition registrated to the IOC container for development purposes. This interface is given to each controller so the controllers has current user information.
This al works fine until i remove the dummy user registration in the Application_Start
I receive this error:
The current type, ...CurrentUserInformation.IUserInformation, is an interface and cannot be constructed. Are you missing a type mapping?
I don't want to work with a dummy user object because I think this is not the best practice.
Can sombody help me or is there a better way to do this custom authentication?
edit added some code
BaseController
public class BaseController : Controller
{
private readonly IUserInformation _userInformation;
public BaseController(IUserInformation userInformation)
{
_userInformation = userInformation
}
}
Bootstrapper Initialize called from Application_Start
public static void Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
//register all services en repositories
//here i put my dummy user wich i want to remove
container.RegisterInstance<IUserInformation>(
new UserInformation
{
UserId = 1,
...
});
return container;
}
You can use InjectionFactory:
container.RegisterType<IUserInformation, UserInformation>(
// User information is destroyed when the request ends.
// You could use an HttpSessionLifetimeManager as well, if it fits your needs
new HttpRequestLifetimeManager(),
new InjectionFactory(container => {
UserInformation userInfo = // TODO: build your userInformation from custom authentication
return userInfo;
}));
Related
I'm new on the asp net boilerplate framework, and i created a new mvc project multipage web application, without module zero.
I would like to use the AbpSession class that from what I understand has inside the user id that is taken over Thread.CurrentPrincipal.
However, I do not understand how to do after login, to save the user id in the Thread.CurrentPrincipal.
I've searched in the network and found several solutions, but in the AbpSession class the user id is always null.
The most optimal solution I found was this:
IList<Claim> claimCollection = new List<Claim>
{
new Claim(AbpClaimTypes.UserId, "5")
};
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection);
var principal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = principal;
It's the first time I use principal and identity and despite being documented I did not quite understand how to use them with asp net boilerplate, and I did not find sample codes.
Do you know how to tell me the right way or tell me where to find some functional codes?
Thanks
Start expanding AbpSession
The last section has cleared the way of thinking. Let's roll up our sleeves and expand in this section.
AbpSession attributes have been injected into three base classes: Application Service, AbpController and ABP ApiController.
So we need to extend AbpSession at the domain level, which is the project at the end of. Core.
Now suppose we need to extend an Email attribute.
Extending IAbpSession
Locate the project at the end of. Core, add the Extensions folder, and then add the IAbpSession Extension interface inherited from IAbpSession:
namespace LearningMpaAbp.Extensions
{
public interface IAbpSessionExtension : IAbpSession
{
string Email { get; }
}
}
Implementing IAbpSession Extension
Add the AbpSession Extension class, which is based on Claims AbpSession and implements the IAbpSession Extension interface.
namespace LearningMpaAbp.Extensions
{
public class AbpSessionExtension : ClaimsAbpSession, IAbpSessionExtension, ITransientDependency
{
public AbpSessionExtension(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{}
public string Email => GetClaimValue(ClaimTypes.Email);
private string GetClaimValue(string claimType)
{
var claimsPrincipal = PrincipalAccessor.Principal;
var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);
if (string.IsNullOrEmpty(claim?.Value))
return null;
return claim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
return claim;
}
Replace the injected AbbSession attribute
First replace the injected ABP Session in AbpController
Locate. ApplicationxxxControllerBase:AbpController. CS and inject IAbpSession Extension with attributes. Add the following code:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Replace the injected ABP Session in Application Service
Locate. ApplicationxxxAppServiceBase.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Chaneg the injected ABP Session in Views AbpRazorPage
Locate. ApplicationxxxRazorPage.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
[RazorInject]
public IAbpSessionExtension AbpSession { get; set; }
Altough the question is very general, i would like to share you some code about how to add custom field to AbpSession in ASP.NET Core.
MyAppSession.cs
//Define your own session and add your custom field to it
//Then, you can inject MyAppSession and use it's new property in your project.
public class MyAppSession : ClaimsAbpSession, ITransientDependency
{
public MyAppSession(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{
}
public string UserEmail
{
get
{
var userEmailClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_UserEmail");
if (string.IsNullOrEmpty(userEmailClaim?.Value))
{
return null;
}
return userEmailClaim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim("Application_UserEmail", user.EmailAddress));
return claim;
}
I am getting this exception while debugging code:
Resolution of the dependency failed, type = "Aqueduct.Interfaces.IMasterDataClient", name = "MDS".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type,
Aqueduct.Interfaces.IMasterDataClient, is an interface and cannot be constructed. Are you missing a type mapping?
At the time of the exception, the container code was:
Resolving Aqueduct.Interfaces.IMasterDataClient,MDS
Unity.config:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
IUnityContainer myContainer = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
myContainer.RegisterType<IMasterDataClient, MasterDataClient>("MDS");
myContainer.RegisterType<ILinksManager, LinksManager>("LDS");
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Web Api controller:
using (var container = new UnityContainer())
{
container.Resolve<IMasterDataClient>("MDS");
container.Resolve<ILinksManager>("LDS");
}
MasterDataClient Interface Implementing class:
public class MasterDataClient : HalClient, IMasterDataClient, IHalClient, ICrestaClient
{
public MasterDataClient(IApiClient apiClient, IUriDispenser dispenser);
}
You have three problems here.
Problem 1. You're creating a new container:
using (var container = new UnityContainer())
{
container.Resolve<IMasterDataClient>("MDS");
container.Resolve<ILinksManager>("LDS");
}
This container will be empty and have 0 registrations. It's not the same container as you made your registrations to. There's a couple of ways to solve it:
Problem 1 - Solution 1: Don't inject the container. Inject the dependencies.
Instead of using the container you should inject your dependencies directly to your controller:
public class MyApiController : ApiController
{
IMasterDataClient _masterDataClient;
ILinksManager _linksManager
public MyApiController(IMasterDataClient masterDataClient, ILinksManager linksManager)
{
_masterDataClient = masterDataClient;
_linksManagerlinksManager;
}
}
This way you can avoid the service locator-pattern and your dependency to the container itself. If you're using named registrations you may need to use the Dependency-attribute.
With Unity how do I inject a named dependency into a constructor?
Problem 1 - Solution 2: Inject your container instead of creating a new one.
If you really need your controller for some reason, then you should try to inject it instead. This way you will get the same container as you used for your registrations.
public class MyApiController : ApiController
{
IUnityContainer _container;
public MyApiController(IUnityContainer container)
{
_container = container;
}
}
Problem 1 - Solution 3: Keep an static reference to your container.
As a last resort you can keep your container as a static instance.
public static class IocContainer
{
private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
return container;
});
public static IUnityContainer Instance
{
get { return Container.Value; }
}
}
And in your registrations:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = IocContainer.Instance;
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
myContainer.RegisterType<IMasterDataClient, MasterDataClient>("MDS");
myContainer.RegisterType<ILinksManager, LinksManager>("LDS");
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Usage:
IocContainer.Instance.Resolve<IMasterDataClient>("MDS");
Problem 2. You don't seem to register the dependencies for MasterDataClient.
It has dependencies for IApiClient and IUriDispenser. They need to be registered as well.
Problem 3. You assign your dependency resolver to the wrong container
You currently create two containers - myContainer and container. You make your registrations to one container, and then use another one as DependencyResolver. You can solve it by removing myContainer and only use container. Otherwise you will not be able to use your registrations in Web Api.
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<IMasterDataClient, MasterDataClient>("MDS");
container.RegisterType<ILinksManager, LinksManager>("LDS");
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Update:
I don't know implementing classes for IApiClient and IUriDispenser as
it is third party dll so how can I register it
Unity needs to know which implementation to use when you resolve an interface. So somehow you need to tell Unity what implementation to use. There's an excellent answer from Mark Seemann in the question below:
Unity IoC for resolving assemblies dynamically
It scans the assembly for implementations of your interface and then registers them/it.
Alternatively you can use registration by convention, which enables you to do several registrations at once:
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMapping.MatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
Note that this will register all implementations, including your own.
If you just want the third party implementations you should be able do do something like this:
// Get the assemblies where IApiClient exists.
IEnumerable<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.GetTypes().Contains(typeof (IApiClient)));
// Register all implementations based on convention.
container.RegisterTypes(AllClasses.FromAssemblies(assemblies),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled); // Maybe another lifetime manager?
Update 2:
Make sure that the controller has a parameterless public constructor.
The error above can have many causes, most of them are covered in these questions:
Unable to inject DBContext into my Web API 2 Controller with Unity
Make sure that the controller has a parameterless public constructor error
error: Make sure that the controller has a parameterless public constructor webapi
Mostly it's Unity telling you that you're trying to resolve something that you didn't register. Or that you haven't registered a DependencyResolver at all.
I have a webapi project and a repositories project.
I have configured to use oauth, which uses owin middleware bearer token authentication.
I have a unitofwork with multiple repositories.
Inside the repositories I want to filter data based on the logged on user.
I would like all repositories to get the logged in user via dependency injection.
I can access the logged on user in the webapi action, but I am struggling to work out if/how I can inject the current user using DI; because the authorization is happening via the webapi Authorize?:
[Authorize(Roles = "User")]
public IQueryable<Folder> Folders()
{
// return UnitOfWork.FolderRepository.All().OrderBy(o=>o.FolderId).Skip(10).Take(50);
var test = HttpContext.Current.GetOwinContext().Authentication;
//test is populated with the logged on user here, but I don't want to set the user details of the UOW in every action in my controllers
return UnitOfWork.FolderRepository.All();
}
So in the action Folders the Authorize annotation logs the user on. But I have already instantiated the unit of work in the controller constructor with DI:
public FolderController(IUnitOfWork uow, UserManager<IdentityUser,int> usermanager)
{
UnitOfWork = uow;
UserManager = usermanager;
}
IOC container:
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.For<HttpContextBase>()
.HybridHttpOrThreadLocalScoped()
.Use(() => new HttpContextWrapper(HttpContext.Current));
x.For<IUnitOfWork>().HttpContextScoped().Use(
() => new UnitOfWork(new BreezeValidator
(new UserManager<AspNet.Identity.SQLServer.IdentityUser, int>(new UserStore(new SqlDatabase()))))
);
}
}
I had tried to pass in HttpContext.Current.GetOwinContext(), but at that point the authorization hasn't taken place and so the Principal has not been set.
I have looked at actionfilters (which are run after the authorization filter), but can't figure out how I would return a new unit of work instance with the logged on user set, back to the controller.
...Or whether I can set a property on the controller from an action filter?
So the question is really, how can I set the user details in all my controller's unitofwork, without lots of duplication?
Thanks
EDIT: I have a working solution, but still not sure it's the right way to go:
I created an action filter and then from there get the controller and set a UserPrincipal property on the controller's unitOfWork property.
using Project1.Web.Controllers;
using System;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Project1.Web.Filters
{
public class InjectUserAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var action = actionContext.ControllerContext.Controller;
UowApiController ctrl = (UowApiController)actionContext.ControllerContext.Controller;
ctrl.UnitOfWork.UserPrincipal = actionContext.RequestContext.Principal;
}
}
Then, in my UnitOfWork setter of the UserPrincipal I set the UserPrincipal in the contained repositories:
public IPrincipal UserPrincipal
{
get
{
return this.userPrincipal;
}
set
{
this.userPrincipal = value;
((Repository<Folder>)FolderRepository).UserPrincipal = value;
}
}
This works now, but it doesn't achieve dependency injection.
Also I would like to know if this is a "right" way to do it, or what would be a better approach?
I was searching for the same thing and decided on this. I think this answer will be relevant to you as well.
Proper way to dependency inject authenticated user to my repository class
I've just added a getter to the service classes that accesses the user identity at request time.
public class MyService
{
//ctor...
public IEnumerable<Results> GetResults()
{
return _ResultRepository.GetResultsByUser(UserIdentity);
}
IIdentity UserIdentity
{
get { return Thread.CurrentPrincipal.Identity; }
}
}
I'm trying to get the following scenario using autofac but I'm not sure how my code will be built to get this up & running.
I have a repository class, this repository class needs to get a project key (string) on initialization (constructor). I want to instantiate this repository in initialization of my "Initialize" method provided to my by Web Api, because the project key will be available in my route.
so instead of calling "new ProductRepository(projectKey)", I want to use Autofac. Can someone point me in the right direction? I didn't find any way to send in specific data to the container in web api, since the container/builder is only available in the appStart.
Should I make the container available as a singleton so that I can approach it, or is this bad practice?
in your initialization code:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
var resolver = new AutofacWebApiDependencyResolver(container);
config.DependencyResolver = resolver;
in your controller:
public class MyController : ApiController
{
public MyController(IComponentContext container)
{
var key = new NamedParameter("projectKey", "keyFromRoute");
var repository = container.Resolve<ProductRepository>(key);
}
}
That should do it.
There is a nuget package that provides a DependencyResolver for WebApi that integrates with AutoFac. Create the DependencyResolver, assign it to the config, register your controllers in the autofac container.
I'm making some assumptions because you didn't provide your code, but I think you have something like this:
public class ProductRepository
{
public ProductRepository(DbContext dbContext, int projectKey)
{
}
}
public class SomeController : Controller
{
private readonly Func<int, ProductRepository> _repoFactory;
public SomeController(Func<int, ProductRepository> repoFactory)
{
_repoFactory = repoFactory;
}
public void DoStuff(int projectKey)
{
var repo = _repoFactory(projectKey);
repo.DoStuff();
}
}
public class RepositoryModule : Module
{
public override Load(ContainerBuilder builder)
{
builder.RegisterType<ProductRepository>();
}
}
I want to use RavenDB with ninject in my asp.net mvc3 project, Any idea how I have to configure this?
kernel.Bind<Raven.Client.IDocumentSession>()
.To<Raven.Client.Document.DocumentStore>()
.InSingletonScope()
.WithConstructorArgument("ConnectionString", ConfigurationManager.ConnectionStrings["RavenDB"].ConnectionString);
Here's how I do mine:
If you install Ninject with Nuget, you'll get an /App_start/ NinjectMVC3.cs file. In there:
private static void RegisterServices(IKernel kernel)
{
kernel.Load<RavenModule>();
}
Here's the RavenModule class:
public class RavenModule : NinjectModule
{
public override void Load()
{
Bind<IDocumentStore>()
.ToMethod(InitDocStore)
.InSingletonScope();
Bind<IDocumentSession>()
.ToMethod(c => c.Kernel.Get<IDocumentStore>().OpenSession())
.InRequestScope();
}
private IDocumentStore InitDocStore(IContext context)
{
DocumentStore ds = new DocumentStore { ConnectionStringName = "Raven" };
RavenProfiler.InitializeFor(ds);
// also good to setup the glimpse plugin here
ds.Initialize();
RavenIndexes.CreateIndexes(ds);
return ds;
}
}
And for completeness here's my index creation class:
public static class RavenIndexes
{
public static void CreateIndexes(IDocumentStore docStore)
{
IndexCreation.CreateIndexes(typeof(RavenIndexes).Assembly, docStore);
}
public class SearchIndex : AbstractMultiMapIndexCreationTask<SearchIndex.Result>
{
// implementation omitted
}
}
I hope this helps!
I recommend using a custom Ninject Provider to set up your RavenDB DocumentStore. First place this in your code block that registers your Ninject services.
kernel.Bind<IDocumentStore>().ToProvider<RavenDocumentStoreProvider>().InSingletonScope();
Next, add this class that implements the Ninject Provider.
public class RavenDocumentStoreProvider : Provider<IDocumentStore>
{
var store = new DocumentStore { ConnectionName = "RavenDB" };
store.Conventions.IdentityPartsSeparator = "-"; // Nice for using IDs in routing
store.Initialize();
return store;
}
The IDocumentStore needs to be a singleton, but do not make the IDocumentSession a singleton. I recommend that you simply create a new IDocumentSession using OpenSession() on the IDocumentStore instance Ninject gives you whenever you need to interact with RavenDB. IDocumentSession objects are very lightweight, follow the unit-of-work pattern, are not thread-safe, and are meant to be used and quickly disposed where needed.
As others have done, you might also consider implementing a base MVC controller that overrides the OnActionExecuting and OnActionExecuted methods to open a session and save changes, respectively.