Autofac: How to inject IPrincipal into Repository layer - c#

I am designing an n-tier application using Repository Layer/Service Layer/Presentation Layer using c#.net web api and Autofac DI container. Here is my dilemma. I am trying to unit test my web api controllers but my repositories have a property dependency on IPrincipal which I would like to property inject into my repository layer. I would like to create a MockUser(IPrincipal) and inject this object into my repository. Here is my current hierarchy, my controllers are constructor injected with the service object, my service object is constructor injected with my repository object. This part seems to work. But for some reason, every time I run the test my Principal property is null. Please review the code below and let me know what I am doing wrong:
Repository Base Class:
protected IPrincipal Principal
{
get { return _principal; }
}
Autofac Config Static Method
public class AutofacConfig
{
public static IContainer ConfigContainer()
{
var _builder = new ContainerBuilder();
UserPrincipal principal = MemberFactory.GetTestUser();
var _config = new HttpConfiguration();
_builder.RegisterControllers(typeof(BillingController).Assembly);
_builder.RegisterWebApiModelBinders(typeof(BillingController).Assembly);
_builder.RegisterApiControllers(typeof(BillingController).Assembly);
_builder.RegisterModelBinders(typeof(BillingController).Assembly);
_builder.RegisterModelBinderProvider();
_builder.RegisterModule(new AutofacWebTypesModule());
_builder.RegisterSource(new ViewRegistrationSource());
_builder.RegisterFilterProvider();
_builder.RegisterWebApiFilterProvider(_config);
//_builder.Register(x => principal).As<IPrincipal>().PropertiesAutowired();
_builder.RegisterType<BillingRepository>().As<IBillingRepository>().PropertiesAutowired();
_builder.RegisterType<UserPrincipal>().As<IPrincipal>().PropertiesAutowired();
_builder.RegisterType<GroupRepository>().As<IGroupRepository>().PropertiesAutowired();
_builder.RegisterType<BillingService>().As<IBillingService>().PropertiesAutowired();
_builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();
_builder.Register(c => principal).As<IPrincipal>();
var _container = _builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(_container));
// Create the depenedency resolver.
var resolver = new AutofacWebApiDependencyResolver(_container);
// Configure Web API with the dependency resolver.
_config.DependencyResolver = resolver;
return _container;
}
}
Test Controller (Get Method)
[TestClass]
public class BillingControllerTest
{
[TestMethod]
public async Task Get()
{
var _container = AutofacConfig.ConfigContainer();
var _controller = _container.Resolve<BillingController>();
var _bodyCompRecords = await _controller.GetMyOutstandingBills(1, 10);
Assert.IsNull(_bodyCompRecords);
Assert.IsNull(_bodyCompRecords.BillingList);
Assert.IsNull(_bodyCompRecords.CurrentPage);
Assert.IsTrue(_bodyCompRecords.BillingList.Count > 0);
}
}

Did you try to add a protected set for this property? I am not sure about autofac but maybe you should decorate this property with some attribute to autoFac knows that is a injectable property.
On the other hand, all threads in .Net applications has a single principal. You can get/set it on the Thread.CurrentPrincipal static property. You could try to set a custom Principal and make your property to result it.
In the setup of your Unittest, you could define it, for sample:
void Setup()
{
IPrincipal principal = new GenericPrincipal(new GenericIdentity("userName"), new string[] { "role1", "role2" });
Thread.CurrentPrincipal = principal;
}
In your repository, you could have a property to expose it,
protected IPrincipal Principal
{
get { return Thread.CurrentPrincipal; }
}
In a Web application (asp.net webforms/mvc/web api), you could use the HttpContext.Current.User static property, so, just call:
HttpContext.Current.User = principal;
as a good pratice, you could set both HttpContext.Current.User and Thread.CurrentPrincipal. Remember the web api is stateless, so, implement a http module to read arguments from http header and set the principal to your application. I am not sure if it is your case but this article shows how to do this.
http://www.asp.net/web-api/overview/security/basic-authentication

Related

How to moq HttpContext on Asp net Core

I have an asp .net core project, and practically in each action we use session, my question is how to test it if I don't have sessionRepository. Controller tests crush because session in controller is null. I try to Moq IHttpContextAcessor and it also doesn't work.
I try this:
HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
but HttpContext doesn't contain the definition for Current. (using Microsoft.AspNetCore.Http;)
Is there any way to moq or test controllers that use HttpContext and sessions?
You can use ControllerContext to set the context to be DefaultHttpContext which you can modify to your needs.
var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext()};
var tested = new MyCtrl();
tested.ControllerContext = ctx;
The controller has an Controller context which you can set (i used the default one) :
Controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext()
};
After time I think is bad idea to moq Session, better way is to create a IOC container, for example we create ISessionManager interface with methods which return stored in session objects:
public class SessionManager:ISessionManager{
public User GetUser(){
User usr= JsonConvert.DeserializeObject<User>(HttpContext.Session.GetString("USER"));
return usr;
}
***here we get data from session***
}
for UnitTest we just create a new class which implement ISessionManager and use it for test Controller's.
public class SessionManagerTest:ISessionManager{
public User GetUser(){
User usr=new User();
...initialize all fields
return usr;
}
******
}

.NET Web Api 2 UserPrincipal.IsAuthenticated always false when using dependency injection (Unity)

I have a custom authorization attribute which verifies token inside header and sets user-principal.
//when token is validated
var userIdentityBase = new UserIdentityBase(token); <-- Inherited GenericIdentity
IPrincipal principal = new GenericPrincipal(userIdentityBase, null);
actionContext.Request.GetRequestContext().Principal = principal;
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
This works fine when I check for Identity inside controller
But when i inject IPrincipal to my service classes, it does not work anymore. IsAuthenticated is false.
Unity setup code:
container.RegisterType<IPrincipal>(new InjectionFactory(u => HttpContext.Current.User));
When injected, it does not work (both screenshots are taken withing same request):
Any suggestions?
I've solved this by creating own interface and implementation, which will extract user identity at runtime. Works like a charm for my case
Interface
public interface IIdentityProvider
{
IPrincipal GetPrincipal();
}
Implementation:
public class IdentityProvider : IIdentityProvider
{
public IPrincipal GetPrincipal()
{
return HttpContext.Current.User;
}
}
unity registration:
container.RegisterType<IIdentityProvider, IdentityProvider>();
And usage:
IIdentityProvider _identityProvider;
public BookingRecordService(IIdentityProvider identityProvider)
{
_identityProvider = identityProvider;
var isAuth = _identityProvider.GetPrincipal().Identity.IsAuthenticated;
}
RequestContext.Principal.Identity.IsAuthenticated

Dependency Injection in ASP.net Session_Start method

I am learning dependency injection and using autofac for the first time. I built the container as mentioned in several autofac examples (see below) and called from my application_start
public class ContainerConfig
{
public static void RegisterContainer()
{
//Create a new ContainerBuilder
var builder = new ContainerBuilder();
// Register all the controllers using the assembly object
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//Registering default convention -- IExample and Example
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.Single(i => i.Name == "I" + t.Name))
.AsImplementedInterfaces();
//Build the container
var container = builder.Build();
//Set the default resolver to use Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
I created UserService in my Core project with IUserService. This has methods to make dbcall to get user information from tables. In my UI project, i have a class called UserProvider to which i am tying to inject UserService.
public class UserProvider
{
private readonly IUserService _userService;
public UserProvider(IUserService userService)
{
_userService = userService;
}
public void LoadCurrentUser()
{
Users FoundUser = _userService.ImportOrGetUser();
if (FoundUser != null)
{
//add it to session
CurrentUser = FoundUser;
}
}
}
This UserProvider, i am using in my session_start
void Session_OnStart()
{
UserProvider OUsrPrv = new UserProvider(new UserService());
OUsrPrv.LoadCurrentUser();
}
In the above code, if i am passing 'new UserService()', my understanding is i am injecting UserService manually. I dont see how autofac is helping here. All the examples in google are talking about Dependency injection in MVCController or WebApiController, not in a individual class (UserProvider) like i am doing.
Can somebody please throw some light? Am I doing it all wrong?
In order to properly use Dependency Injection, you should never create instance by yourself, the underlying framework should provide instances for you.
But ASP.net invokes the Session_OnStart without any Dependency Injection. In this case you can use the DependencyResolver.Current static property to resolve the requested service.
void Session_OnStart()
{
UserProvider userProvider = DependencyResolver.Current.GetService<UserProvider>();
userProvider.LoadCurrentUser();
}
The event model in the System.Web.HttpApplication is part of ASP.NET, not MVC. It was not designed for use with dependency injection.
The answer that Cyril suggested is using a service locator to get a reference to the service. This is far from ideal, since you are taking on a dependency to the service locator in your code.
The MVC-centric way of implementing cross cutting concerns (such as loading user data into session state) is to use globally registered filters. You can either implement IAuthorizationFilter or IActionFilter to get the desired effect. In this case it makes sense to use IActionFilter since you want to wait until you are sure there is an authorized user before it is called.
NOTE: While this answers your specific question, it is best not to use session state for this scenario in MVC. An alternative is to use ASP.NET Identity with Claims to store user profile data instead of using Session.
using System;
using System.Web.Mvc;
using System.Security.Principal;
public class GetUserActionFilter : IActionFilter
{
private readonly IUserRepository userRepository;
public GetUserActionFilter(IUserRepository userRepository)
{
if (userRepository == null)
throw new ArgumentNullException("userRepository");
this.userRepository = userRepository;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Do nothing - this occurs after the action method has run
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
IPrincipal user = filterContext.HttpContext.User;
if (user == null)
{
return;
}
IIdentity identity = user.Identity;
if (identity == null)
{
return;
}
// Make sure we have a valid identity and it is logged in.
if (identity.IsAuthenticated)
{
string key = "__CurrentUserData";
var userData = filterContext.HttpContext.Session[key];
if (userData == null)
{
// User data doesn't exist in session, so load it
userData = userRepository.GetUserData(identity.Name);
// Add it to session state
filterContext.HttpContext.Session[key] = userData;
}
}
}
}
Now, to add your filter globally, you need to:
Register the filter and its dependencies with Autofac.
Pass the container to the static RegisterGlobalFilters method.
Register the Filter
Using a named instance to differentiate it from other potential IActionFilter instances.
builder.RegisterType<GetUserActionFilter>()
.Named<IActionFilter>("getUserActionFilter");
Pass the Container
FilterConfig.cs
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters, IContainer container)
{
filters.Add(container.ResolveNamed<IActionFilter>("getUserActionFilter"));
filters.Add(new HandleErrorAttribute());
}
}
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
// This method serves as the composition root
// for the project.
protected void Application_Start()
{
// Register Autofac DI
IContainer container = ContainerConfig.RegisterContainer();
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, container);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
ContainerConfig.cs
public class ContainerConfig
{
public static IContainer RegisterContainer()
{
//Create a new ContainerBuilder
var builder = new ContainerBuilder();
// Register all the controllers using the assembly object
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//Registering default convention -- IExample and Example
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.Single(i => i.Name == "I" + t.Name))
.AsImplementedInterfaces();
// Register our filter
builder.RegisterType<GetUserActionFilter>()
.Named<IActionFilter>("getUserActionFilter");
//Build the container
var container = builder.Build();
//Set the default resolver to use Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Return the container to our composition root.
return container;
}
}
Note that I just used a repository service here, since HttpContext is available already through the action filter directly and additional logic is needed here because we don't know for sure if it exists in session state or not or whether there is even a user to lookup, so our filter does those checks in addition to loading session state.

How do I gain access to a repository in a custom ValidationAttribute?

public class UniqueNameAttribute : ValidationAttribute
{
private const string UniqueNameViolationMessage = "This name is already taken. Please select another.";
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//return somerepo.IsUniqueName(name)
//how do I get access to repo here? Tried DI with autofac but it came out as null
}
}
I'm using dependency injection to inject repositories into my Web API. However, it seems I cannot inject into a ValidationAttribute. It comes out as null if I try to inject. I'm using Autofac for DI. Is there another way I can gain access to my repo?
Note that I am only using Web API, not MVC, so I cannot use the MVC DependencyResolver.
builder.RegisterType<MyRepository>().As<IMyRepository>().InstancePerRequest();
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container); ;
I'm guessing you're missing this line:
var builder = new ContainerBuilder();
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
And rather than inherit from IActionFilter, inherit from IAutofacActionFilter.
More details here: https://autofaccn.readthedocs.io/en/latest/integration/webapi.html#provide-filters-via-dependency-injection

Is it possible to inject different service into controller based on if user is logged in or not?

I am using Autofac as a DI-container in a MVC4 app with forms authentication. I inject services that depend on the a IContext in the constructor of the controllers as follows:
public MyController(IUserService userService)
{
this.userService = userService;
}
The Autofac RegisterDependencies method has the following body:
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<MyDbContext>()
.As<IContext>()
.InstancePerRequest();
builder.RegisterType<UserService>()
.As<IUserService>;
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
When a user is logged in the UserService implementation of IUserService is injected. Now I want to have a different implementation of the IUserService for not logged in users: AnonymousService.
Is there a way to inject the AnonymousService via Autofac if a user is not logged in and a UserService if a user is logged in?
I think this situation is the perfect candidate for using a keyed service as documented here
http://docs.autofac.org/en/latest/advanced/keyed-services.html
You only have two possible implementations for the service, so you just need to define a two values enum:
public enum AuthenticatedStatusEnum
{
Anonymous,
Authenticated
}
and register your service (let's say IUserNameRetriever) twice, once foreach enum value:
builder.RegisterType<AnonymousUserNameRetriever>().Keyed<IUserNameRetriever>(AuthenticatedStatusEnum.Anonymous);
builder.RegisterType<AuthenticatedNameRetriever>().Keyed<IUserNameRetriever>(AuthenticatedStatusEnum.Authenticated);
Your controller will be defined in this way:
public class HomeController : Controller
{
IUserNameRetriever _currentUserNameRetriever;
public HomeController(IIndex<AuthenticatedStatusEnum, IUserNameRetriever> userNameRetrievers)
{
_currentUserNameRetriever = _userNameRetrievers[AuthenticatedStatus];
}
AuthenticatedStatusEnum AuthenticatedStatus
{
get
{
return System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated ? AuthenticatedStatus.Authenticated : AuthenticatedStatus.Anonymous;
}
}
The _currentUserNameRetriever service will have the correct implementation based on the authentication status of the user.
P.S. In a real application you will typically take the AuthenticatedStatus value from another injected service.

Categories