Symfony Session Timeout Annoyance…
I have recently found that symfony doesn’t attempt to properly maintain the user’s session after logout.
This is a tad annoying as any attributes that have been set during a valid login session will still be set when the user is communicating with the server. It is also a security risk.
Symfony provides the basic building blocks for creating an application, and session management is a part of that. The myUser class contains paramter, attribute, credentials classes as well as some variables like culture (for internationalisation), authenticated and timedout.
The ‘authenticated’ variable’s use should be pretty obvious – when the application is processing the users login it should set the authenticated variable to ‘true’ :
$this->getUser()->setAuthenticated(true);
The ‘timedout’ variable is set automatically be symfony when … alas … the users session has expired/timed out – pretty self explanatory.
The problem comes when your application sets any parameters or attributes during the sessions lifetime. For example, in my application when the user logs in I set a session variable ‘nickname’ so that I can reference/identify the user during any subsequent requests :
$this->getUser()->setAttribute('nickname', $user->getNickname());
This is pretty basic and normal functionality.
This attribute should die along with the session, when it times out – but it doesn’t. There are some perfectly valid reasons why this happens e.g The ‘Validated User’ and the ‘Browsing User’ are two different things to the system but the contents of the shopping cart are not. Another example would be website tracking software. It monitors where users are and what they are looking at – a very good tool for marketing. This type of software doesn’t have to care whether the user is logged in order not as its only concern is where they are and what they are doing. This information needs session data to follow the user. If all session data were cleared after the user logged out – then the monitoring software would be thinking a new user arrived – which would be wrong.
What is needed in this situation is a trigger or a test that determines if the session has expired and if it has – then action something appropriately.
The only way that I have determined to do this is by editing the myUser.class.php file for the application (and every other application) in your project.
As the myUser class is instantiated before any actions we can alter/clean up the session before any proper processing is done :
class myUser extends sfBasicSecurityUser
{
public function initialize($context, $parameters = array()) {
parent::initialize($context, $parameters = array());
if($this->isTimedOut()) {
$this->getAttributeHolder()->remove('nickname');
}
}
}
If you don’t like the idea of having to do this for all your applications – then you can create a new class and make the myUser class extend it instead (you must ensure that the new class also extends sfBasicSecurityUser. That way you have one central place to update when you need to clean up your session.
Hope this helps…
If you have any better ways of doing this – please let me know. I’d be interested to hear your take on it.
Categories: Programming, symfony
If this is something you’ll be using in multiple projects, I’d recommend making a plugin. In our projects, I’m using sfGuardPlugin, and my own additional plugin that extends sfGuardPlugin.
Hi Fork,
Good idea. Plugins for Symfony are the future…
One thing you can do is to store all session attributes in an application-specific namespace. Then upon logging out, remove the namespace, rather than specific attributes, so that it clears everything. This means that as new attributes are introduced into the system, you don’t have to remember to clear them up as well.
Hi Halfer,
I see what you mean.
Handling the ‘logout’ action is simple enough. The issue described here references the timeout side of ending an authenticated session – which could not be properly handled in an action.
But, yeah – application specific namespaces would be a good idea as it bundles all attributes in one space.
hello,
how can I refernce the new myUser class instead of the original sfBasicSecurityUser? Also, where should I place this class at? Is it supposed to be under the same sfBasciSecurityUser folder?
Thanks
newbiew
halfer is correct. The best way to do this would be to keep all items you want to expire (upon logout) in one namespace… for example loggedInUser. See below
public function initialize($context, $parameters = array()) {
parent::initialize($context, $parameters = array());
if($this->isTimedOut()) {
$this->getAttributeHolder()->removeNamespace(‘loggedInUser’);
}
}
Hi thichquay,
Terribly sorry for the tardy reply – but here goes :
You can find the myUser class in your application directory :
your_project/apps/your_app/lib
The file is called myUser.class.php.
Hope this helps.
Hi toolagio!
Thanks for the code.
I think I would expand the code slightly to test if the namespace exists (if the user is logged in) and if not – then create it by default so that all the rest of my classes don’t need to think about creating the namespace if/when they need to store something there.
But its all semantics
sir,
i have one doubt.i am using symfony framework.i have 3 pages totally.first page i added something and submitting the page.coming to second page also i added values and submitting the page.i given my condition is ,while going third page i used if and else statements.in else part i used redirect to come back(second page).when i submit to the second page,the first page values are not coming.can you help me for this question.this is for session in symfony framework.
Hi devaraj said…
It’s hard to say what’s wrong without code.
Try to use froward instead to redirect(forward keeps what you have in POST).
I’m assuming that on the third page you have valeues from the first (var_dump($_POST))
This CODE
parent::initialize($context, $parameters = array());
take an error to me
[Tue Apr 28 20:15:44 2009] [error] [client 127.0.0.1] PHP Fatal error: Call to a member function set() on a non-object in /cache/frontend/prod/config/config_core_compile.yml.php on line 1814