Test Failed
Push — develop ( fe93c2...a526c9 )
by nguereza
05:50
created

DBSessionHandler::read()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
eloc 14
c 2
b 1
f 0
nc 3
nop 1
dl 0
loc 19
rs 9.7998
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 MIT License (MIT)
9
     *
10
     * Copyright (c) 2017 TNH Framework
11
     *
12
     * Permission is hereby granted, free of charge, to any person obtaining a copy
13
     * of this software and associated documentation files (the "Software"), to deal
14
     * in the Software without restriction, including without limitation the rights
15
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
     * copies of the Software, and to permit persons to whom the Software is
17
     * furnished to do so, subject to the following conditions:
18
     *
19
     * The above copyright notice and this permission notice shall be included in all
20
     * copies or substantial portions of the Software.
21
     *
22
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
     * SOFTWARE.
29
     */
30
	
31
    /**
32
     * check if the interface "SessionHandlerInterface" exists (normally in PHP 5.4 this already exists)
33
     */
34
    if (!interface_exists('SessionHandlerInterface')) {
35
        show_error('"SessionHandlerInterface" interface does not exists or is disabled can not use it to handler database session.');
36
    }
37
38
    class DBSessionHandler extends BaseClass implements SessionHandlerInterface {
39
		
40
        /**
41
         * The encryption method to use to encrypt session data in database
42
         * @const string
43
         */
44
        const DB_SESSION_HASH_METHOD = 'AES-256-CBC';
45
		
46
        /**
47
         * Super global instance
48
         * @var object
49
         */
50
        protected $OBJ = null;
51
52
        /**
53
         * Session secret to use 
54
         * @var string
55
         */
56
        private $sessionSecret = null;
57
58
        /**
59
         * The initialisation vector to use for openssl
60
         * @var string
61
         */
62
        private $initializerVector = null;
63
64
        /**
65
         * The model instance to use
66
         * @var object
67
         */
68
        private $model = null;
69
70
        /**
71
         * The columns of the table to use to store session data
72
         * @var array
73
         */
74
        private $sessionTableColumns = array();
75
76
        /**
77
         * Create new instance of Database session handler
78
         * @param object $model the model instance
79
         */
80
        public function __construct(DBSessionHandlerModel $model = null) {
81
            parent::__construct();
82
            $this->OBJ = & get_instance();
83
            if (is_object($model)) {
84
                $this->setModel($model);
85
            }
86
        }
87
88
        
89
        /**
90
         * Open the database session handler, here nothing to do just return true
91
         * @param  string $savePath    the session save path
92
         * @param  string $sessionName the session name
93
         * @return boolean 
94
         */
95
        public function open($savePath, $sessionName) {
96
            $this->logger->debug('Opening database session handler save path [' . $savePath . '], session name [' . $sessionName . ']');
97
            //try to check if session secret is set before
98
            $secret = $this->getSessionSecret();
99
            if (empty($secret)) {
100
                $secret = get_config('session_secret', null);
101
                $this->setSessionSecret($secret);
102
            }
103
            $this->logger->info('Session secret: ' . $secret);
104
105
            if (!is_object($this->model)) {
106
                $this->setModelFromSessionConfig();
107
            }
108
            $this->setInitializerVector($secret);
109
110
            //set session tables columns
111
            $this->sessionTableColumns = $this->model->getSessionTableColumns();
112
113
            if (empty($this->sessionTableColumns)) {
114
                show_error('The session handler is "database" but the table columns is not set');
115
            }
116
            $this->logger->info('Database session, the model columns are listed below: ' . stringify_vars($this->sessionTableColumns));
117
			
118
            //delete the expired session
119
            $timeActivity = get_config('session_inactivity_time', 100);
120
            $this->gc($timeActivity);
121
			
122
            return true;
123
        }
124
125
        /**
126
         * Close the session
127
         * @return boolean
128
         */
129
        public function close() {
130
            $this->logger->debug('Closing database session handler');
131
            return true;
132
        }
133
134
        /**
135
         * Get the session value for the given session id
136
         * @param  string $sid the session id to use
137
         * @return string      the session data in serialiaze format
138
         */
139
        public function read($sid) {
140
            $this->logger->debug('Reading database session data for SID: ' . $sid);
141
            $instance = $this->getModel();
142
            $columns = $this->sessionTableColumns;
143
            list(, $host, $browser) = $this->getSessionDataParams();
144
			
145
            $data = $instance->getSingleRecordCond(array($columns['sid'] => $sid, $columns['shost'] => $host, $columns['sbrowser'] => $browser));
146
            if ($data && isset($data->{$columns['sdata']})) {
147
                //checking inactivity 
148
                $timeInactivity = time() - get_config('session_inactivity_time', 100);
149
                if ($data->{$columns['stime']} < $timeInactivity) {
150
                    $this->logger->info('Database session data for SID: ' . $sid . ' already expired, destroy it');
151
                    $this->destroy($sid);
152
                    return null;
153
                }
154
                return $this->decode($data->{$columns['sdata']});
155
            }
156
            $this->logger->warning('Database session data for SID: ' . $sid . ' is not valid return false, may be the session ID is wrong');
157
            return null;
158
        }
159
160
        /**
161
         * Save the session data
162
         * @param  string $sid  the session ID
163
         * @param  mixed $data the session data to save in serialize format
164
         * @return boolean 
165
         */
166
        public function write($sid, $data) {
167
            $this->logger->debug('Saving database session data for SID: ' . $sid . ', data: ' . stringify_vars($data));
168
            $instance = $this->getModel();
169
            $columns = $this->sessionTableColumns;
170
            $keyValue = $instance->getKeyValue();
171
            list($ip, $host, $browser) = $this->getSessionDataParams();
172
            $data = $this->encode($data);
173
            $params = array(
174
                            $columns['sid'] => $sid,
175
                            $columns['sdata'] => $data,
176
                            $columns['stime'] => time(),
177
                            $columns['shost'] => $host,
178
                            $columns['sbrowser'] => $browser,
179
                            $columns['sip'] => $ip,
180
                            $columns['skey'] => $keyValue
181
                        );
182
            $this->logger->info('Database session data to save are listed below :' . stringify_vars($params));
183
            $exists = $instance->getSingleRecord($sid);
184
            if ($exists) {
185
                $this->logger->info('Session data for SID: ' . $sid . ' already exists, just update it');
186
                //update
187
                unset($params[$columns['sid']]);
188
                return $instance->update($sid, $params);
189
            }
190
            $this->logger->info('Session data for SID: ' . $sid . ' not yet exists, insert it now');
191
            return $instance->insert($params);
192
        }
193
194
195
        /**
196
         * Destroy the session data for the given session id
197
         * @param  string $sid the session id value
198
         * @return boolean
199
         */
200
        public function destroy($sid) {
201
            $this->logger->debug('Destroy of session data for SID: ' . $sid);
202
            $this->model->delete($sid);
203
            return true;
204
        }
205
206
        /**
207
         * Clean the expire session data to save espace
208
         * @param  integer $maxLifetime the max lifetime
209
         * @return boolean
210
         */
211
        public function gc($maxLifetime) {
212
            $time = time() - $maxLifetime;
213
            $this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
214
            $this->model->deleteExipredSession($time);
215
            return true;
216
        }
217
218
        /**
219
         * Set the session secret used to encrypt the data in database 
220
         * @param string $secret the base64 string secret
221
         */
222
        public function setSessionSecret($secret) {
223
            $this->sessionSecret = $secret;
224
            return $this;
225
        }
226
227
        /**
228
         * Return the session secret
229
         * @return string 
230
         */
231
        public function getSessionSecret() {
232
            return $this->sessionSecret;
233
        }
234
235
236
        /**
237
         * Set the initializer vector for openssl 
238
         * @param string $key the session secret used in base64 format
239
         */
240
        public function setInitializerVector($key) {
241
            $ivLength = openssl_cipher_iv_length(self::DB_SESSION_HASH_METHOD);
242
            $key = base64_decode($key);
243
            $this->initializerVector = substr(hash('sha256', $key), 0, $ivLength);
244
            return $this;
245
        }
246
247
        /**
248
         * Return the initializer vector
249
         * @return string 
250
         */
251
        public function getInitializerVector() {
252
            return $this->initializerVector;
253
        }
254
255
        /**
256
         * Return the model instance
257
         * @return object DBSessionHandlerModel the model instance
258
         */
259
        public function getModel() {
260
            return $this->model;
261
        }
262
263
        /**
264
         * set the model instance for future use
265
         * @param DBSessionHandlerModel $model the model object
266
         */
267
        public function setModel(DBSessionHandlerModel $model) {
268
            $this->model = $model;
269
            return $this;
270
        }
271
272
        /**
273
         * Encode the session data using the openssl
274
         * @param  mixed $data the session data to encode
275
         * @return mixed the encoded session data
276
         */
277
        protected function encode($data) {
278
            $key = base64_decode($this->sessionSecret);
279
            $dataEncrypted = openssl_encrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
280
            $output = base64_encode($dataEncrypted);
281
            return $output;
282
        }
283
284
285
        /**
286
         * Decode the session data using the openssl
287
         * @param  mixed $data the data to decode
288
         * @return mixed       the decoded data
289
         */
290
        protected function decode($data) {
291
            $key = base64_decode($this->sessionSecret);
292
            $data = base64_decode($data);
293
            $data = openssl_decrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
294
            return $data;
295
        }
296
297
298
        /**
299
         * Set the model instance using the configuration for session
300
         */
301
        protected function setModelFromSessionConfig() {
302
            $modelName = get_config('session_save_path');
303
            $this->logger->info('The database session model: ' . $modelName);
304
            $this->OBJ->loader->model($modelName, 'dbsessionhandlerinstance'); 
305
            //@codeCoverageIgnoreStart
306
            if (isset($this->OBJ->dbsessionhandlerinstance) 
307
                && !($this->OBJ->dbsessionhandlerinstance instanceof DBSessionHandlerModel)
308
            ) {
309
                show_error('To use database session handler, your class model "' . get_class($this->OBJ->dbsessionhandlerinstance) . '" need extends "DBSessionHandlerModel"');
310
            }  
311
            //@codeCoverageIgnoreEnd
312
			
313
            //set model instance
314
            $this->model = $this->OBJ->dbsessionhandlerinstance;
315
        }
316
317
        /**
318
         * Get some parameters need like ip address, hostname, browser info, etc.
319
         * @return array
320
         */
321
        protected function getSessionDataParams(){
322
            $this->OBJ->loader->functions('user_agent'); 
323
            $this->OBJ->loader->library('Browser'); 
324
            
325
            $ip = get_ip();
326
            $host = gethostbyaddr($ip);
327
            $browser = $this->OBJ->browser->getPlatform() . ', ' 
328
                            . $this->OBJ->browser->getBrowser() 
329
                            . ' ' . $this->OBJ->browser->getVersion();
330
            return array($ip, $host, $browser);
331
        }
332
    }
333