統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / Model / Datasource / CakeSession.php @ 0b1b8047

履歴 | 表示 | アノテート | ダウンロード (18.596 KB)

1
<?php
2
/**
3
 * Session class for CakePHP.
4
 *
5
 * CakePHP abstracts the handling of sessions.
6
 * There are several convenient methods to access session information.
7
 * This class is the implementation of those methods.
8
 * They are mostly used by the Session Component.
9
 *
10
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
11
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * For full copyright and license information, please see the LICENSE.txt
15
 * Redistributions of files must retain the above copyright notice.
16
 *
17
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
18
 * @link          http://cakephp.org CakePHP(tm) Project
19
 * @package       Cake.Model.Datasource
20
 * @since         CakePHP(tm) v .0.10.0.1222
21
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
22
 */
23

    
24
App::uses('Hash', 'Utility');
25
App::uses('Security', 'Utility');
26

    
27
/**
28
 * Session class for CakePHP.
29
 *
30
 * CakePHP abstracts the handling of sessions. There are several convenient methods to access session information.
31
 * This class is the implementation of those methods. They are mostly used by the Session Component.
32
 *
33
 * @package       Cake.Model.Datasource
34
 */
35
class CakeSession {
36

    
37
/**
38
 * True if the Session is still valid
39
 *
40
 * @var bool
41
 */
42
        public static $valid = false;
43

    
44
/**
45
 * Error messages for this session
46
 *
47
 * @var array
48
 */
49
        public static $error = false;
50

    
51
/**
52
 * User agent string
53
 *
54
 * @var string
55
 */
56
        protected static $_userAgent = '';
57

    
58
/**
59
 * Path to where the session is active.
60
 *
61
 * @var string
62
 */
63
        public static $path = '/';
64

    
65
/**
66
 * Error number of last occurred error
67
 *
68
 * @var int
69
 */
70
        public static $lastError = null;
71

    
72
/**
73
 * Start time for this session.
74
 *
75
 * @var int
76
 */
77
        public static $time = false;
78

    
79
/**
80
 * Cookie lifetime
81
 *
82
 * @var int
83
 */
84
        public static $cookieLifeTime;
85

    
86
/**
87
 * Time when this session becomes invalid.
88
 *
89
 * @var int
90
 */
91
        public static $sessionTime = false;
92

    
93
/**
94
 * Current Session id
95
 *
96
 * @var string
97
 */
98
        public static $id = null;
99

    
100
/**
101
 * Hostname
102
 *
103
 * @var string
104
 */
105
        public static $host = null;
106

    
107
/**
108
 * Session timeout multiplier factor
109
 *
110
 * @var int
111
 */
112
        public static $timeout = null;
113

    
114
/**
115
 * Number of requests that can occur during a session time without the session being renewed.
116
 * This feature is only used when config value `Session.autoRegenerate` is set to true.
117
 *
118
 * @var int
119
 * @see CakeSession::_checkValid()
120
 */
121
        public static $requestCountdown = 10;
122

    
123
/**
124
 * Whether or not the init function in this class was already called
125
 *
126
 * @var bool
127
 */
128
        protected static $_initialized = false;
129

    
130
/**
131
 * Session cookie name
132
 *
133
 * @var string
134
 */
135
        protected static $_cookieName = null;
136

    
137
/**
138
 * Pseudo constructor.
139
 *
140
 * @param string|null $base The base path for the Session
141
 * @return void
142
 */
143
        public static function init($base = null) {
144
                static::$time = time();
145

    
146
                if (env('HTTP_USER_AGENT')) {
147
                        static::$_userAgent = md5(env('HTTP_USER_AGENT') . Configure::read('Security.salt'));
148
                }
149

    
150
                static::_setPath($base);
151
                static::_setHost(env('HTTP_HOST'));
152

    
153
                if (!static::$_initialized) {
154
                        register_shutdown_function('session_write_close');
155
                }
156

    
157
                static::$_initialized = true;
158
        }
159

    
160
/**
161
 * Setup the Path variable
162
 *
163
 * @param string|null $base base path
164
 * @return void
165
 */
166
        protected static function _setPath($base = null) {
167
                if (empty($base)) {
168
                        static::$path = '/';
169
                        return;
170
                }
171
                if (strpos($base, 'index.php') !== false) {
172
                        $base = str_replace('index.php', '', $base);
173
                }
174
                if (strpos($base, '?') !== false) {
175
                        $base = str_replace('?', '', $base);
176
                }
177
                static::$path = $base;
178
        }
179

    
180
/**
181
 * Set the host name
182
 *
183
 * @param string $host Hostname
184
 * @return void
185
 */
186
        protected static function _setHost($host) {
187
                static::$host = $host;
188
                if (strpos(static::$host, ':') !== false) {
189
                        static::$host = substr(static::$host, 0, strpos(static::$host, ':'));
190
                }
191
        }
192

    
193
/**
194
 * Starts the Session.
195
 *
196
 * @return bool True if session was started
197
 */
198
        public static function start() {
199
                if (static::started()) {
200
                        return true;
201
                }
202

    
203
                $id = static::id();
204
                static::_startSession();
205

    
206
                if (!$id && static::started()) {
207
                        static::_checkValid();
208
                }
209

    
210
                static::$error = false;
211
                static::$valid = true;
212
                return static::started();
213
        }
214

    
215
/**
216
 * Determine if Session has been started.
217
 *
218
 * @return bool True if session has been started.
219
 */
220
        public static function started() {
221
                return isset($_SESSION) && session_id();
222
        }
223

    
224
/**
225
 * Returns true if given variable is set in session.
226
 *
227
 * @param string $name Variable name to check for
228
 * @return bool True if variable is there
229
 */
230
        public static function check($name) {
231
                if (empty($name) || !static::_hasSession() || !static::start()) {
232
                        return false;
233
                }
234

    
235
                return Hash::get($_SESSION, $name) !== null;
236
        }
237

    
238
/**
239
 * Returns the session id.
240
 * Calling this method will not auto start the session. You might have to manually
241
 * assert a started session.
242
 *
243
 * Passing an id into it, you can also replace the session id if the session
244
 * has not already been started.
245
 * Note that depending on the session handler, not all characters are allowed
246
 * within the session id. For example, the file session handler only allows
247
 * characters in the range a-z A-Z 0-9 , (comma) and - (minus).
248
 *
249
 * @param string|null $id Id to replace the current session id
250
 * @return string Session id
251
 */
252
        public static function id($id = null) {
253
                if ($id) {
254
                        static::$id = $id;
255
                        session_id(static::$id);
256
                }
257
                if (static::started()) {
258
                        return session_id();
259
                }
260
                return static::$id;
261
        }
262

    
263
/**
264
 * Removes a variable from session.
265
 *
266
 * @param string $name Session variable to remove
267
 * @return bool Success
268
 */
269
        public static function delete($name) {
270
                if (static::check($name)) {
271
                        static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
272
                        return !static::check($name);
273
                }
274
                return false;
275
        }
276

    
277
/**
278
 * Used to write new data to _SESSION, since PHP doesn't like us setting the _SESSION var itself.
279
 *
280
 * @param array &$old Set of old variables => values
281
 * @param array $new New set of variable => value
282
 * @return void
283
 */
284
        protected static function _overwrite(&$old, $new) {
285
                if (!empty($old)) {
286
                        foreach ($old as $key => $var) {
287
                                if (!isset($new[$key])) {
288
                                        unset($old[$key]);
289
                                }
290
                        }
291
                }
292
                foreach ($new as $key => $var) {
293
                        $old[$key] = $var;
294
                }
295
        }
296

    
297
/**
298
 * Return error description for given error number.
299
 *
300
 * @param int $errorNumber Error to set
301
 * @return string Error as string
302
 */
303
        protected static function _error($errorNumber) {
304
                if (!is_array(static::$error) || !array_key_exists($errorNumber, static::$error)) {
305
                        return false;
306
                }
307
                return static::$error[$errorNumber];
308
        }
309

    
310
/**
311
 * Returns last occurred error as a string, if any.
312
 *
313
 * @return mixed Error description as a string, or false.
314
 */
315
        public static function error() {
316
                if (static::$lastError) {
317
                        return static::_error(static::$lastError);
318
                }
319
                return false;
320
        }
321

    
322
/**
323
 * Returns true if session is valid.
324
 *
325
 * @return bool Success
326
 */
327
        public static function valid() {
328
                if (static::start() && static::read('Config')) {
329
                        if (static::_validAgentAndTime() && static::$error === false) {
330
                                static::$valid = true;
331
                        } else {
332
                                static::$valid = false;
333
                                static::_setError(1, 'Session Highjacking Attempted !!!');
334
                        }
335
                }
336
                return static::$valid;
337
        }
338

    
339
/**
340
 * Tests that the user agent is valid and that the session hasn't 'timed out'.
341
 * Since timeouts are implemented in CakeSession it checks the current static::$time
342
 * against the time the session is set to expire. The User agent is only checked
343
 * if Session.checkAgent == true.
344
 *
345
 * @return bool
346
 */
347
        protected static function _validAgentAndTime() {
348
                $config = static::read('Config');
349
                $validAgent = (
350
                        Configure::read('Session.checkAgent') === false ||
351
                        isset($config['userAgent']) && static::$_userAgent === $config['userAgent']
352
                );
353
                return ($validAgent && static::$time <= $config['time']);
354
        }
355

    
356
/**
357
 * Get / Set the user agent
358
 *
359
 * @param string|null $userAgent Set the user agent
360
 * @return string Current user agent.
361
 */
362
        public static function userAgent($userAgent = null) {
363
                if ($userAgent) {
364
                        static::$_userAgent = $userAgent;
365
                }
366
                if (empty(static::$_userAgent)) {
367
                        CakeSession::init(static::$path);
368
                }
369
                return static::$_userAgent;
370
        }
371

    
372
/**
373
 * Returns given session variable, or all of them, if no parameters given.
374
 *
375
 * @param string|null $name The name of the session variable (or a path as sent to Set.extract)
376
 * @return mixed The value of the session variable, null if session not available,
377
 *   session not started, or provided name not found in the session, false on failure.
378
 */
379
        public static function read($name = null) {
380
                if (empty($name) && $name !== null) {
381
                        return null;
382
                }
383
                if (!static::_hasSession() || !static::start()) {
384
                        return null;
385
                }
386
                if ($name === null) {
387
                        return static::_returnSessionVars();
388
                }
389
                $result = Hash::get($_SESSION, $name);
390

    
391
                if (isset($result)) {
392
                        return $result;
393
                }
394
                return null;
395
        }
396

    
397
/**
398
 * Returns all session variables.
399
 *
400
 * @return mixed Full $_SESSION array, or false on error.
401
 */
402
        protected static function _returnSessionVars() {
403
                if (!empty($_SESSION)) {
404
                        return $_SESSION;
405
                }
406
                static::_setError(2, 'No Session vars set');
407
                return false;
408
        }
409

    
410
/**
411
 * Writes value to given session variable name.
412
 *
413
 * @param string|array $name Name of variable
414
 * @param string $value Value to write
415
 * @return bool True if the write was successful, false if the write failed
416
 */
417
        public static function write($name, $value = null) {
418
                if (empty($name) || !static::start()) {
419
                        return false;
420
                }
421

    
422
                $write = $name;
423
                if (!is_array($name)) {
424
                        $write = array($name => $value);
425
                }
426
                foreach ($write as $key => $val) {
427
                        static::_overwrite($_SESSION, Hash::insert($_SESSION, $key, $val));
428
                        if (Hash::get($_SESSION, $key) !== $val) {
429
                                return false;
430
                        }
431
                }
432
                return true;
433
        }
434

    
435
/**
436
 * Reads and deletes a variable from session.
437
 *
438
 * @param string $name The key to read and remove (or a path as sent to Hash.extract).
439
 * @return mixed The value of the session variable, null if session not available,
440
 *   session not started, or provided name not found in the session.
441
 */
442
        public static function consume($name) {
443
                if (empty($name)) {
444
                        return null;
445
                }
446
                $value = static::read($name);
447
                if ($value !== null) {
448
                        static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
449
                }
450
                return $value;
451
        }
452

    
453
/**
454
 * Helper method to destroy invalid sessions.
455
 *
456
 * @return void
457
 */
458
        public static function destroy() {
459
                if (!static::started()) {
460
                        static::_startSession();
461
                }
462

    
463
                if (static::started()) {
464
                        session_destroy();
465
                }
466

    
467
                $_SESSION = null;
468
                static::$id = null;
469
                static::$_cookieName = null;
470
        }
471

    
472
/**
473
 * Clears the session.
474
 *
475
 * Optionally also clears the session id and renews the session.
476
 *
477
 * @param bool $renew If the session should also be renewed. Defaults to true.
478
 * @return void
479
 */
480
        public static function clear($renew = true) {
481
                if (!$renew) {
482
                        $_SESSION = array();
483
                        return;
484
                }
485

    
486
                $_SESSION = null;
487
                static::$id = null;
488
                static::renew();
489
        }
490

    
491
/**
492
 * Helper method to initialize a session, based on CakePHP core settings.
493
 *
494
 * Sessions can be configured with a few shortcut names as well as have any number of ini settings declared.
495
 *
496
 * @return void
497
 * @throws CakeSessionException Throws exceptions when ini_set() fails.
498
 */
499
        protected static function _configureSession() {
500
                $sessionConfig = Configure::read('Session');
501

    
502
                if (isset($sessionConfig['defaults'])) {
503
                        $defaults = static::_defaultConfig($sessionConfig['defaults']);
504
                        if ($defaults) {
505
                                $sessionConfig = Hash::merge($defaults, $sessionConfig);
506
                        }
507
                }
508
                if (!isset($sessionConfig['ini']['session.cookie_secure']) && env('HTTPS')) {
509
                        $sessionConfig['ini']['session.cookie_secure'] = 1;
510
                }
511
                if (isset($sessionConfig['timeout']) && !isset($sessionConfig['cookieTimeout'])) {
512
                        $sessionConfig['cookieTimeout'] = $sessionConfig['timeout'];
513
                }
514
                if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
515
                        $sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
516
                }
517

    
518
                if (!isset($sessionConfig['ini']['session.name'])) {
519
                        $sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
520
                }
521
                static::$_cookieName = $sessionConfig['ini']['session.name'];
522

    
523
                if (!empty($sessionConfig['handler'])) {
524
                        $sessionConfig['ini']['session.save_handler'] = 'user';
525
                } elseif (!empty($sessionConfig['session.save_path']) && Configure::read('debug')) {
526
                        if (!is_dir($sessionConfig['session.save_path'])) {
527
                                mkdir($sessionConfig['session.save_path'], 0775, true);
528
                        }
529
                }
530

    
531
                if (!isset($sessionConfig['ini']['session.gc_maxlifetime'])) {
532
                        $sessionConfig['ini']['session.gc_maxlifetime'] = $sessionConfig['timeout'] * 60;
533
                }
534
                if (!isset($sessionConfig['ini']['session.cookie_httponly'])) {
535
                        $sessionConfig['ini']['session.cookie_httponly'] = 1;
536
                }
537

    
538
                if (empty($_SESSION)) {
539
                        if (!empty($sessionConfig['ini']) && is_array($sessionConfig['ini'])) {
540
                                foreach ($sessionConfig['ini'] as $setting => $value) {
541
                                        if (ini_set($setting, $value) === false) {
542
                                                throw new CakeSessionException(__d('cake_dev', 'Unable to configure the session, setting %s failed.', $setting));
543
                                        }
544
                                }
545
                        }
546
                }
547
                if (!empty($sessionConfig['handler']) && !isset($sessionConfig['handler']['engine'])) {
548
                        call_user_func_array('session_set_save_handler', $sessionConfig['handler']);
549
                }
550
                if (!empty($sessionConfig['handler']['engine'])) {
551
                        $handler = static::_getHandler($sessionConfig['handler']['engine']);
552
                        session_set_save_handler(
553
                                array($handler, 'open'),
554
                                array($handler, 'close'),
555
                                array($handler, 'read'),
556
                                array($handler, 'write'),
557
                                array($handler, 'destroy'),
558
                                array($handler, 'gc')
559
                        );
560
                }
561
                Configure::write('Session', $sessionConfig);
562
                static::$sessionTime = static::$time + ($sessionConfig['timeout'] * 60);
563
        }
564

    
565
/**
566
 * Get session cookie name.
567
 *
568
 * @return string
569
 */
570
        protected static function _cookieName() {
571
                if (static::$_cookieName !== null) {
572
                        return static::$_cookieName;
573
                }
574

    
575
                static::init();
576
                static::_configureSession();
577

    
578
                return static::$_cookieName = session_name();
579
        }
580

    
581
/**
582
 * Returns whether a session exists
583
 *
584
 * @return bool
585
 */
586
        protected static function _hasSession() {
587
                return static::started() || isset($_COOKIE[static::_cookieName()]);
588
        }
589

    
590
/**
591
 * Find the handler class and make sure it implements the correct interface.
592
 *
593
 * @param string $handler Handler name.
594
 * @return void
595
 * @throws CakeSessionException
596
 */
597
        protected static function _getHandler($handler) {
598
                list($plugin, $class) = pluginSplit($handler, true);
599
                App::uses($class, $plugin . 'Model/Datasource/Session');
600
                if (!class_exists($class)) {
601
                        throw new CakeSessionException(__d('cake_dev', 'Could not load %s to handle the session.', $class));
602
                }
603
                $handler = new $class();
604
                if ($handler instanceof CakeSessionHandlerInterface) {
605
                        return $handler;
606
                }
607
                throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
608
        }
609

    
610
/**
611
 * Get one of the prebaked default session configurations.
612
 *
613
 * @param string $name Config name.
614
 * @return bool|array
615
 */
616
        protected static function _defaultConfig($name) {
617
                $defaults = array(
618
                        'php' => array(
619
                                'cookie' => 'CAKEPHP',
620
                                'timeout' => 240,
621
                                'ini' => array(
622
                                        'session.use_trans_sid' => 0,
623
                                        'session.cookie_path' => static::$path
624
                                )
625
                        ),
626
                        'cake' => array(
627
                                'cookie' => 'CAKEPHP',
628
                                'timeout' => 240,
629
                                'ini' => array(
630
                                        'session.use_trans_sid' => 0,
631
                                        'url_rewriter.tags' => '',
632
                                        'session.serialize_handler' => 'php',
633
                                        'session.use_cookies' => 1,
634
                                        'session.cookie_path' => static::$path,
635
                                        'session.save_path' => TMP . 'sessions',
636
                                        'session.save_handler' => 'files'
637
                                )
638
                        ),
639
                        'cache' => array(
640
                                'cookie' => 'CAKEPHP',
641
                                'timeout' => 240,
642
                                'ini' => array(
643
                                        'session.use_trans_sid' => 0,
644
                                        'url_rewriter.tags' => '',
645
                                        'session.use_cookies' => 1,
646
                                        'session.cookie_path' => static::$path,
647
                                        'session.save_handler' => 'user',
648
                                ),
649
                                'handler' => array(
650
                                        'engine' => 'CacheSession',
651
                                        'config' => 'default'
652
                                )
653
                        ),
654
                        'database' => array(
655
                                'cookie' => 'CAKEPHP',
656
                                'timeout' => 240,
657
                                'ini' => array(
658
                                        'session.use_trans_sid' => 0,
659
                                        'url_rewriter.tags' => '',
660
                                        'session.use_cookies' => 1,
661
                                        'session.cookie_path' => static::$path,
662
                                        'session.save_handler' => 'user',
663
                                        'session.serialize_handler' => 'php',
664
                                ),
665
                                'handler' => array(
666
                                        'engine' => 'DatabaseSession',
667
                                        'model' => 'Session'
668
                                )
669
                        )
670
                );
671
                if (isset($defaults[$name])) {
672
                        return $defaults[$name];
673
                }
674
                return false;
675
        }
676

    
677
/**
678
 * Helper method to start a session
679
 *
680
 * @return bool Success
681
 */
682
        protected static function _startSession() {
683
                static::init();
684
                session_write_close();
685
                static::_configureSession();
686

    
687
                if (headers_sent()) {
688
                        if (empty($_SESSION)) {
689
                                $_SESSION = array();
690
                        }
691
                } else {
692
                        // For IE<=8
693
                        session_cache_limiter("must-revalidate");
694
                        session_start();
695
                }
696
                return true;
697
        }
698

    
699
/**
700
 * Helper method to create a new session.
701
 *
702
 * @return void
703
 */
704
        protected static function _checkValid() {
705
                $config = static::read('Config');
706
                if ($config) {
707
                        $sessionConfig = Configure::read('Session');
708

    
709
                        if (static::valid()) {
710
                                static::write('Config.time', static::$sessionTime);
711
                                if (isset($sessionConfig['autoRegenerate']) && $sessionConfig['autoRegenerate'] === true) {
712
                                        $check = $config['countdown'];
713
                                        $check -= 1;
714
                                        static::write('Config.countdown', $check);
715

    
716
                                        if ($check < 1) {
717
                                                static::renew();
718
                                                static::write('Config.countdown', static::$requestCountdown);
719
                                        }
720
                                }
721
                        } else {
722
                                $_SESSION = array();
723
                                static::destroy();
724
                                static::_setError(1, 'Session Highjacking Attempted !!!');
725
                                static::_startSession();
726
                                static::_writeConfig();
727
                        }
728
                } else {
729
                        static::_writeConfig();
730
                }
731
        }
732

    
733
/**
734
 * Writes configuration variables to the session
735
 *
736
 * @return void
737
 */
738
        protected static function _writeConfig() {
739
                static::write('Config.userAgent', static::$_userAgent);
740
                static::write('Config.time', static::$sessionTime);
741
                static::write('Config.countdown', static::$requestCountdown);
742
        }
743

    
744
/**
745
 * Restarts this session.
746
 *
747
 * @return void
748
 */
749
        public static function renew() {
750
                if (session_id() === '') {
751
                        return;
752
                }
753
                if (isset($_COOKIE[session_name()])) {
754
                        setcookie(Configure::read('Session.cookie'), '', time() - 42000, static::$path);
755
                }
756
                session_regenerate_id(true);
757
        }
758

    
759
/**
760
 * Helper method to set an internal error message.
761
 *
762
 * @param int $errorNumber Number of the error
763
 * @param string $errorMessage Description of the error
764
 * @return void
765
 */
766
        protected static function _setError($errorNumber, $errorMessage) {
767
                if (static::$error === false) {
768
                        static::$error = array();
769
                }
770
                static::$error[$errorNumber] = $errorMessage;
771
                static::$lastError = $errorNumber;
772
        }
773

    
774
}