Test Failed
Push — 1.0.0-dev ( 72449d...860cec )
by nguereza
02:46
created

DBSessionHandler::getModelInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 1
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 2
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 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: ' . stringfy_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($ip, $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->info('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: ' . stringfy_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 :' . stringfy_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
            $instance = $this->model;
203
            $instance->delete($sid);
204
            return true;
205
        }
206
207
        /**
208
         * Clean the expire session data to save espace
209
         * @param  integer $maxLifetime the max lifetime
210
         * @return boolean
211
         */
212
        public function gc($maxLifetime) {
213
            $instance = $this->model;
214
            $time = time() - $maxLifetime;
215
            $this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
216
            $instance->deleteExipredSession($time);
217
            return true;
218
        }
219
220
        /**
221
         * Set the session secret used to encrypt the data in database 
222
         * @param string $secret the base64 string secret
223
         */
224
        public function setSessionSecret($secret) {
225
            $this->sessionSecret = $secret;
226
            return $this;
227
        }
228
229
        /**
230
         * Return the session secret
231
         * @return string 
232
         */
233
        public function getSessionSecret() {
234
            return $this->sessionSecret;
235
        }
236
237
238
        /**
239
         * Set the initializer vector for openssl 
240
         * @param string $key the session secret used in base64 format
241
         */
242
        public function setInitializerVector($key) {
243
            $ivLength = openssl_cipher_iv_length(self::DB_SESSION_HASH_METHOD);
244
            $key = base64_decode($key);
245
            $this->initializerVector = substr(hash('sha256', $key), 0, $ivLength);
246
            return $this;
247
        }
248
249
        /**
250
         * Return the initializer vector
251
         * @return string 
252
         */
253
        public function getInitializerVector() {
254
            return $this->initializerVector;
255
        }
256
257
        /**
258
         * Return the model instance
259
         * @return object DBSessionHandlerModel the model instance
260
         */
261
        public function getModel() {
262
            return $this->model;
263
        }
264
265
        /**
266
         * set the model instance for future use
267
         * @param DBSessionHandlerModel $model the model object
268
         */
269
        public function setModel(DBSessionHandlerModel $model) {
270
            $this->model = $model;
271
            return $this;
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
        protected 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
        protected 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
         * Set the model instance using the configuration for session
302
         */
303
        protected function setModelFromSessionConfig() {
304
            $modelName = get_config('session_save_path');
305
            $this->logger->info('The database session model: ' . $modelName);
306
            $this->OBJ->loader->model($modelName, 'dbsessionhandlerinstance'); 
307
            //@codeCoverageIgnoreStart
308
            if (isset($this->OBJ->dbsessionhandlerinstance) 
309
                && !($this->OBJ->dbsessionhandlerinstance instanceof DBSessionHandlerModel)
310
            ) {
311
                show_error('To use database session handler, your class model "' . get_class($this->OBJ->dbsessionhandlerinstance) . '" need extends "DBSessionHandlerModel"');
312
            }  
313
            //@codeCoverageIgnoreEnd
314
			
315
            //set model instance
316
            $this->model = $this->OBJ->dbsessionhandlerinstance;
317
        }
318
319
        /**
320
         * Get some parameters data need like ip address, hostname, browser info, etc.
321
         * @return array
322
         */
323
        protected function getSessionDataParams(){
324
            $this->OBJ->loader->functions('user_agent'); 
325
            $this->OBJ->loader->library('Browser'); 
326
            
327
            $ip = get_ip();
328
            $host = gethostbyaddr($ip);
329
            $browser = $this->OBJ->browser->getPlatform() . ', ' 
330
                            . $this->OBJ->browser->getBrowser() 
331
                            . ' ' . $this->OBJ->browser->getVersion();
332
            return array($ip, $host, $browser);
333
        }
334
    }
335