Getting the current Principal outside the Web tier

I have the following ntier application: MVC> Services> Repository> Domain. I use forms authentication. Is it safe to use Thread.CurrentPrincipal outside my MVC level to get the current user in my application, or should I use HttpContext.Current.User?

The reason I am asking there is apparently some problems around Thread.CurrentPrincipal, but I am carefully adding a link to System.Web outside my MVC level in case I need to provide an end to the web font in the future.

Update

I followed the advice received so far to pass the username to the Service as part of the parameters of the called method, and this led to a clarification of my original question. I need to check if the user has a specific role in a number of my service and domain methods. There seem to be several solutions for this, just wondering which one works best:

  • Pass the entire HttpContext.Current.User as a parameter instead of a username.
  • Call Thread.CurrentPrincipal outside of my web tier and use this. But how can I guarantee that it is equal to HttpContext.Current.User?
  • Stick to the username as suggested so far, and then use Roles.IsUserInRole. The problem with this approach is that it requires a reference to System.Web, which I feel does not match the appearance of my MVC level.

How would you advise me to continue?

+8
authentication asp.net-mvc asp.net-mvc-3 forms-authentication iprincipal
source share
5 answers

I wouldnโ€™t do this either, HttpContext.Current.User specific to your web tier.

Why not enter a username at your service level?

+2
source share

You must abstract your user information so that it does not depend on Thread.CurrentPrincipal or HttpContext.Current.User .

You can add a constructor or method parameter that takes a username, for example.

Here's an overly simplified example of a constructor parameter:

 class YourBusinessClass { string _userName; public YourBusinessClass(string userName) { _userName = userName; } public void SomeBusinessMethodThatNeedsUserName() { if (_userName == "sally") { // do something for sally } } } 
+2
source share

Match the appropriate user data with the new Class to represent LoggedInUser and pass this as an argument to your business-level method

  public class LoggedInUser { public string UserName { set;get;} //other relevant proerties } 

Now set the values โ€‹โ€‹of this and go to your BL method

 var usr=new LoggedInUser(); usr.UserName="test value "; //Read from the FormsAuthentication stuff and Set var result=YourBusinessLayerClass.SomeOperation(usr); 
+2
source share

I prefer option number 2 (use Thread.CurrentPrincipal outside of the web tier). as this will not get your service level and data level methods. with bonuses: you can save your roles + additional information in the user principle;

To ensure that Thread.CurrentPrincipal in your service and data layer matches your web layer; you can set your HttpContext.Current.User (Context.User) to Global.asax (Application_AuthenticateRequest). Another alternative place where you can install this is added at the bottom.

code example:

  //sample synchronizing HttpContext.Current.User with Thread.CurrentPrincipal protected void Application_AuthenticateRequest(Object sender, EventArgs e) { HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; //make sure principal is not set for anonymous user/unauthenticated request if (authCookie != null && Request.IsAuthenticated) { FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); //your additional info stored in cookies: multiple roles, privileges, etc string userData = authTicket.UserData; CustomPrincipal userPrincipal = PrincipalHelper.CreatePrincipal(authTicket.Name, authTicket.UserData, Request.IsAuthenticated); Context.User = userPrincipal; } } 

Of course, you must first implement your registration form in order to create authorization cookies containing your user principle.

Application_AuthenticateRequest will be executed for any request to the server (css files, javascript files, image files, etc.). To limit this functionality only to the action of the controller, you can try to set the user principle in ActionFilter (I have not tried this). What I tried installs this functionality inside Interceptor for Controllers (I use Castle Windsor for my dependency injection and aspect-oriented programming).

+2
source share

I believe that you have encountered this problem because you need to further limit the liability of your domains. You should not be responsible for authorizing your service or your document. This responsibility must be fulfilled by your MVC level, as the current user is logged in to your web application, and not to your domain.

If, instead of looking for the current user from your service or document, you check your MVC application, you get something like this:

 if(Roles.IsUserInRole("DocumentEditorRole")){ //UpdateDocument does NOT authorize the user. It does only 1 thing, update the document. myDocumentService.UpdateDocument(currentUsername, documentToEdit); } else { lblPermissionDenied.InnerText = @"You do not have permission to edit this document."; } 

which is clean, easy to read and allows you to save your services and domain classes without authorization problems. You can still map Roles.IsUserInRole("DocumentEditorRole") to the view model, so the only thing you lose is the CurrentUserCanEdit method in your Document class. But if you think of your domain model as real-world objects, this method in any case does not apply to the document. You may think of it as a method on a domain user object ( user.CanEditDocument(doc) ), but overall, I think you will be happier if you save authorization outside your domain.

+1
source share

All Articles