Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtaining the current Principal outside of the Web tier

I have the following ntier app: MVC > Services > Repository > Domain. I am using Forms authentication. Is it safe to use Thread.CurrentPrincipal outside of my MVC layer to get the currently logged in user of my application or should I be using HttpContext.Current.User?

The reason I ask is there seems to be some issues around Thread.CurrentPrincipal, but I am cautious to add a reference to System.Web outside of my MVC layer in case I need to provide a non web font end in the future.

Update

I have been following the advice recieved so far to pass the username into the Service as part of the params to the method being called and this has lead to a refinement of my original question. I need to be able to check if the user is in a particular role in a number of my Service and Domain methods. There seems to be a couple of solutions to this, just wondering which is the best way to proceed:

  1. Pass the whole HttpContext.Current.User as a param instead of just the username.
  2. Call Thread.CurrentPrincipal outside of my web tier and use that. But how do I ensure it is equal to HttpContext.Current.User?
  3. Stick to passing in the username as suggested so far and then use Roles.IsUserInRole. The problem with this approach is that it requires a ref to System.Web which I feel is not correct outside of my MVC layer.

How would you suggest I proceed?

like image 712
James Avatar asked Jul 18 '12 14:07

James


4 Answers

I wouldn't do either, HttpContext.Current.User is specific to your web layer.

Why not inject the username into your service layer?

like image 55
Joe Ratzer Avatar answered Oct 13 '22 10:10

Joe Ratzer


Map the relevant User details to a new Class to represent the LoggedInUser and pass that as an argument to your Business layer method

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

Now set the values of this and pass to your BL method

var usr=new LoggedInUser();
usr.UserName="test value ";  //Read from the FormsAuthentication stuff and Set
var result=YourBusinessLayerClass.SomeOperation(usr);
like image 26
Shyju Avatar answered Oct 13 '22 09:10

Shyju


You should abstract your user information so that it doesn't depend on Thread.CurrentPrincipal or HttpContext.Current.User.

You could add a constructor or method parameter that accepts a user name, 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
      }
   }
}
like image 42
jrummell Avatar answered Oct 13 '22 08:10

jrummell


I prefer option number 2( use Thread.CurrentPrincipal outside of web tier ). since this will not polute your service tier & data tier methods. with bonuses: you can store your roles + additional info in the custom principal;

To make sure Thread.CurrentPrincipal in your service and data tier is the same as your web tier; you can set your HttpContext.Current.User (Context.User) in Global.asax(Application_AuthenticateRequest). Other alternative location where you can set this are added at the bottom.

sample code:

    //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 first you must implement your login form to create authorization cookies containing your custom principal.

Application_AuthenticateRequest will be executed for any request to server(css files, javascript files, images files etc). To limit this functionality only to controller action, you can try setting the custom principal in ActionFilter(I haven't tried this). What I have tried is setting this functionality inside an Interceptor for Controllers(I use Castle Windsor for my Dependency Injection and Aspect Oriented Programming).

like image 37
kite Avatar answered Oct 13 '22 09:10

kite