~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Helper/Session.php

  • Committer: Dan Garner
  • Date: 2016-02-04 14:13:07 UTC
  • mto: (454.4.101)
  • mto: This revision was merged to the branch mainline in revision 483.
  • Revision ID: git-v1:f9078f575b16a62a51e2f3d7bc15581058fb0747
Problem with DataSet form putting a 0 in library image references (should be empty)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
2
/*
3
3
 * Xibo - Digital Signage - http://www.xibo.org.uk
4
 
 * Copyright (C) 2006-2018 Spring Signage Ltd
 
4
 * Copyright (C) 2006-2013 Daniel Garner
5
5
 *
6
6
 * This file is part of Xibo.
7
7
 *
20
20
 */
21
21
namespace Xibo\Helper;
22
22
 
23
 
use Xibo\Service\LogService;
24
 
use Xibo\Storage\PdoStorageService;
 
23
use Xibo\Storage\PDOConnect;
25
24
 
26
 
/**
27
 
 * Class Session
28
 
 * @package Xibo\Helper
29
 
 */
30
 
class Session implements \SessionHandlerInterface
 
25
class Session
31
26
{
32
 
    private $maxLifetime;
 
27
    private $max_lifetime;
33
28
    private $key;
34
29
 
35
30
    /**
39
34
    public $refreshExpiry = true;
40
35
 
41
36
    /**
42
 
     * Expiry time
43
 
     * @var int
44
 
     */
45
 
    private $sessionExpiry = 0;
46
 
 
47
 
    /**
48
37
     * Is the session expired?
49
 
     * @var bool
50
 
     */
51
 
    private $expired = true;
52
 
 
53
 
    /**
54
 
     * The UserId whom owns this session
55
38
     * @var int
56
39
     */
57
 
    private $userId = 0;
58
 
 
59
 
    /**
60
 
     * @var bool Whether gc() has been called
61
 
     */
62
 
    private $gcCalled = false;
63
 
 
64
 
    /**
65
 
     * Prune this key?
66
 
     * @var bool
67
 
     */
68
 
    private $pruneKey = false;
69
 
 
70
 
    /**
71
 
     * The database connection
72
 
     * @var PdoStorageService
73
 
     */
74
 
    private $pdo = null;
75
 
 
76
 
    /**
77
 
     * Log
78
 
     * @var LogService
79
 
     */
80
 
    private $log;
81
 
 
82
 
    /**
83
 
     * Session constructor.
84
 
     * @param LogService $log
85
 
     */
86
 
    function __construct($log)
 
40
    public $isExpired = 1;
 
41
 
 
42
    function __construct()
87
43
    {
88
 
        $this->log = $log;
89
 
 
90
44
        session_set_save_handler(
91
45
            array(&$this, 'open'),
92
46
            array(&$this, 'close'),
103
57
        session_start();
104
58
    }
105
59
 
106
 
    /**
107
 
     * {@inheritdoc}
108
 
     */
109
 
    public function open($savePath, $sessionName)
110
 
    {
111
 
        //$this->log->debug('Session open');
112
 
        $this->maxLifetime = ini_get('session.gc_maxlifetime');
113
 
        return true;
114
 
    }
115
 
 
116
 
    /**
117
 
     * {@inheritdoc}
118
 
     */
119
 
    public function close()
120
 
    {
121
 
        //$this->log->debug('Session close');
122
 
 
123
 
        try {
124
 
            // Commit
125
 
            $this->commit();
126
 
        } catch (\PDOException $e) {
127
 
            $this->log->error('Error closing session: %s', $e->getMessage());
128
 
        }
129
 
 
130
 
        try {
131
 
 
132
 
            // Prune this session if necessary
133
 
            if ($this->pruneKey || $this->gcCalled) {
134
 
                $db = new PdoStorageService($this->log);
135
 
                $db->setConnection();
136
 
 
137
 
                if ($this->pruneKey) {
138
 
                    $db->update('DELETE FROM `session` WHERE session_id = :session_id', array('session_id' => $this->key));
139
 
                }
140
 
 
141
 
                if ($this->gcCalled) {
142
 
                    // Delete sessions older than 10 times the max lifetime
143
 
                    $db->update('DELETE FROM `session` WHERE IsExpired = 1 AND session_expiration < :expiration', array('expiration' => (time() - ($this->maxLifetime * 10))));
144
 
 
145
 
                    // Update expired sessions as expired
146
 
                    $db->update('UPDATE `session` SET IsExpired = 1 WHERE session_expiration < :expiration', array('expiration' => time()));
147
 
                }
148
 
 
149
 
                $db->commitIfNecessary();
150
 
                $db->close();
151
 
            }
152
 
 
153
 
        } catch (\PDOException $e) {
154
 
            $this->log->error('Error closing session: %s', $e->getMessage());
155
 
        }
156
 
 
157
 
        // Close
158
 
        $this->getDb()->close();
159
 
 
160
 
        return true;
161
 
    }
162
 
 
163
 
    /**
164
 
     * {@inheritdoc}
165
 
     */
 
60
    function open($save_path, $session_name)
 
61
    {
 
62
        $this->max_lifetime = ini_get('session.gc_maxlifetime');
 
63
        return true;
 
64
    }
 
65
 
 
66
    function close()
 
67
    {
 
68
 
 
69
        $this->gc($this->max_lifetime);
 
70
        return true;
 
71
    }
 
72
 
166
73
    function read($key)
167
74
    {
168
 
        //$this->log->debug('Session read');
 
75
        $userAgent = substr(Sanitize::string($_SERVER['HTTP_USER_AGENT']), 0, 253);
 
76
        $remoteAddr = Sanitize::string($_SERVER['REMOTE_ADDR']);
 
77
        $securityToken = Sanitize::getString('SecurityToken');
169
78
 
170
 
        $data = '';
171
79
        $this->key = $key;
 
80
        $newExp = time() + $this->max_lifetime;
172
81
 
173
 
        $userAgent = substr($_SERVER['HTTP_USER_AGENT'], 0, 253);
 
82
        $this->gc($this->max_lifetime);
174
83
 
175
84
        try {
176
 
            $dbh = $this->getDb();
177
 
 
178
 
            // Start a transaction
179
 
            $this->beginTransaction();
 
85
            $dbh = PDOConnect::init();
180
86
 
181
87
            // Get this session
182
 
            $sth = $dbh->getConnection()->prepare('
183
 
                SELECT `session_data`, `isexpired`, `useragent`, `session_expiration`, `userId` 
184
 
                  FROM `session`
185
 
                 WHERE `session_id` = :session_id
186
 
            ');
187
 
            $sth->execute(['session_id' => $key]);
 
88
            $sth = $dbh->prepare('SELECT session_data, isexpired, securitytoken, useragent FROM session WHERE session_id = :session_id');
 
89
            $sth->execute(array('session_id' => $key));
 
90
 
 
91
            if (!$row = $sth->fetch())
 
92
                return settype($empty, "string");
 
93
 
 
94
            // What happens if the UserAgent has changed?
 
95
            if ($row['useragent'] != $userAgent) {
 
96
                // Make sure we are logged out (delete all data)
 
97
                $usth = $dbh->prepare('DELETE FROM session WHERE session_id = :session_id');
 
98
                $usth->execute(array('session_id' => $key));
 
99
 
 
100
                throw new \Exception('Different UserAgent');
 
101
            }
 
102
 
 
103
            // We have the Key and the Remote Address.
 
104
            if ($securityToken == null) {
 
105
                // If there is no security token then obey the IsExpired
 
106
                $this->isExpired = $row['isexpired'];
 
107
            } elseif ($securityToken == $row['securitytoken']) {
 
108
                // We have a security token, so dont require a login
 
109
                $this->isExpired = 0;
 
110
 
 
111
                $usth = $dbh->prepare('UPDATE session SET session_expiration = :expiry, isExpired = 0 WHERE session_id = :session_id');
 
112
                $usth->execute(array('session_id' => $key, 'expiry' => $newExp));
 
113
            } else {
 
114
                // Its set - but its wrong - not good
 
115
                Log::error('Incorrect SecurityToken from ' . $remoteAddr);
 
116
 
 
117
                $this->isExpired = 1;
 
118
            }
 
119
 
 
120
            // Either way - update this SESSION so that the security token is NULL
 
121
            $usth = $dbh->prepare('UPDATE `session` SET SecurityToken = NULL WHERE session_id = :session_id');
 
122
            $usth->execute(array('session_id' => $key));
 
123
 
 
124
            return ($row['session_data']);
 
125
        } catch (\Exception $e) {
 
126
            Log::error($e->getMessage());
 
127
            $empty = '';
 
128
            return settype($empty, "string");
 
129
        }
 
130
    }
 
131
 
 
132
    function write($key, $val)
 
133
    {
 
134
        $newExp = time() + $this->max_lifetime;
 
135
        $lastaccessed = date("Y-m-d H:i:s");
 
136
 
 
137
        $userAgent = substr(Sanitize::getString('HTTP_USER_AGENT', 'No user agent', $_SERVER), 0, 253);
 
138
        $remoteAddr = Sanitize::getString('REMOTE_ADDR');
 
139
 
 
140
        try {
 
141
            $dbh = PDOConnect::init();
 
142
 
 
143
            $sth = $dbh->prepare('SELECT session_id FROM session WHERE session_id = :session_id');
 
144
            $sth->execute(array('session_id' => $key));
188
145
 
189
146
            if (!$row = $sth->fetch()) {
190
 
                // New session.
191
 
                $this->insertSession($key, '', time(), time() + $this->maxLifetime);
192
 
 
193
 
                $this->expired = false;
194
 
 
 
147
                // Insert a new session
 
148
                $SQL = 'INSERT INTO `session` (session_id, session_data, session_expiration, lastaccessed, lastpage, userid, isexpired, useragent, remoteaddr)
 
149
                          VALUES (:session_id, :session_data, :session_expiration, :lastaccessed, :lastpage, :userid, :isexpired, :useragent, :remoteaddr) ';
 
150
 
 
151
                $isth = $dbh->prepare($SQL);
 
152
 
 
153
                $isth->execute(
 
154
                    array(
 
155
                        'session_id' => $key,
 
156
                        'session_data' => $val,
 
157
                        'session_expiration' => $newExp,
 
158
                        'lastaccessed' => $lastaccessed,
 
159
                        'lastpage' => 'login',
 
160
                        'userid' => NULL,
 
161
                        'isexpired' => 0,
 
162
                        'useragent' => $userAgent,
 
163
                        'remoteaddr' => $remoteAddr
 
164
                    )
 
165
                );
195
166
            } else {
196
 
                // Existing session
197
 
                // Check the session hasn't expired
198
 
                if ($row['session_expiration'] < time())
199
 
                    $this->expired = true;
200
 
                else
201
 
                    $this->expired = $row['isexpired'];
202
 
 
203
 
                // What happens if the UserAgent has changed?
204
 
                if ($row['useragent'] != $userAgent) {
205
 
                    // Force delete this session
206
 
                    $this->expired = 1;
207
 
                    $this->pruneKey = true;
 
167
                // Punch a very small hole in the authentication system
 
168
                // we do not want to update the expiry time of a session if it is the Clock Timer going off
 
169
                $autoRefresh = (isset($_REQUEST['autoRefresh']) && Sanitize::bool($_REQUEST['autoRefresh']));
 
170
 
 
171
                if (!$this->refreshExpiry || $autoRefresh) {
 
172
 
 
173
                    // Update the existing session without the expiry
 
174
                    $SQL = "UPDATE session SET session_data = :session_data WHERE session_id = :session_id ";
 
175
 
 
176
                    $isth = $dbh->prepare($SQL);
 
177
 
 
178
                    $isth->execute(
 
179
                        array('session_id' => $key, 'session_data' => $val)
 
180
                    );
 
181
                } else {
 
182
                    // Update the existing session
 
183
                    $SQL = "UPDATE `session` SET
 
184
                                session_data = :session_data,
 
185
                                session_expiration = :session_expiration,
 
186
                                lastaccessed    = :lastaccessed,
 
187
                                remoteaddr      = :remoteaddr
 
188
                         WHERE session_id = :session_id ";
 
189
 
 
190
                    $isth = $dbh->prepare($SQL);
 
191
 
 
192
                    $isth->execute(
 
193
                        array(
 
194
                            'session_id' => $key,
 
195
                            'session_data' => $val,
 
196
                            'session_expiration' => $newExp,
 
197
                            'lastaccessed' => $lastaccessed,
 
198
                            'remoteaddr' => $remoteAddr
 
199
                        )
 
200
                    );
208
201
                }
209
 
 
210
 
                $this->userId = $row['userId'];
211
 
                $this->sessionExpiry = $row['session_expiration'];
212
 
 
213
 
                // Set the session data (expired or not)
214
 
                $data = $row['session_data'];
215
202
            }
216
 
 
217
 
            return (string)$data;
218
 
 
219
 
        } catch (\Exception $e) {
220
 
            $this->log->error('Error reading session: %s', $e->getMessage());
221
 
 
222
 
            return (string)$data;
223
 
        }
224
 
    }
225
 
 
226
 
    /**
227
 
     * {@inheritdoc}
228
 
     */
229
 
    public function write($key, $val)
230
 
    {
231
 
        //$this->log->debug('Session write');
232
 
 
233
 
        // What should we do with expiry?
234
 
        $expiry = ($this->refreshExpiry) ? time() + $this->maxLifetime : $this->sessionExpiry;
235
 
 
236
 
        try {
237
 
            $this->updateSession($key, $val, time(), $expiry);
238
 
 
239
 
        } catch (\PDOException $e) {
240
 
            $this->log->error('Error writing session data: %s', $e->getMessage());
241
 
            return false;
242
 
        }
243
 
 
244
 
        return true;
245
 
    }
246
 
 
247
 
    /**
248
 
     * {@inheritdoc}
249
 
     */
250
 
    public function destroy($key)
251
 
    {
252
 
        //$this->log->debug('Session destroy');
253
 
        try {
254
 
            $this->getDb()->update('DELETE FROM `session` WHERE session_id = :session_id', ['session_id' => $key]);
255
 
        } catch (\PDOException $e) {
256
 
            $this->log->error('Error destroying session: %s', $e->getMessage());
257
 
        }
258
 
 
259
 
        return true;
260
 
    }
261
 
 
262
 
    /**
263
 
     * {@inheritdoc}
264
 
     */
265
 
    public function gc($maxLifetime)
266
 
    {
267
 
        //$this->log->debug('Session gc');
268
 
        $this->gcCalled = true;
269
 
        return true;
270
 
    }
271
 
 
272
 
    /**
273
 
     * Sets the User Id
274
 
     * @param $userId
275
 
     */
276
 
    public function setUser($userId)
277
 
    {
278
 
        //$this->log->debug('Setting user Id to %d', $userId);
279
 
        $_SESSION['userid'] = $userId;
280
 
        $this->userId = $userId;
 
203
        } catch (\Exception $e) {
 
204
            Log::error($e->getMessage());
 
205
            return false;
 
206
        }
 
207
 
 
208
        return true;
 
209
    }
 
210
 
 
211
    function destroy($key)
 
212
    {
 
213
        try {
 
214
            $dbh = PDOConnect::init();
 
215
 
 
216
            $sth = $dbh->prepare('UPDATE session SET IsExpired = 1 WHERE session_id = :session_id');
 
217
            $sth->execute(array('session_id', $key));
 
218
        } catch (\Exception $e) {
 
219
            Log::error($e->getMessage());
 
220
        }
 
221
 
 
222
        return true;
 
223
    }
 
224
 
 
225
    function gc($max_lifetime)
 
226
    {
 
227
        try {
 
228
            $dbh = PDOConnect::init();
 
229
 
 
230
            // Delete sessions older than 10 times the max lifetime
 
231
            $sth = $dbh->prepare('DELETE FROM `session` WHERE IsExpired = 1 AND session_expiration < :expiration');
 
232
            $sth->execute(array('expiration' => (time() - ($max_lifetime * 10))));
 
233
 
 
234
            // Update expired sessions as expired
 
235
            $sth = $dbh->prepare('UPDATE `session` SET IsExpired = 1 WHERE session_expiration < :expiration');
 
236
            $sth->execute(array('expiration' => time()));
 
237
        } catch (\Exception $e) {
 
238
            Log::error($e->getMessage());
 
239
        }
 
240
    }
 
241
 
 
242
    function setUser($key, $userid)
 
243
    {
 
244
        $_SESSION['userid'] = $userid;
 
245
 
 
246
        try {
 
247
            $dbh = PDOConnect::init();
 
248
 
 
249
            // Delete sessions older than 10 times the max lifetime
 
250
            $sth = $dbh->prepare('UPDATE `session` SET userid = :userid WHERE session_id = :session_id');
 
251
            $sth->execute(array('session_id' => $key, 'userid' => $userid));
 
252
        } catch (\Exception $e) {
 
253
            Log::error($e->getMessage());
 
254
            return false;
 
255
        }
281
256
    }
282
257
 
283
258
    /**
284
259
     * Updates the session ID with a new one
285
260
     */
286
 
    public function regenerateSessionId()
287
 
    {
288
 
        //$this->log->debug('Session regenerate');
289
 
        session_regenerate_id(true);
290
 
 
291
 
        $this->key = session_id();
292
 
 
293
 
        // PHP7 calls open/close on regenerate
294
 
        // PHP5 does neither
295
 
        if (version_compare(phpversion(), '7.0') === -1) {
296
 
            $this->insertSession($this->key, '', time(), time() + $this->maxLifetime);
297
 
        }
298
 
    }
299
 
 
300
 
    /**
301
 
     * Set this session to expired
302
 
     * @param $isExpired
303
 
     */
304
 
    public function setIsExpired($isExpired)
305
 
    {
306
 
        $this->expired = $isExpired;
 
261
    public function regenerateSessionId($oldSessionID)
 
262
    {
 
263
 
 
264
        session_regenerate_id(false);
 
265
 
 
266
        $new_sess_id = session_id();
 
267
 
 
268
        $this->key = $new_sess_id;
 
269
 
 
270
        try {
 
271
            $dbh = PDOConnect::init();
 
272
 
 
273
            // Delete sessions older than 10 times the max lifetime
 
274
            $sth = $dbh->prepare('UPDATE session SET session_id = :new_session_id WHERE session_id = :session_id');
 
275
            $sth->execute(array('session_id' => $oldSessionID, 'new_session_id' => $new_sess_id));
 
276
        } catch (\Exception $e) {
 
277
            Log::error($e->getMessage());
 
278
            return false;
 
279
        }
 
280
    }
 
281
 
 
282
    function setPage($key, $lastpage)
 
283
    {
 
284
        $_SESSION['pagename'] = $lastpage;
 
285
 
 
286
        try {
 
287
            $dbh = PDOConnect::init();
 
288
 
 
289
            // Delete sessions older than 10 times the max lifetime
 
290
            $sth = $dbh->prepare('UPDATE session SET lastpage = :lastpage WHERE session_id = :session_id');
 
291
            $sth->execute(array('session_id' => $key, 'lastpage' => $lastpage));
 
292
        } catch (\Exception $e) {
 
293
            Log::error($e->getMessage());
 
294
            return false;
 
295
        }
 
296
    }
 
297
 
 
298
    function setIsExpired($isExpired)
 
299
    {
 
300
        $this->isExpired = $isExpired;
 
301
 
 
302
        try {
 
303
            $dbh = PDOConnect::init();
 
304
 
 
305
            // Delete sessions older than 10 times the max lifetime
 
306
            $sth = $dbh->prepare('UPDATE session SET isexpired = :isexpired WHERE session_id = :session_id');
 
307
            $sth->execute(array('session_id' => $this->key, 'isexpired' => $isExpired));
 
308
        } catch (\Exception $e) {
 
309
            Log::error($e->getMessage());
 
310
            return false;
 
311
        }
307
312
    }
308
313
 
309
314
    /**
345
350
 
346
351
        return false;
347
352
    }
348
 
 
349
 
    /**
350
 
     * Is the session expired?
351
 
     * @return bool
352
 
     */
353
 
    public function isExpired()
354
 
    {
355
 
        return $this->expired;
356
 
    }
357
 
 
358
 
    /**
359
 
     * Get a Database
360
 
     * @return PdoStorageService
361
 
     */
362
 
    private function getDb()
363
 
    {
364
 
        if ($this->pdo == null)
365
 
            $this->pdo = (new PdoStorageService($this->log))->setConnection();
366
 
 
367
 
        return $this->pdo;
368
 
    }
369
 
 
370
 
    /**
371
 
     * Helper method to begin a transaction.
372
 
     *
373
 
     * MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
374
 
     * due to http://www.mysqlperformanceblog.com/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
375
 
     * So we change it to READ COMMITTED.
376
 
     */
377
 
    private function beginTransaction()
378
 
    {
379
 
        if (!$this->getDb()->getConnection()->inTransaction() && DBVERSION > 122) {
380
 
            try {
381
 
                $this->getDb()->getConnection()->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
382
 
            } catch (\PDOException $e) {
383
 
                // https://github.com/xibosignage/xibo/issues/787
384
 
                // this only works if BINLOG format is set to MIXED or ROW
385
 
                $this->log->error('Unable to set session transaction isolation level, message = ' . $e->getMessage());
386
 
            }
387
 
            $this->getDb()->getConnection()->beginTransaction();
388
 
        }
389
 
    }
390
 
 
391
 
    /**
392
 
     * Commit
393
 
     */
394
 
    private function commit()
395
 
    {
396
 
        if ($this->getDb()->getConnection()->inTransaction())
397
 
            $this->getDb()->getConnection()->commit();
398
 
    }
399
 
 
400
 
    /**
401
 
     * Insert session
402
 
     * @param $key
403
 
     * @param $data
404
 
     * @param $lastAccessed
405
 
     * @param $expiry
406
 
     */
407
 
    private function insertSession($key, $data, $lastAccessed, $expiry)
408
 
    {
409
 
        //$this->log->debug('Session insert');
410
 
 
411
 
        $sql = '
412
 
          INSERT INTO `session` (session_id, session_data, session_expiration, lastaccessed, userid, isexpired, useragent, remoteaddr)
413
 
            VALUES (:session_id, :session_data, :session_expiration, :lastAccessed, :userId, :expired, :useragent, :remoteaddr)
414
 
        ';
415
 
 
416
 
        $params = [
417
 
            'session_id' => $key,
418
 
            'session_data' => $data,
419
 
            'session_expiration' => $expiry,
420
 
            'lastAccessed' => date('Y-m-d H:i:s', $lastAccessed),
421
 
            'userId' => $this->userId,
422
 
            'expired' => ($this->expired) ? 1 : 0,
423
 
            'useragent' => substr($_SERVER['HTTP_USER_AGENT'], 0, 253),
424
 
            'remoteaddr' => $_SERVER['REMOTE_ADDR']
425
 
        ];
426
 
 
427
 
        $this->getDb()->update($sql, $params);
428
 
    }
429
 
 
430
 
    /**
431
 
     * Update Session
432
 
     * @param $key
433
 
     * @param $data
434
 
     * @param $lastAccessed
435
 
     * @param $expiry
436
 
     */
437
 
    private function updateSession($key, $data, $lastAccessed, $expiry)
438
 
    {
439
 
        //$this->log->debug('Session update');
440
 
 
441
 
        $sql = '
442
 
            UPDATE `session` SET
443
 
              session_data = :session_data,
444
 
              session_expiration = :session_expiration,
445
 
              LastAccessed = :lastAccessed,
446
 
              userID = :userId,
447
 
              IsExpired = :expired
448
 
            WHERE session_id = :session_id
449
 
        ';
450
 
 
451
 
        $params = [
452
 
            'session_data' => $data,
453
 
            'session_expiration' => $expiry,
454
 
            'lastAccessed' => date('Y-m-d H:i:s', $lastAccessed),
455
 
            'userId' => $this->userId,
456
 
            'expired' => ($this->expired) ? 1 : 0,
457
 
            'session_id' => $key
458
 
        ];
459
 
 
460
 
        $this->getDb()->update($sql, $params);
461
 
    }
462
353
}
 
354
 
 
355
?>
 
 
b'\\ No newline at end of file'