I have a class declaration, public abstract class CompanyHttpApplication : HttpApplication
In the CompanyHttpApplication I have
public static CompanyHttpApplication Current
{
get { return (CompanyHttpApplication)HttpContext.Current.ApplicationInstance; }
}
and
public CompanyProfileInfo Profile
{
get
{
return profile ?? (profile = ProfileManager
.FindProfilesByUserName(ProfileAuthenticationOption.Authenticated,
User.Identity.Name).Cast<CompanyProfileInfo>().ToList().First());
}
private set { profile = value; }
}
Whenever I access the user:
CatapultHttpApplication.Current.Profile.UserName
it gets whoever is logged in last. I understand why (because it's in the application instead of the session), but I don't know how to change it. What can I do to change it so it uses the session instead of the application?
Related
I have a Singleton model class in my MVC application to determine if the user logging in has authorization/admin (based on memberships to certain AD groups). This model class needs to be a Singleton so that the user's access rights can be established once at first logon and used throughout the session:
public sealed class ApplicationUser
{
// SINGLETON IMPLEMENTATION
// from http://csharpindepth.com/articles/general/singleton.aspx#lazy
public static ApplicationUser CurrentUser { get { return lazy.Value; } }
private static readonly Lazy<ApplicationUser> lazy =
new Lazy<ApplicationUser>(() => new ApplicationUser());
private ApplicationUser()
{
GetUserDetails(); // determine if user is authorized/admin
}
// Public members
public string Name { get { return name; } }
public bool IsAuthorized { get { return isAuthorized; } }
public bool IsAdmin { get { return isAdmin; } }
// Private members
// more code
}
The Singleton is instantiated for the first time in my EntryPointController that all other controllers derive from:
public abstract class EntryPointController : Controller
{
// this is where the ApplicationUser class in instantiated for the first time
protected ApplicationUser currentUser = ApplicationUser.CurrentUser;
// more code
// all other controllers derive from this
}
This patterns allows me to use ApplicationUser.CurrentUser.Name or ApplicationUser.CurrentUser.IsAuthorized etc all over my application.
However, the problem is this:
The Singleton holds the reference of the very first user that logs in at the launch of the web application! All subsequent users who log in see the name of the earliest logged-in user!
How can I make the Singleton session specific?
I think you are looking for the Multiton pattern, where each instance is linked to a key.
An example from here
http://designpatternsindotnet.blogspot.ie/2012/07/multiton.html
using System.Collections.Generic;
using System.Linq;
namespace DesignPatterns
{
public class Multiton
{
//read-only dictionary to track multitons
private static IDictionary<int, Multiton> _Tracker = new Dictionary<int, Multiton> { };
private Multiton()
{
}
public static Multiton GetInstance(int key)
{
//value to return
Multiton item = null;
//lock collection to prevent changes during operation
lock (_Tracker)
{
//if value not found, create and add
if(!_Tracker.TryGetValue(key, out item))
{
item = new Multiton();
//calculate next key
int newIdent = _Tracker.Keys.Max() + 1;
//add item
_Tracker.Add(newIdent, item);
}
}
return item;
}
}
}
I got it working with a mixed Singleton-Multiton approach (thanks #Kickaha for the Multiton pointer).
public sealed class ApplicationUser
{
// SINGLETON-LIKE REFERENCE TO CURRENT USER ONLY
public static ApplicationUser CurrentUser
{
get
{
return GetUser(HttpContext.Current.User.Identity.Name);
}
}
// MULTITON IMPLEMENTATION (based on http://stackoverflow.com/a/32238734/979621)
private static Dictionary<string, ApplicationUser> applicationUsers
= new Dictionary<string, ApplicationUser>();
private static ApplicationUser GetUser(string username)
{
ApplicationUser user = null;
//lock collection to prevent changes during operation
lock (applicationUsers)
{
// find existing value, or create a new one and add
if (!applicationUsers.TryGetValue(username, out user))
{
user = new ApplicationUser();
applicationUsers.Add(username, user);
}
}
return user;
}
private ApplicationUser()
{
GetUserDetails(); // determine current user's AD groups and access level
}
// REST OF THE CLASS CODE
public string Name { get { return name; } }
public bool IsAuthorized { get { return isAuthorized; } }
public bool IsAdmin { get { return isAdmin; } }
private string name = HttpContext.Current.User.Identity.Name;
private bool isAuthorized = false;
private bool isAdmin = false;
// Get User details
private void GetUserDetails()
{
// Check user's AD groups and determine isAuthorized and isAdmin
}
}
No changes to my model and controllers.
The current user's object is instantiated in the EntryPointController:
public abstract class EntryPointController : Controller
{
// this is where the ApplicationUser class in instantiated for the first time
protected ApplicationUser currentUser = ApplicationUser.CurrentUser;
// more code
// all other controllers derive from this
}
In my model and everywhere else, I can access the current user's properties using ApplicationUser.CurrentUser.Name or ApplicationUser.CurrentUser.IsAuthorized etc.
How can I make the Singleton session specific?
Will lead to your problem below.
The Singleton holds the reference of the very first user that logs in
at the launch of the web application! All subsequent users who log in
see the name of the earliest logged-in user!
I think you just simply need to store your ApplicationUser object in session per user.
The mechanism should look like this:
Create an instance of your ApplicationUser every authenticated user.
Store ApplicationUser instance in a session with key. ( Don't worry about same key per user because ASP.NET HttpSessionState will handle it for you. )
If you want to access your ApplicationUser object per user just simply get it from HttpSessionState.
You have an option to create/re-create your session in Session_OnStart or in your base controller.
Setup your session setting if you want it to expire or not.
I hope this solution will make sense to you. :)
I'm creating a practice admin application using MVC4, but I'm not sure the best method to persist the logged in user data for the entire lifetime of the session so that it will be accessible to all views & controllers.
For example, I desire a user to log in, then download the user data from the database, and for the entire session I want to maintain the User model (Name, Database ID etc) so that it's accessible throughout the entire web application until the user is logged out.
Is the best approach to store this data in an encrypted cookie? Or is there a way of using a Static Class?
Currently I've read about using ViewModel Base class like so:
public abstract class ViewModelBase
{
public UserModel User { get; set; }
}
Then all of my ViewModels can inherit the base class, thus providing access to the user data model:
public class AllEmployeesViewModel : ViewModelBase
{
public List<EmployeeModel> Employees { get; set; }
}
However, if in one Controller action I set the user data, it will only be lost when loading another Controller Action.
To me it seems a waste of resources & will increase load times to have to keep downloading the user data from the database in every action.
All advice is much welcome for this new web programmer. If I've missed any important details, please do request it and I will try my best to answer.
You should look into SessionState to store data for the duration of the user's browser session.
http://msdn.microsoft.com/en-us/library/vstudio/ms178581(v=vs.100).aspx
I would abstract the Session for you application in a class in a way it is accessible to your controllers. Maybe an web application core class.
namespace MyApplication.Core
{
public class MySession
{
private const string _SessionName = "__MY_SESSION__";
private MySession() { }
public static MySession Current
{
get
{
MySession session =
(MySession)HttpContext.Current.Session[_SessionName];
if (session == null)
{
session = new MySession();
HttpContext.Current.Session[_SessionName] = session;
}
return session;
}
}
public UserModel CurrentUser { get; set; }
}
}
Somewhere in your login controller logic path
public void SomeLoginFunction(string userName,string password)
{
//DO AUTHENTICATION STUFF
MySession.Current.CurrentUser=aCurrentUserFromDB;
}
In your base class
public class ViewModelBase
{
public UserModel User { get{return {MySession.Current.CurrentUser;} }
}
Given the following masterpage or content page ...............
namespace Intranet
{
public partial class Site : System.Web.UI.MasterPage
{
WebSite.Security.Users.CurrentUser currentUser;
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
currentUser = new WebSite.Security.Users.CurrentUser();
currentUser = WebSite.Security.Users.GetCurrentUser(currentUser);
Label_UserId.Text = currentUser.UserId;
}
}
}
}
that calls the following ............
namespace Intranet
{
public class WebSite
{
public class Security
{
public class Users
{
public class CurrentUser
{
public string UserId { get; set; }
}
public static CurrentUser GetCurrentUser(CurrentUser cu)
{
cu.UserId = "MethodThatGetsUserId";
return cu;
}
}
}
}
}
Will the returned instantiated class 'currentUser' contain unique information even if several different users are on the page at the same time?
Thanks for your time and insight.
Yes, a new class is instantiated for each request even, not just each user.
Static fields in the class will be shared, and you should use session and application data to share data across requests or users.
Nope, with this line:
currentUser = new WebSite.Security.Users.CurrentUser();
You are creating a new instance in your master page class. Instances created in each request are only available in that request (of course, depending on the scope), unless you use static variables. Static variables are the same for all users/threads in your application.
However, what you actually want to do is to get the current user. This should be done using the HttpContext.Current.User or Page.Current which is an IPrincipal and should contain information you filled in the Authenticate_Request method of your application.
To understand more about the ASP.NET forms authentication, please refer to: http://msdn.microsoft.com/en-us/library/9wff0kyh(v=vs.100).aspx
Hi i`m having trouble with a website. Sometimes after the Login my Site redirects me to the Login-Page saying i´m not having the rights to use this function, please login.
Ok first i thought, somethign wrong with my Accesrights but thats not, if i just go back one site in the Browser and send the Request again, it works.
So i debugged it, and after a ton of tests i got a result finding out, that when i create my Service Context with Ninject the UserName is an empty string.
Here is my Code:
First my Interface IServiceContext:
public interface IServiceContext
{
string UserName { get; }
}
Then the Class ServiceContext:
public class ServiceContext : IServiceContext
{
private static Object _syncRoot = new Object();
private static long _instance = 0;
private long _currentInstance = 0;
public string UserName { get; private set; }
public ServiceContext()
{
lock (_syncRoot)
{
if (_instance >= long.MaxValue)
_instance = 0;
_currentInstance = _instance++;
}
if (System.Web.HttpContext.Current.User == null)
UserName = "";
else
UserName = System.Web.HttpContext.Current.User.Identity.Name;
}
public ServiceContext(string userName)
{
UserName = userName;
}
public override string ToString()
{
return string.Format("#{0}, UserName: {1}", _currentInstance, UserName);
}
}
My Binding is set in a seperate file, looks like:
Bind<SecurityManagement >().ToSelf().InTransientScope();
Rebind<IServiceContext>().To<ServiceContext>().InRequestScope();
I need to use rebind, cause in my framework a StandardBinding for serviceContext is made.
And the Call from my InvokeMethod:
class SecurityManagement : IInterceptor
{
public void Intercept(IInvocation invocation)
{
IServiceContext _context = _kernel.Get<IServiceContext>();
String name = System.Web.HttpContext.Current.User.Identity.Name;
}
}
So Sometimes it happens, that my _context.UserName is an empty String. I found out that the System.Web.HttpContext.Current.User.Identity.Name property is an Empty string whenn Injecting into ServiceContext, but the Variable Name has the right Username. It is no Option for me to make the setter for the Property UserName public cause of the framework i´m using.
So anybody an idea why this is happening? perhaps any idea to solve the problem
Accessing straight to System.Web.HttpContext inside Service Layer and Repository Layer is not a good practice, because it is hard to unit test your project.
Instead, you want to inject HttpContextBase via constructor.
public class ServiceContext : IServiceContext
{
HttpContextBase _httpContextBase,
public ServiceContext(HttpContextBase httpContextBase)
{
_httpContextBase = httpContextBase;
}
}
Then access user like this -
string username = _httpContextBase.User.Identity.Name;
I have an object that contains all login data, that's in my controller (it was programmed before switching to MVC3).
I'm trying to add authorization to the site, so so far I have:
public LoginObject MyLoginObject
{
get;
set;
}
[CustomAuthorization()]
public ActionResult Index()
{
return View();
}
and
public class CustomAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
//should be return myLoginObject.IsLoggedIn;
}
}
Is there anyway to pass MyLoginObject into the AuthorizeAttribute class? If not could I at least pass in a boolean from the object that specifies if the user is authorized or not?
Edit: My solution based on Zonnenberg's advice.
public class LoginObject : IPrincipal // Now extends IPrincipal
{
... //old code
private class IdentityImpl : IIdentity
{
public string AuthenticationType
{
get;
set;
}
public bool IsAuthenticated
{
get;
set;
}
public string Name
{
get;
set;
}
}
public IIdentity Identity
{
get { return new IdentityImpl { AuthenticationType = "Custom Authentication", IsAuthenticated = this.IsLoggedIn, Name = this.Id}; }
}
}
Then I moved the instantiation of loginobject into CustomAuthorization
public override void OnAuthorization(AuthorizationContext filterContext)
{
// ... Set up LoginObject
filterContext.RequestContext.HttpContext.User = myLoginObject;
base.OnAuthorization(filterContext);
}
So now logging in, is done inside the authorization, and I can call User to access the login from the controller.
You can check wheter the user is logged in by using httpContext.User.Identity.IsAuthenticated.
To store more information you could use the httpContext.User object. You can write your own implementation of IPrincipal and IIdentity to store all kinds of login information.
Other option is to store login info in the Session.
How is your LoginObject instantiated?
If it's instantiated via a service or repository (ex. MyLoginObject = loginService.GetLogin() then you can move this call into the CustomAuthorization attribute.
If the logic is within the controller itself then this should be refactored into a service or repository depending on you solution architecture so that you can do the above.