Passed
Push — 1.0.0-dev ( 93958a...e1c8ef )
by nguereza
02:26
created

DBSessionHandler::setModelInstanceFromSessionConfig()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 14
rs 10
1
<?php 
2
	defined('ROOT_PATH') || exit('Access denied');
3
	/**
4
	 * TNH Framework
5
	 *
6
	 * A simple PHP framework using HMVC architecture
7
	 *
8
	 * This content is released under the GNU GPL License (GPL)
9
	 *
10
	 * Copyright (C) 2017 Tony NGUEREZA
11
	 *
12
	 * This program is free software; you can redistribute it and/or
13
	 * modify it under the terms of the GNU General Public License
14
	 * as published by the Free Software Foundation; either version 3
15
	 * of the License, or (at your option) any later version.
16
	 *
17
	 * This program is distributed in the hope that it will be useful,
18
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
	 * GNU General Public License for more details.
21
	 *
22
	 * You should have received a copy of the GNU General Public License
23
	 * along with this program; if not, write to the Free Software
24
	 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25
	*/
26
	
27
	/**
28
	 * check if the interface "SessionHandlerInterface" exists (normally in PHP 5.4 this already exists)
29
	 */
30
	if ( !interface_exists('SessionHandlerInterface')){
31
		show_error('"SessionHandlerInterface" interface does not exists or is disabled can not use it to handler database session.');
32
	}
33
34
	class DBSessionHandler extends BaseClass implements SessionHandlerInterface{
35
		
36
		/**
37
		 * The encryption method to use to encrypt session data in database
38
		 * @const string
39
		 */
40
		const DB_SESSION_HASH_METHOD = 'AES-256-CBC';
41
		
42
		/**
43
		 * Super global instance
44
		 * @var object
45
		 */
46
		protected $OBJ = null;
47
48
		/**
49
		 * Session secret to use 
50
		 * @var string
51
		 */
52
		private $sessionSecret = null;
53
54
		/**
55
		 * The initialisation vector to use for openssl
56
		 * @var string
57
		 */
58
		private $iv = null;
59
60
		/**
61
		 * The model instance to use
62
		 * @var object
63
		 */
64
		private $modelInstance = null;
65
66
		/**
67
		 * The columns of the table to use to store session data
68
		 * @var array
69
		 */
70
		private $sessionTableColumns = array();
71
72
		/**
73
         * Instance of the Loader class
74
         * @var Loader
75
         */
76
        protected $loader = null;
77
78
        /**
79
         * Create new instance of Database session handler
80
         * @param object $modelInstance the model instance
81
         */
82
		public function __construct(DBSessionHandlerModel $modelInstance = null){
83
			parent::__construct();
84
			
85
	    	//Set Loader instance to use
86
	        $this->setDependencyInstanceFromParamOrCreate('loader', null, 'Loader', 'classes');
87
	       
88
		    $this->OBJ = & get_instance();
89
		    
90
			if (is_object($modelInstance)){
91
				$this->setModelInstance($modelInstance);
92
			}
93
		}
94
95
		/**
96
		 * Set the session secret used to encrypt the data in database 
97
		 * @param string $secret the base64 string secret
98
		 */
99
		public function setSessionSecret($secret){
100
			$this->sessionSecret = $secret;
101
			return $this;
102
		}
103
104
		/**
105
		 * Return the session secret
106
		 * @return string 
107
		 */
108
		public function getSessionSecret(){
109
			return $this->sessionSecret;
110
		}
111
112
113
		/**
114
		 * Set the initializer vector for openssl 
115
		 * @param string $key the session secret used in base64 format
116
		 */
117
		public function setInitializerVector($key){
118
			$ivLength = openssl_cipher_iv_length(self::DB_SESSION_HASH_METHOD);
119
			$key = base64_decode($key);
120
			$this->iv = substr(hash('sha256', $key), 0, $ivLength);
121
			return $this;
122
		}
123
124
		/**
125
		 * Return the initializer vector
126
		 * @return string 
127
		 */
128
		public function getInitializerVector(){
129
			return $this->iv;
130
		}
131
132
		/**
133
		 * Open the database session handler, here nothing to do just return true
134
		 * @param  string $savePath    the session save path
135
		 * @param  string $sessionName the session name
136
		 * @return boolean 
137
		 */
138
		public function open($savePath, $sessionName){
139
			$this->logger->debug('Opening database session handler for [' . $sessionName . ']');
140
			//try to check if session secret is set before
141
			$secret = $this->getSessionSecret();
142
			if (empty($secret)){
143
				$secret = get_config('session_secret', null);
144
				$this->setSessionSecret($secret);
145
			}
146
			$this->logger->info('Session secret: ' . $secret);
147
148
			if (! is_object($this->modelInstance)){
149
				$this->setModelInstanceFromSessionConfig();
150
			}
151
			$this->setInitializerVector($secret);
152
153
			//set session tables columns
154
			$this->sessionTableColumns = $this->modelInstance->getSessionTableColumns();
155
156
			if (empty($this->sessionTableColumns)){
157
				show_error('The session handler is "database" but the table columns not set');
158
			}
159
			$this->logger->info('Database session, the model columns are listed below: ' . stringfy_vars($this->sessionTableColumns));
160
			
161
			//delete the expired session
162
			$timeActivity = get_config('session_inactivity_time', 100);
163
			$this->gc($timeActivity);
164
			
165
			return true;
166
		}
167
168
		/**
169
		 * Close the session
170
		 * @return boolean
171
		 */
172
		public function close(){
173
			$this->logger->debug('Closing database session handler');
174
			return true;
175
		}
176
177
		/**
178
		 * Get the session value for the given session id
179
		 * @param  string $sid the session id to use
180
		 * @return string      the session data in serialiaze format
181
		 */
182
		public function read($sid){
183
			$this->logger->debug('Reading database session data for SID: ' . $sid);
184
			$instance = $this->getModelInstance();
185
			$columns = $this->sessionTableColumns;
186
			$this->loader->functions('user_agent'); 
187
			$this->loader->library('Browser'); 
188
			
189
			$ip = get_ip();
190
			$host = @gethostbyaddr($ip) or null;
191
			$browser = $this->OBJ->browser->getPlatform().', '.$this->OBJ->browser->getBrowser().' '.$this->OBJ->browser->getVersion();
192
			
193
			$data = $instance->get_by(array($columns['sid'] => $sid, $columns['shost'] => $host, $columns['sbrowser'] => $browser));
194
			if ($data && isset($data->{$columns['sdata']})){
195
				//checking inactivity 
196
				$timeInactivity = time() - get_config('session_inactivity_time', 100);
197
				if ($data->{$columns['stime']} < $timeInactivity){
198
					$this->logger->info('Database session data for SID: ' . $sid . ' already expired, destroy it');
199
					$this->destroy($sid);
200
					return null;
201
				}
202
				return $this->decode($data->{$columns['sdata']});
203
			}
204
			$this->logger->info('Database session data for SID: ' . $sid . ' is not valid return false, may be the session ID is wrong');
205
			return null;
206
		}
207
208
		/**
209
		 * Save the session data
210
		 * @param  string $sid  the session ID
211
		 * @param  mixed $data the session data to save in serialize format
212
		 * @return boolean 
213
		 */
214
		public function write($sid, $data){
215
			$this->logger->debug('Saving database session data for SID: ' . $sid . ', data: ' . stringfy_vars($data));
216
			$instance = $this->getModelInstance();
217
			$columns = $this->sessionTableColumns;
218
219
			$this->loader->functions('user_agent'); 
220
			$this->loader->library('Browser'); 
221
222
			$ip = get_ip();
223
			$keyValue = $instance->getKeyValue();
224
			$host = @gethostbyaddr($ip) or null;
225
			$browser = $this->OBJ->browser->getPlatform().', '.$this->OBJ->browser->getBrowser().' '.$this->OBJ->browser->getVersion();
226
			$data = $this->encode($data);
227
			$params = array(
228
							$columns['sid'] => $sid,
229
							$columns['sdata'] => $data,
230
							$columns['stime'] => time(),
231
							$columns['shost'] => $host,
232
							$columns['sbrowser'] => $browser,
233
							$columns['sip'] => $ip,
234
							$columns['skey'] => $keyValue
235
						);
236
			$this->logger->info('Database session data to save are listed below :' . stringfy_vars($params));
237
			$exists = $instance->get($sid);
238
			if ($exists){
239
				$this->logger->info('Session data for SID: ' . $sid . ' already exists, just update it');
240
				//update
241
				unset($params[$columns['sid']]);
242
				return $instance->update($sid, $params);
243
			}
244
			$this->logger->info('Session data for SID: ' . $sid . ' not yet exists, insert it now');
245
			return $instance->insert($params);
246
		}
247
248
249
		/**
250
		 * Destroy the session data for the given session id
251
		 * @param  string $sid the session id value
252
		 * @return boolean
253
		 */
254
		public function destroy($sid){
255
			$this->logger->debug('Destroy of session data for SID: ' . $sid);
256
			$instance = $this->modelInstance;
257
			$instance->delete($sid);
258
			return true;
259
		}
260
261
		/**
262
		 * Clean the expire session data to save espace
263
		 * @param  integer $maxLifetime the max lifetime
264
		 * @return boolean
265
		 */
266
		public function gc($maxLifetime){
267
			$instance = $this->modelInstance;
268
			$time = time() - $maxLifetime;
269
			$this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
270
			$instance->deleteByTime($time);
271
			return true;
272
		}
273
274
		/**
275
		 * Encode the session data using the openssl
276
		 * @param  mixed $data the session data to encode
277
		 * @return mixed the encoded session data
278
		 */
279
		public function encode($data){
280
			$key = base64_decode($this->sessionSecret);
281
			$dataEncrypted = openssl_encrypt($data , self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
282
			$output = base64_encode($dataEncrypted);
283
			return $output;
284
		}
285
286
287
		/**
288
		 * Decode the session data using the openssl
289
		 * @param  mixed $data the data to decode
290
		 * @return mixed       the decoded data
291
		 */
292
		public function decode($data){
293
			$key = base64_decode($this->sessionSecret);
294
			$data = base64_decode($data);
295
			$data = openssl_decrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
296
			return $data;
297
		}
298
299
		
300
		/**
301
         * Return the loader instance
302
         * @return object Loader the loader instance
303
         */
304
        public function getLoader(){
305
            return $this->loader;
306
        }
307
308
        /**
309
         * set the loader instance for future use
310
         * @param object Loader $loader the loader object
311
         */
312
         public function setLoader($loader){
313
            $this->loader = $loader;
314
            return $this;
315
        }
316
317
        /**
318
         * Return the model instance
319
         * @return object DBSessionHandlerModel the model instance
320
         */
321
        public function getModelInstance(){
322
            return $this->modelInstance;
323
        }
324
325
        /**
326
         * set the model instance for future use
327
         * @param DBSessionHandlerModel $modelInstance the model object
328
         */
329
         public function setModelInstance(DBSessionHandlerModel $modelInstance){
330
            $this->modelInstance = $modelInstance;
331
            return $this;
332
        }
333
334
	    /**
335
	     * Set the model instance using the configuration for session
336
	     */
337
	    protected function setModelInstanceFromSessionConfig(){
338
	    	$modelName = get_config('session_save_path');
339
			$this->logger->info('The database session model: ' . $modelName);
340
			$this->loader->model($modelName, 'dbsessionhandlerinstance'); 
341
			//@codeCoverageIgnoreStart
342
            if (isset($this->OBJ->dbsessionhandlerinstance) 
343
            	&& ! ($this->OBJ->dbsessionhandlerinstance instanceof DBSessionHandlerModel)
344
            ) {
345
				show_error('To use database session handler, your class model "' . get_class($this->OBJ->dbsessionhandlerinstance) . '" need extends "DBSessionHandlerModel"');
346
			}  
347
			//@codeCoverageIgnoreEnd
348
			
349
			//set model instance
350
			$this->modelInstance = $this->OBJ->dbsessionhandlerinstance;
351
	    }
352
	}
353