Test Failed
Push — 1.0.0-dev ( 76319a...aa9039 )
by nguereza
02:25
created

DBSessionHandler::setLoader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 1
b 1
f 0
nc 1
nop 1
dl 0
loc 3
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 $modelInstance = 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 $modelInstance the model instance
79
         */
80
        public function __construct(DBSessionHandlerModel $modelInstance = null) {
81
            parent::__construct();
82
            $this->OBJ = & get_instance();
83
            if (is_object($modelInstance)) {
84
                $this->setModelInstance($modelInstance);
85
            }
86
        }
87
88
        /**
89
         * Set the session secret used to encrypt the data in database 
90
         * @param string $secret the base64 string secret
91
         */
92
        public function setSessionSecret($secret) {
93
            $this->sessionSecret = $secret;
94
            return $this;
95
        }
96
97
        /**
98
         * Return the session secret
99
         * @return string 
100
         */
101
        public function getSessionSecret() {
102
            return $this->sessionSecret;
103
        }
104
105
106
        /**
107
         * Set the initializer vector for openssl 
108
         * @param string $key the session secret used in base64 format
109
         */
110
        public function setInitializerVector($key) {
111
            $ivLength = openssl_cipher_iv_length(self::DB_SESSION_HASH_METHOD);
112
            $key = base64_decode($key);
113
            $this->initializerVector = substr(hash('sha256', $key), 0, $ivLength);
114
            return $this;
115
        }
116
117
        /**
118
         * Return the initializer vector
119
         * @return string 
120
         */
121
        public function getInitializerVector() {
122
            return $this->initializerVector;
123
        }
124
125
        /**
126
         * Open the database session handler, here nothing to do just return true
127
         * @param  string $savePath    the session save path
128
         * @param  string $sessionName the session name
129
         * @return boolean 
130
         */
131
        public function open($savePath, $sessionName) {
132
            $this->logger->debug('Opening database session handler save path [' . $savePath . '], session name [' . $sessionName . ']');
133
            //try to check if session secret is set before
134
            $secret = $this->getSessionSecret();
135
            if (empty($secret)) {
136
                $secret = get_config('session_secret', null);
137
                $this->setSessionSecret($secret);
138
            }
139
            $this->logger->info('Session secret: ' . $secret);
140
141
            if (!is_object($this->modelInstance)) {
142
                $this->setModelInstanceFromSessionConfig();
143
            }
144
            $this->setInitializerVector($secret);
145
146
            //set session tables columns
147
            $this->sessionTableColumns = $this->modelInstance->getSessionTableColumns();
148
149
            if (empty($this->sessionTableColumns)) {
150
                show_error('The session handler is "database" but the table columns not set');
151
            }
152
            $this->logger->info('Database session, the model columns are listed below: ' . stringfy_vars($this->sessionTableColumns));
153
			
154
            //delete the expired session
155
            $timeActivity = get_config('session_inactivity_time', 100);
156
            $this->gc($timeActivity);
157
			
158
            return true;
159
        }
160
161
        /**
162
         * Close the session
163
         * @return boolean
164
         */
165
        public function close() {
166
            $this->logger->debug('Closing database session handler');
167
            return true;
168
        }
169
170
        /**
171
         * Get the session value for the given session id
172
         * @param  string $sid the session id to use
173
         * @return string      the session data in serialiaze format
174
         */
175
        public function read($sid) {
176
            $this->logger->debug('Reading database session data for SID: ' . $sid);
177
            $instance = $this->getModelInstance();
178
            $columns = $this->sessionTableColumns;
179
            list($ip, $host, $browser) = $this->getSessionDataParams();
180
			
181
            $data = $instance->getSingleRecordCond(array($columns['sid'] => $sid, $columns['shost'] => $host, $columns['sbrowser'] => $browser));
182
            if ($data && isset($data->{$columns['sdata']})) {
183
                //checking inactivity 
184
                $timeInactivity = time() - get_config('session_inactivity_time', 100);
185
                if ($data->{$columns['stime']} < $timeInactivity) {
186
                    $this->logger->info('Database session data for SID: ' . $sid . ' already expired, destroy it');
187
                    $this->destroy($sid);
188
                    return null;
189
                }
190
                return $this->decode($data->{$columns['sdata']});
191
            }
192
            $this->logger->info('Database session data for SID: ' . $sid . ' is not valid return false, may be the session ID is wrong');
193
            return null;
194
        }
195
196
        /**
197
         * Save the session data
198
         * @param  string $sid  the session ID
199
         * @param  mixed $data the session data to save in serialize format
200
         * @return boolean 
201
         */
202
        public function write($sid, $data) {
203
            $this->logger->debug('Saving database session data for SID: ' . $sid . ', data: ' . stringfy_vars($data));
204
            $instance = $this->getModelInstance();
205
            $columns = $this->sessionTableColumns;
206
            $keyValue = $instance->getKeyValue();
207
            list($ip, $host, $browser) = $this->getSessionDataParams();
208
            $data = $this->encode($data);
209
            $params = array(
210
                            $columns['sid'] => $sid,
211
                            $columns['sdata'] => $data,
212
                            $columns['stime'] => time(),
213
                            $columns['shost'] => $host,
214
                            $columns['sbrowser'] => $browser,
215
                            $columns['sip'] => $ip,
216
                            $columns['skey'] => $keyValue
217
                        );
218
            $this->logger->info('Database session data to save are listed below :' . stringfy_vars($params));
219
            $exists = $instance->getSingleRecord($sid);
220
            if ($exists) {
221
                $this->logger->info('Session data for SID: ' . $sid . ' already exists, just update it');
222
                //update
223
                unset($params[$columns['sid']]);
224
                return $instance->update($sid, $params);
225
            }
226
            $this->logger->info('Session data for SID: ' . $sid . ' not yet exists, insert it now');
227
            return $instance->insert($params);
228
        }
229
230
231
        /**
232
         * Destroy the session data for the given session id
233
         * @param  string $sid the session id value
234
         * @return boolean
235
         */
236
        public function destroy($sid) {
237
            $this->logger->debug('Destroy of session data for SID: ' . $sid);
238
            $instance = $this->modelInstance;
239
            $instance->delete($sid);
240
            return true;
241
        }
242
243
        /**
244
         * Clean the expire session data to save espace
245
         * @param  integer $maxLifetime the max lifetime
246
         * @return boolean
247
         */
248
        public function gc($maxLifetime) {
249
            $instance = $this->modelInstance;
250
            $time = time() - $maxLifetime;
251
            $this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
252
            $instance->deleteExipredSession($time);
253
            return true;
254
        }
255
256
        /**
257
         * Encode the session data using the openssl
258
         * @param  mixed $data the session data to encode
259
         * @return mixed the encoded session data
260
         */
261
        public function encode($data) {
262
            $key = base64_decode($this->sessionSecret);
263
            $dataEncrypted = openssl_encrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
264
            $output = base64_encode($dataEncrypted);
265
            return $output;
266
        }
267
268
269
        /**
270
         * Decode the session data using the openssl
271
         * @param  mixed $data the data to decode
272
         * @return mixed       the decoded data
273
         */
274
        public function decode($data) {
275
            $key = base64_decode($this->sessionSecret);
276
            $data = base64_decode($data);
277
            $data = openssl_decrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
278
            return $data;
279
        }
280
281
        /**
282
         * Return the model instance
283
         * @return object DBSessionHandlerModel the model instance
284
         */
285
        public function getModelInstance() {
286
            return $this->modelInstance;
287
        }
288
289
        /**
290
         * set the model instance for future use
291
         * @param DBSessionHandlerModel $modelInstance the model object
292
         */
293
            public function setModelInstance(DBSessionHandlerModel $modelInstance) {
294
            $this->modelInstance = $modelInstance;
295
            return $this;
296
        }
297
298
        /**
299
         * Set the model instance using the configuration for session
300
         */
301
        protected function setModelInstanceFromSessionConfig() {
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->modelInstance = $this->OBJ->dbsessionhandlerinstance;
315
        }
316
317
        /**
318
         * Get some parameters data 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