Passed
Push — 1.0.0-dev ( ea6d41...cf37bc )
by nguereza
03:00
created

DBSessionHandler::getSessionDataParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 10
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 save path [' . $savePath . '], session name [' . $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
            list($ip, $host, $browser) = $this->getSessionDataParams();
187
			
188
            $data = $instance->get_by(array($columns['sid'] => $sid, $columns['shost'] => $host, $columns['sbrowser'] => $browser));
189
            if ($data && isset($data->{$columns['sdata']})) {
190
                //checking inactivity 
191
                $timeInactivity = time() - get_config('session_inactivity_time', 100);
192
                if ($data->{$columns['stime']} < $timeInactivity) {
193
                    $this->logger->info('Database session data for SID: ' . $sid . ' already expired, destroy it');
194
                    $this->destroy($sid);
195
                    return null;
196
                }
197
                return $this->decode($data->{$columns['sdata']});
198
            }
199
            $this->logger->info('Database session data for SID: ' . $sid . ' is not valid return false, may be the session ID is wrong');
200
            return null;
201
        }
202
203
        /**
204
         * Save the session data
205
         * @param  string $sid  the session ID
206
         * @param  mixed $data the session data to save in serialize format
207
         * @return boolean 
208
         */
209
        public function write($sid, $data) {
210
            $this->logger->debug('Saving database session data for SID: ' . $sid . ', data: ' . stringfy_vars($data));
211
            $instance = $this->getModelInstance();
212
            $columns = $this->sessionTableColumns;
213
            $keyValue = $instance->getKeyValue();
214
            list($ip, $host, $browser) = $this->getSessionDataParams();
215
            $data = $this->encode($data);
216
            $params = array(
217
                            $columns['sid'] => $sid,
218
                            $columns['sdata'] => $data,
219
                            $columns['stime'] => time(),
220
                            $columns['shost'] => $host,
221
                            $columns['sbrowser'] => $browser,
222
                            $columns['sip'] => $ip,
223
                            $columns['skey'] => $keyValue
224
                        );
225
            $this->logger->info('Database session data to save are listed below :' . stringfy_vars($params));
226
            $exists = $instance->get($sid);
227
            if ($exists) {
228
                $this->logger->info('Session data for SID: ' . $sid . ' already exists, just update it');
229
                //update
230
                unset($params[$columns['sid']]);
231
                return $instance->update($sid, $params);
232
            }
233
            $this->logger->info('Session data for SID: ' . $sid . ' not yet exists, insert it now');
234
            return $instance->insert($params);
235
        }
236
237
238
        /**
239
         * Destroy the session data for the given session id
240
         * @param  string $sid the session id value
241
         * @return boolean
242
         */
243
        public function destroy($sid) {
244
            $this->logger->debug('Destroy of session data for SID: ' . $sid);
245
            $instance = $this->modelInstance;
246
            $instance->delete($sid);
247
            return true;
248
        }
249
250
        /**
251
         * Clean the expire session data to save espace
252
         * @param  integer $maxLifetime the max lifetime
253
         * @return boolean
254
         */
255
        public function gc($maxLifetime) {
256
            $instance = $this->modelInstance;
257
            $time = time() - $maxLifetime;
258
            $this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
259
            $instance->deleteByTime($time);
260
            return true;
261
        }
262
263
        /**
264
         * Encode the session data using the openssl
265
         * @param  mixed $data the session data to encode
266
         * @return mixed the encoded session data
267
         */
268
        public function encode($data) {
269
            $key = base64_decode($this->sessionSecret);
270
            $dataEncrypted = openssl_encrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
271
            $output = base64_encode($dataEncrypted);
272
            return $output;
273
        }
274
275
276
        /**
277
         * Decode the session data using the openssl
278
         * @param  mixed $data the data to decode
279
         * @return mixed       the decoded data
280
         */
281
        public function decode($data) {
282
            $key = base64_decode($this->sessionSecret);
283
            $data = base64_decode($data);
284
            $data = openssl_decrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
285
            return $data;
286
        }
287
288
		
289
        /**
290
         * Return the loader instance
291
         * @return object Loader the loader instance
292
         */
293
        public function getLoader() {
294
            return $this->loader;
295
        }
296
297
        /**
298
         * set the loader instance for future use
299
         * @param object Loader $loader the loader object
300
         */
301
            public function setLoader($loader) {
302
            $this->loader = $loader;
303
            return $this;
304
        }
305
306
        /**
307
         * Return the model instance
308
         * @return object DBSessionHandlerModel the model instance
309
         */
310
        public function getModelInstance() {
311
            return $this->modelInstance;
312
        }
313
314
        /**
315
         * set the model instance for future use
316
         * @param DBSessionHandlerModel $modelInstance the model object
317
         */
318
            public function setModelInstance(DBSessionHandlerModel $modelInstance) {
319
            $this->modelInstance = $modelInstance;
320
            return $this;
321
        }
322
323
        /**
324
         * Set the model instance using the configuration for session
325
         */
326
        protected function setModelInstanceFromSessionConfig() {
327
            $modelName = get_config('session_save_path');
328
            $this->logger->info('The database session model: ' . $modelName);
329
            $this->loader->model($modelName, 'dbsessionhandlerinstance'); 
330
            //@codeCoverageIgnoreStart
331
            if (isset($this->OBJ->dbsessionhandlerinstance) 
332
                && !($this->OBJ->dbsessionhandlerinstance instanceof DBSessionHandlerModel)
333
            ) {
334
                show_error('To use database session handler, your class model "' . get_class($this->OBJ->dbsessionhandlerinstance) . '" need extends "DBSessionHandlerModel"');
335
            }  
336
            //@codeCoverageIgnoreEnd
337
			
338
            //set model instance
339
            $this->modelInstance = $this->OBJ->dbsessionhandlerinstance;
340
        }
341
342
        /**
343
         * Get some parameters data need like ip address, hostname, browser info, etc.
344
         * @return array
345
         */
346
        protected function getSessionDataParams(){
347
            $this->getLoader()->functions('user_agent'); 
348
            $this->getLoader()->library('Browser'); 
349
            
350
            $ip = get_ip();
351
            $host = gethostbyaddr($ip);
352
            $browser = $this->OBJ->browser->getPlatform() . ', ' 
353
                            . $this->OBJ->browser->getBrowser() 
354
                            . ' ' . $this->OBJ->browser->getVersion();
355
            return array($ip, $host, $browser);
356
        }
357
    }
358