1

I have a site where I allow some users to proxy in as an other user. When they do, they should see the entire site as if they where the user they proxy in as. I do this by changing the current user object

internal static void SetProxyUser(int userID)
{
    HttpContext.Current.User = GetGenericPrincipal(userID);
}

This code works fine for me.

On the site, to proxy in, the user selects a value in a dropdown that I render in my _layout file as such, so that it appears on all pages.

 @Html.Action("SetProxyUsers", "Home")

The SetProxyUsers view looks like this:

@using (@Html.BeginForm("SetProxyUsers", "Home")) {

@Html.DropDownList("ddlProxyUser", (SelectList)ViewBag.ProxyUsers_SelectList, new { onchange = "this.form.submit();" })

}

The controller actions for this looks like this

    [HttpGet]
    public ActionResult SetProxyUsers()
    {
        ViewBag.ProxyUsers_SelectList = GetAvailableProxyUsers(originalUserID);

        return PartialView();
    }

    [HttpPost]
    public ActionResult SetProxyUsers(FormCollection formCollection)
    {
        int id = int.Parse(formCollection["ddlProxyUser"]);

        RolesHelper.SetProxyUser(id);

        ViewBag.ProxyUsers_SelectList = GetAvailableProxyUsers(originalUserID);

        return Redirect(Request.UrlReferrer.ToString());
    }

All this works (except for the originalUserID variable, which I put in here to symbolize what I want done next.

My problem is that the values in the dropdown list are based on the logged in user. So, when I change user using the proxy, I also change the values in the proxy dropdown list (to either disappear if the "new" user isn't allowed to proxy, or to show the "new" user's list of available proxy users).

I need to have this selectlist stay unchanged. How do I go about storing the id of the original user? I could store it in a session variable, but I don't want to mess with potential time out issues, so that's a last resort.

Please help, and let me know if there is anything unclear with the question.

Update

I didn't realize that the HttpContext is set for each post. I haven't really worked with this kind of stuff before and for some reason assumed I was setting the values for the entire session (stupid, I know). However, I'm using windows authentication. How can I change the user on a more permanent basis (as long as the browser is open)? I assume I can't use FormAuthentication cookies since I'm using windows as my authentication mode, right?

tereško
  • 58,060
  • 25
  • 98
  • 150
The Jonas Persson
  • 1,726
  • 1
  • 17
  • 36

2 Answers2

2

Instead of faking the authentication, why not make it real? On a site that I work on we let admins impersonate other users by setting the authentication cookie for the user to be impersonated. Then the original user id is stored in session so if they ever log out from the impersonated users account, they are actually automatically logged back in to their original account.

Edit:

Here's a code sample of how I do impersonation:

[Authorize] //I use a custom authorize attribute; just make sure this is secured to only certain users.
public ActionResult Impersonate(string email) {
    var user = YourMembershipProvider.GetUser(email);

    if (user != null) {
        //Store the currently logged in username in session so they can be logged back in if they log out from impersonating the user.
        UserService.SetImpersonateCache(WebsiteUser.Email, user.Email);

        FormsAuthentication.SetAuthCookie(user.Email, false);
    }

    return new RedirectResult("~/");
}

Simple as that! It's been working great. The only tricky piece is storing the session data (which certainly isn't required, it was just a nice feature to offer to my users so they wouldn't have to log back in as themselves all the time). The session key that I am using is:

string.Format("Impersonation.{0}", username)

Where username is the name of the user being impersonated (the value for that session key is the username of the original/admin user). This is important because then when the log out occurs I can say, "Hey, are there any impersonation keys for you? Because if so, I am going to log you in as that user stored in session. If not, I'll just log you out".

Here's an example of the LogOff method:

[Authorize]
public ActionResult LogOff() {
    //Get from session the name of the original user that was logged in and started impersonating the current user.
    var originalLoggedInUser = UserService.GetImpersonateCache(WebsiteUser.Email);

    if (string.IsNullOrEmpty(originalLoggedInUser)) {
        FormsAuthentication.SignOut();
    } else {
        FormsAuthentication.SetAuthCookie(originalLoggedInUser, false);
    }

    return RedirectToAction("Index", "Home");
}
Justin Helgerson
  • 24,900
  • 17
  • 97
  • 124
  • Well, I was hoping not to have to use the session variable, but maybe that's the only way? I don't know enough about the form authentication cookies to know exactly what you mean, but I'm not really faking the authentication. The call to the function `GetGenericPrincipal(userID);` creates a Generic Principal with all the data needed to impersonate another user. – The Jonas Persson Jun 14 '12 at 19:32
  • Unless you set the authentication cookie, aren't you having to call `GetGenericPrincipal` on every request? – Justin Helgerson Jun 14 '12 at 21:51
  • That's what I'm starting to realize. I guess I'll have to do some more research, or could you post a little code snippet on how to set the authentication cookie? – The Jonas Persson Jun 15 '12 at 01:00
  • Thanks a lot for your answers and your time. I'm using integrated windows authentication, so the form authentication cookie won't work for me. I'm sorry if I wasted your time. I'm still banging my head against the wall here... – The Jonas Persson Jun 15 '12 at 15:43
  • 1
    Ah, IWA would change the scenario a bit. I think your best bet would be to place your code in your Global.asax; perhaps in the `Application_AuthorizeRequest` method since authentication should have taken place by then. Then you could modify the `User` object as you need. – Justin Helgerson Jun 15 '12 at 16:30
  • Thanks, I'll give that a try (which means I'll set the user on each request). – The Jonas Persson Jun 15 '12 at 17:07
0

I used the mvc example in the comments on this article http://www.codeproject.com/Articles/43724/ASP-NET-Forms-authentication-user-impersonation to

It uses FormsAuthentication.SetAuthCookie() to just change the current authorized cookie and also store the impersonated user identity in a cookie. This way it can easily re-authenticate you back to your original user.

I got it working very quickly. Use it to allow admin to login as anyone else.

GraemeMiller
  • 11,973
  • 8
  • 57
  • 111