Good way of storing UserProfile information and preventing database call on every page load - c#

I am trying to create a way to store user's profile information in either session or cache and be able to have that information on hand every time the page loads to prevent calling the database every time we load a page. I want to be able to put some of these values in a shared layout (so i can put the user's name in the upper right hand corner etc.). At the top of my master page layout I have the following:
#{
Domain.Entities.UserProfile user = (Domain.Entities.UserProfile)HttpContext.Current.Session[Membership.GetUser().ProviderUserKey.ToString()];
}
I use values like user.FirstName in my master page. When a user logs in I set the session. Is there a better way of doing this? If I am logged out and I just navigate to a member only page, the [Authorize] filter is bypassed and I get an error Object reference not set to an instance of an object. because my user profile is not stored in session yet. I do not store any sensitive information in the session like a UserId or anything, just the users name, profile image url etc. I feel there may be a better way of doing such a task.

You can store your user information in the authentication cookie. Keep in mind that the cookie will be sent back and forth from the client with every request. So if there's a lot of information to be stored maybe that's not the right way.
Check the step 4 of this document that explains how to store additional user data in the cookie http://www.asp.net/web-forms/tutorials/security/introduction/forms-authentication-configuration-and-advanced-topics-cs
Then in your master page, you can have something like this
#{
if (User.Identity.IsAuthenticated)
{
//Your code goes here, no way user data is null.
}

You can create a static class as an interface to get the user properties, which will be stored in the session state.
public class UserData{
public static Domain.Entities.UserProfile GetUser(){
string username;
try{
username = Membership.GetUser().ProviderUserKey.ToString();
}catch(Exception e){
// just in case
throw new AuthenticationException("The user isn't authenticated!");
}
var user = HttpContext.Current.Session[username] as Domain.Entities.UserProfile;
if(user == null){
user = // get data from database
HttpContext.Current.Session[username] = user;
}
return user;
}
}
Then, in your methods you can use it like this:
if (User.Identity.IsAuthenticated)
{
UserProfile user = UserData.GetUser();
//Your code goes here, no way user data is null.
}
Or in controllers:
[Authorize]
public ActionResult Index(){
UserProfile user = UserData.GetUser();
return //stuff
}
Note: I made this without being able to test, but I hope you get the point :)

Related

What is the best way to keep user information entered in MVC?

I have some users information like personal information and profile image. I need to user these information thought my web application.
I've created a public class that I set to users when I log in and I use the fields in that class in the entire web application.
Is this the right way?
Is there a better way to use this information at no cost?
Note: I have more than 2000 users in the user table.
Login Action:
var user = new LoginAction().getLogin(obj);
if (user!=null)
{
FormsAuthentication.SetAuthCookie(obj.Username, false);
Helper.loginUser = user;
}
Helper Class:
public static tblUser loginUser;
Static class in not good choice for storing users profile in memory. First problem is that Helper.loginUser always has last logged in user profile. Session and Cache are better choice to keep users profile in memory and after user logout or expiring session memory will be freed.
var user = new LoginAction().getLogin(obj);
if (user!=null)
{
FormsAuthentication.SetAuthCookie(obj.Username, false);
Session["User"] = user;
}

How can I get the Role for a user after Login?

Here is my sample code for logging in:
if (WebSecurity.Login(playerModel.WorkerID, playerModel.Password))
{
if (Roles.IsUserInRole(User.Identity.Name, "Administrator"))
{
// display the admin panel
ViewBag.Message = "Admin Panel";
return View("Index");
}
else
{
ViewBag.ButtonText = "Log In";
ViewBag.Message = "You are not an Admin user";
return View();
}
}
For some reason, the first time I log in, the user is not in "Administrator" even though they are in the correct Role in the table. I have also tried using this:
Roles.GetRolesForUser();
That returns an empty array. I'm assuming there is a limitation to do with the user logging in, a post-back occurring and then being able to check the role. But I'm hoping there is a workaround (or I have something wrong).
After a post-back both the Roles.IsUserInRole and Roles.GetRolesForUSer methods work correctly.
you could try using
HttpContext.Current.User.IsInRole("role")
I'm not 100% sure on when the Roles security class gets updated after a login - but I bets its not until the forward page gets called and a whole new request is generated.
The solution was to redirect to another page and check there. The issue is that the cookie isn't read back in with the role information until you have another postback (read the cookie you just wrote).

Pass data to another view

If the user login is valid, i will redirect the view to another view as shown below. The following method is in a Controller class.
if (loginSuccess(email,pwd))
{
FormsAuthentication.SetAuthCookie(email, false);
return RedirectToAction("Index", "SuccessPage");
}
Now i want to display the username on the redirected view (which is /SuccessPage/Index). How can i do this ?
You can pass parameters in the redirect.
if (loginSuccess(email,pwd))
{
string name = User.Identity.Name;
return RedirectToAction("Index", "SuccessPage"), new {username = name };
}
(Obviously, the controller action needs to have access to the username attribute before it can pass the value)
Note: The example above is useful for understanding how to pass additional parameters in a redirect, but in this case it's overkill since User.Identity.Name is available in the session once a user has authenticated and you can access it from any controller or view, so no need to pass (see Josh's response).
When a user is authenticated, you can use the HttpContext to get basic user information. For example, you can use the following to get the username:
string userName = HttpContext.User.Identity.Name;
You don't need to worry about passing the information between controllers. The information will be available as long as the user is autheticated.
In the SuccessPage.Index action, get the user's name and pass it into the view via the model.
To get the user's name, you either get it from a saved Session variable, or you can use controller.HttpContext.User.Identity.Name.
You have 4 options if we're just discussing passing data. (If we're talking about just the username then you want Josh's response)
Passing parameters in the redirect (as Jack suggested)
return RedirectToAction("Index", "SuccessPage"), new {username = name };
Storing it in temp data (only works for 1 request)
TempData[Key] = name;
return RedirectToAction("Index", "SuccessPage");
Storing it in session (lasts as long as the session lasts)
Session[Key] = name;
return RedirectToAction("Index", "SuccessPage");
Storing it in the database and linking that data via their session id.
/* databasey code here */
return RedirectToAction("Index", "SuccessPage");
That's your full set of options from simplest to most complex. I'd suggest in your case you just pass the values in the URL (first one) as your system expands and grows you may want to consider trying out the other options.
It's worth noting that TempData doesn't last across a page refresh.

TempData lost on page reload

I use TempData to store login user role but after the user logs in and presses F5 to reload the page, I run into an error stating that Object reference not set to an instance of an object
I use TempData because my page needs redirection.
The user fills in the login form before submitting it, I don't know how to retrieve the posted data since I need to redirect the user to admin page in case his role is administrator or just standard page in case his is normal user.
private bool IsAdmin(string username)
{
return (Roles.GetRolesForUser(username).ToList().Contains("administrator"));
}
public ActionResult AdminLayout()
{
if(IsAdmin(TempData["LoginUsername"].ToString())) //Error TempDate on Reload
{
return View();
}
else
{
return Index();
}
}
Why don't you store it in a session variable? As the name suggests, TempData is only temporary and session variables will hold the data for longer.

Set proxy user in a GenericPrincipal, while keeping the old identity, using MVC

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?
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");
}
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.

Categories