Development

/branches/1.2/lib/storage/sfCacheSessionStorage.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/storage/sfCacheSessionStorage.class.php

Revision 24599, 8.1 kB (checked in by FabianLange, 5 years ago)

[1.2] backported #7554 to 1.2. Cache Session Storage correctly sets and regenerates session_id

Line 
1 <?php
2
3 /**
4  * sfCacheSessionStorage manages session storage via a signed cookie and cache backend.
5  *
6  * This class stores the session data in via sfCache instance and with an id issued in a
7  * signed cookie. Useful when you don't want to store the session.
8  *
9  * @package    symfony
10  * @subpackage storage
11  * @author     Dustin Whittle <dustin.whittle@symfony-project.com>
12  */
13 class sfCacheSessionStorage extends sfStorage
14 {
15   protected
16     $id          = null,
17     $context     = null,
18     $dispatcher  = null,
19     $request     = null,
20     $response    = null,
21     $cache       = null,
22     $data        = array(),
23     $dataChanged = false;
24
25   /**
26    * Initialize this Storage.
27    *
28    * @param array $options  An associative array of initialization parameters.
29    *                        session_name [required] name of session to use
30    *                        session_cookie_path [required] cookie path
31    *                        session_cookie_domain [required] cookie domain
32    *                        session_cookie_lifetime [required] liftime of cookie
33    *                        session_cookie_secure [required] send only if secure connection
34    *                        session_cookie_http_only [required] accessible only via http protocol
35    *
36    * @return bool true, when initialization completes successfully, otherwise false.
37    *
38    * @throws <b>sfInitializationException</b> If an error occurs while initializing this Storage.
39    */
40   public function initialize($options = array())
41   {
42     // initialize parent
43     parent::initialize(array_merge(array('session_name' => 'sfproject',
44                                          'session_cookie_lifetime' => '+30 days',
45                                          'session_cookie_path' => '/',
46                                          'session_cookie_domain' => null,
47                                          'session_cookie_secure' => false,
48                                          'session_cookie_http_only' => true,
49                                          'session_cookie_secret' => 'sf$ecret'), $options));
50
51     // create cache instance
52     if (isset($this->options['cache']) && $this->options['cache']['class'])
53     {
54       $this->cache = new $this->options['cache']['class'](is_array($this->options['cache']['param']) ? $this->options['cache']['param'] : array());
55     }
56     else
57     {
58       throw new InvalidArgumentException('sfCacheSessionStorage requires cache option.');
59     }
60
61     $this->context     = sfContext::getInstance();
62
63     $this->dispatcher  = $this->context->getEventDispatcher();
64     $this->request     = $this->context->getRequest();
65     $this->response    = $this->context->getResponse();
66
67     $cookie = $this->request->getCookie($this->options['session_name']);
68
69     if(strpos($cookie, ':') !== false)
70     {
71       // split cookie data id:signature(id+secret)
72       list($id, $signature) = explode(':', $cookie, 2);
73
74       if($signature == sha1($id.':'.$this->options['session_cookie_secret']))
75       {
76         // cookie is valid
77         $this->id = $id;
78       }
79       else
80       {
81         // cookie signature broken
82         $this->id = null;
83       }
84     }
85     else
86     {
87       // cookie format wrong
88       $this->id = null;
89     }
90
91     if(empty($this->id))
92     {
93        $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'localhost';
94        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'ua';
95
96        // generate new id based on random # / ip / user agent / secret
97        $this->id = md5(rand(0, 999999).$ip.$ua.$this->options['session_cookie_secret']);
98
99        if(sfConfig::get('sf_logging_enabled'))
100        {
101          $this->dispatcher->notify(new sfEvent($this, 'application.log', array('New session created')));
102        }
103
104        // only send cookie when id is issued
105        $this->response->setCookie($this->options['session_name'],
106                                   $this->id.':'.sha1($this->id.':'.$this->options['session_cookie_secret']),
107                                   $this->options['session_cookie_lifetime'],
108                                   $this->options['session_cookie_path'],
109                                   $this->options['session_cookie_domain'],
110                                   $this->options['session_cookie_secure'],
111                                   $this->options['session_cookie_http_only']);
112
113        $this->data = array();
114     }
115     else
116     {
117       // load data from cache
118       $this->data = $this->cache->get($this->id, array());
119
120       if(sfConfig::get('sf_logging_enabled'))
121       {
122         $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Restored previous session')));
123       }
124     }
125     session_id($this->id);
126     return true;
127   }
128
129   /**
130    * Write data to this storage.
131    *
132    * The preferred format for a key is directory style so naming conflicts can be avoided.
133    *
134    * @param string $key  A unique key identifying your data.
135    * @param mixed  $data Data associated with your key.
136    *
137    * @return void
138    */
139   public function write($key, $data)
140   {
141     $this->dataChanged = true;
142
143     $this->data[$key] =& $data;
144   }
145
146   /**
147    * Read data from this storage.
148    *
149    * The preferred format for a key is directory style so naming conflicts can be avoided.
150    *
151    * @param string $key A unique key identifying your data.
152    *
153    * @return mixed Data associated with the key.
154    */
155   public function read($key)
156   {
157     $retval = null;
158
159     if (isset($this->data[$key]))
160     {
161       $retval =& $this->data[$key];
162     }
163
164     return $retval;
165   }
166
167   /**
168    * Remove data from this storage.
169    *
170    * The preferred format for a key is directory style so naming conflicts can be avoided.
171    *
172    * @param string $key A unique key identifying your data.
173    *
174    * @return mixed Data associated with the key.
175    */
176   public function remove($key)
177   {
178     $retval = null;
179
180     if (isset($this->data[$key]))
181     {
182       $this->dataChanged = true;
183
184       $retval =& $this->data[$key];
185       unset($this->data[$key]);
186     }
187
188     return $retval;
189   }
190
191   /**
192    * Regenerates id that represents this storage.
193    *
194    * @param boolean $destroy Destroy session when regenerating?
195    *
196    * @return boolean True if session regenerated, false if error
197    *
198    * @throws <b>sfStorageException</b> If an error occurs while regenerating this storage
199    */
200   public function regenerate($destroy = false)
201   {
202     if($destroy)
203     {
204       $this->data = array();
205       $this->cache->remove($this->id);
206     }
207
208     // generate session id
209     $this->id = md5(rand(0, 999999).$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].$this->options['session_cookie_secret']);
210
211     // save data to cache
212     $this->cache->set($this->id, $this->data);
213
214     // update session id in signed cookie
215     $this->response->setCookie($this->options['session_name'],
216                                $this->id.':'.sha1($this->id.':'.$this->options['session_cookie_secret']),
217                                $this->options['session_cookie_lifetime'],
218                                $this->options['session_cookie_path'],
219                                $this->options['session_cookie_domain'],
220                                $this->options['session_cookie_secure'],
221                                $this->options['session_cookie_http_only']);
222     session_id($this->id);
223     return true;
224   }
225
226   /**
227    * Expires the session storage instance.
228    */
229   public function expire()
230   {
231     // destroy data and regenerate id
232     $this->regenerate(true);
233
234     if(sfConfig::get('sf_logging_enabled'))
235     {
236       $this->dispatcher->notify(new sfEvent($this, 'application.log', array('new session created due to expiraton')));
237     }
238   }
239
240   /**
241    * Executes the shutdown procedure.
242    *
243    * @throws <b>sfStorageException</b> If an error occurs while shutting down this storage
244    */
245   public function shutdown()
246   {
247     // only update cache if session has changed
248     if($this->dataChanged === true)
249     {
250       $this->cache->set($this->id, $this->data);
251       if(sfConfig::get('sf_logging_enabled'))
252       {
253         $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Storing session to cache')));
254         // $this->dispatcher->notify(new sfEvent($this, 'application.log', array(var_export($this->data, true))));
255       }
256     }
257   }
258 }
259
Note: See TracBrowser for help on using the browser.