Access Thread.CurrentPrincipal when binding with NInject in Owin context - asp.net-web-api

I need to access my claims when binding my services, but appears that Ninject bind occurs before HttpMessageHandlerAdapter sets the Thread.CurrentPrincipal so I cannot access my claims when I'm binding my services. I tried this
kernel.Bind<IMyService>().ToMethod(x => GetMyService());
private static IMyService GetMyService()
{
var principal = Thread.CurrentPrincipal;
var userId = [GetUserIdFromPrincipalClaims]
return new MyService(userId);
}
But the claims list is empty, if I access Thread.CurrentPrincipal claims list from any action I can get an userId. How can I access my claims list when binding?

Since identity claims are a run-time value, I would say it can't be done (easily). However, one approach involving the decorator pattern and an abstract factory might look like this:
class MyServiceDecoraptor : IMyService
{
private readonly IMyServiceFactory _myServiceFactory;
public MyServiceDecoraptor(IMyServiceFactory myServiceFactory)
{
_myServiceFactory = myserviceFactory
}
public void MyServiceMethod()
{
var principal = Thread.CurrentPrincipal;
var userid = GetUserId(principal);
_myServiceFactory.Create(userid).MyServiceMethod();
}
//more code here
}

Related

ASP NET Boilerplate, Login saved in ABPSession

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;
}

Where can I load the user information to the session in ASP.NET MVC 5 with windows authentication?

I want to use the ASP.NET MVC 5 for my web app. I need use the windows authentication.
If I use the windows authentication where is the best place for reading user information (userid and roles) and store its to the Session?
I have the method for getting the user information by username from the database like this:
public class CurrentUser
{
public int UserId { get; set; }
public string UserName { get; set; }
public Roles Roles { get; set; }
}
public enum Roles
{
Administrator,
Editor,
Reader
}
public class AuthService
{
public CurrentUser GetUserInfo(string userName)
{
var currentUser = new CurrentUser();
//load from DB
return currentUser;
}
}
You've asked two questions (1) the best place to obtain user information and (2) how to store it in the Session. I'll answer (1) and in so doing perhaps show that you need not put any additional information in the session.
You've stated that your application is using Windows Authentication, so that means the hard work of authenticating the user has already been done by IIS/HttpListener before your app receives the request. When you receive the request there will be a WindowsPrincipal in HttpContext.User. This will have the windows username and AD roles already established, but you wish to use additional roles stored in the database...
You could access your AuthService from anywhere in your application, but probably the best approach is to register an IAuthorizationFilter and do the work there. By following this approach, the additional roles and other information you fetch from the database will be available in your controller methods and, perhaps more importantly, from any additional library code that needs to check user credentials.
Prior to .Net 4.5, if you wanted to add additional information to the WindowsPrincipal I think your only choice was to replace the system-provided User with another object that implemented the IPrincipal interface. This approach is still available (and what I recommend), but since the introduction of Windows Identity Foundation (WIF) in .Net 4.5, WindowsPrincipal is derived from  System.Security.Claims.ClaimsIdentityClaimsIdentity, which supports adding additional roles (and other useful information) to the system-provided principal. However, as several people have found, there is a bug/feature in Windows which can cause an exception The trust relationship between the primary domain and the trusted domain failed to be thrown when checking roles that have been added programmatically. We have found that a simple and reliable way to avoid this is to replace the User with a GenericPrincipal.
Steps required:
(1) create an IAuthorizationFilter.
class MyAuthorizationFilter : IAuthorizationFilter
{
AuthService _authService;
public MyAuthorizationFilter(AuthService authService)
{
_authService = authService;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var principal = filterContext.HttpContext.User;
if (principal.Identity != null && principal.Identity.IsAuthenticated)
{
// Add username (and other details) to session if you have a need
filterContext.HttpContext.Session["User"] = principal.Identity.Name;
// get user info from DB and embue the identity with additional attributes
var user = _authService.GetUserInfo(principal.Identity.Name);
// Create a new Principal and add the roles belonging to the user
GenericPrincipal gp = new GenericPrincipal(principal.Identity, user.RoleNames.ToArray());
filterContext.HttpContext.User = gp;
}
}
}
(2) Register your filter. This can be registered at the controller level or globally. Typically you will do this in App_Start\FilterConfig.cs:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizationFilter(new AuthService()));
}
}
(3) Use the provided GenericPrincipal in your application code to answer questions about the user identification and other credentials. e.g. in your controller method you can access the username or any other "claims" (e.g. email address) stored in the GenericPrincipal by your filter.
public ActionResult Index()
{
ViewBag.Name = HttpContext.User.Identity.Name;
if(HttpContext.User.IsInRole("Administrator"))
{
// some role-specific action
}
return View();
}
Because you've used the built-in mechanism to record Principal roles, you can access user details from anywhere using HttpContext.User or System.Threading.Thread.CurrentPrincipal. Also you can use the AuthorizeAttribute in you controller methods to declare which actions are available to certain roles or users. e.g.
public class HomeController : Controller
{
[Authorize(Roles = "Administrator")]
public ActionResult Admin()
{
return View();
}
See MSDN for further details about ClaimsIdentity
I hope this helps
-Rob
First and foremost: never, never, never store user details in the session. Seriously. Just don't do it.
If you're using Windows Auth, the user is in AD. You have use AD to get the user information. Microsoft has an MSDN article describing how this should be done.
The long and short is that you create a subclass of UserIdentity and extend it with the additional properties you want to return on the user:
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("inetOrgPerson")]
public class InetOrgPerson : UserPrincipal
{
// Inplement the constructor using the base class constructor.
public InetOrgPerson(PrincipalContext context) : base(context)
{
}
// Implement the constructor with initialization parameters.
public InetOrgPerson(PrincipalContext context,
string samAccountName,
string password,
bool enabled)
: base(context,
samAccountName,
password,
enabled)
{
}
InetOrgPersonSearchFilter searchFilter;
new public InetOrgPersonSearchFilter AdvancedSearchFilter
{
get
{
if ( null == searchFilter )
searchFilter = new InetOrgPersonSearchFilter(this);
return searchFilter;
}
}
// Create the mobile phone property.
[DirectoryProperty("mobile")]
public string MobilePhone
{
get
{
if (ExtensionGet("mobile").Length != 1)
return null;
return (string)ExtensionGet("mobile")[0];
}
set
{
ExtensionSet( "mobile", value );
}
}
...
}
In the example code above, a property is added to bind to the AD's user's mobile field. This is done by implementing the property as shown utilizing ExtensionSet, and then annotating the property with the DirectoryProperty attribute to tell it what field it binds to.
The DirectoryRdnPrefix and DirectoryObjectClass attributes on the class need to line up with how your AD is set up.
Once this is implemented, then you will be able to get at the values simply by referencing them off User.Identity. For example, User.Identity.MobilePhone would return the mobile field from AD for the user.

WebApi External Bearer Authentication and Injecting User into respositories

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; }
}
}

MVC 3 Forms authentication with IOC container

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;
}));

Moq, TDD and multiple layers

I have a data assembly which defines my repositories.
I then have a service assembly which defines my services, each service has a repository injected into the constructor by Ninject.
When I'm unit testing my service methods, everything is fine:
[TestClass]
public class UserTests
{
IQueryable<User> users;
int userID;
public UserTests()
{
users = new List<User>()
{
new User { EmailAddress = "known#user.com", Password = "password", ID = 1}
}.AsQueryable();
}
[TestMethod]
public void Known_User_Can_Login()
{
// Arrange
var repository = new Mock<IUserRepository>();
var service = new UserService(repository.Object);
repository.Setup(r => r.GetAll())
.Returns(users);
// Act
bool loggedIn = service.UserLogin("known#user.com", "password", out userID);
// Assert
Assert.IsTrue(loggedIn);
Assert.AreEqual(1, userID);
}
[TestMethod]
public void Unknown_User_Cannot_Login()
{
// Arrange
var repository = new Mock<IUserRepository>();
var service = new UserService(repository.Object);
repository.Setup(r => r.GetAll())
.Returns(users);
// Act
bool loggedIn = service.UserLogin("unknown#user.com", "password", out userID);
//
Assert.IsFalse(loggedIn);
}
}
However, I'm running into problems when testing my controller (another level down).
My controller has dependencies on a service, and the service depends on a repository. I can't seem to get my controller tests to work. Here's what I have so far:
[TestClass]
public class AccountTests
{
IQueryable<User> users;
public AccountTests()
{
users = new List<User>()
{
new User { CompanyId = 1, EmailAddress = "known#user.com", Password = "password"},
new User { CompanyId = 1, EmailAddress = "unknown#user.com", Password = "password"}
}.AsQueryable();
}
[TestMethod]
public void Known_User_Can_Login()
{
int userID = 0;
var repo = new Mock<IUserRepository>();
// Arrange
var service = new Mock<UserService>(repo.Object);
var controller = new AccountController(service.Object);
// Act
var result = controller.Login(new LoginModel { EmailAddress = "known#user.com", Password = "password" }) as RedirectToRouteResult;
// Assert
service.Verify(x => x.UserLogin("known#user.com", "password", out userID), Times.Once());
Assert.AreEqual("Index", result.RouteValues["Action"]);
}
}
I keep getting an error about instantiating the UserService (i'm attempting to pass an instance of the UserRepository to it but it isn't working).
I must be doing something daft, but what?
Make sure that UserService is an interface.
Also, you must not create an instance of IUserRepository in your second test as you are doing here:
var repo = new Mock<IUserRepository>();
// Arrange
var service = new Mock<UserService>(repo.Object);
Since UserService is mocked, it will not make calls to IUserRepository anyway and you don't have to supply it's dependencies when creating the mock.
What you need to do is simply create the mock for UserService this way:
var service = new Mock<UserService>();
Even tough UserService normally has a dependency on a repository, Moq will just supply canned responses to methods and thus won't need to make the calls to the an actual repository.
UserService class I am guessing is not an interface or an abstract class, if not then it is bound to fail since Mock cannot create an implementation of the UserService. Thus either create an interface for UserService or define it as an abstract class or instantiate it as follows:
var service = new UserService(repo.Object);
or you could use Mock.Create<UserService>() but you need to have provided all the implemenation UserService needs using Mock.Provide<T>() api.
Hope this helps.
If this isnt the problem please update the question with the classes for UserService and your repository

Resources