1: <?php
2: /*
3: You may not change or alter any portion of this comment or credits
4: of supporting developers from this source code or any supporting source code
5: which is considered copyrighted (c) material of the original comment or credit authors.
6:
7: This program is distributed in the hope that it will be useful,
8: but WITHOUT ANY WARRANTY; without even the implied warranty of
9: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10: */
11:
12: namespace Xoops\Core\Session;
13:
14: use Xoops\Core\AttributeInterface;
15: use Xoops\Core\HttpRequest;
16:
17: /**
18: * Session management
19: *
20: * Credits due to Robert Hafner's article "How to Create Bulletproof Sessions"
21: * see: http://blog.teamtreehouse.com/how-to-create-bulletproof-sessions
22: *
23: * @category Xoops\Core\Session
24: * @package Manager
25: * @author Richard Griffith <richard@geekwright.com>
26: * @copyright 2015 XOOPS Project (http://xoops.org)
27: * @license GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
28: * @link http://xoops.org
29: */
30: class Manager implements AttributeInterface
31: {
32: /**
33: * @var \Xoops
34: */
35: protected $xoops = null;
36:
37: /**
38: * @var \Xoops\Core\HttpRequest
39: */
40: protected $httpRequest = null;
41:
42: /**
43: * @var Fingerprint fingerprint object
44: */
45: protected $fingerprint = null;
46:
47: /**
48: * @var SessionUser session user object
49: */
50: protected $sessionUser = null;
51:
52: /**
53: * establish access to other classes we will use
54: */
55: public function __construct()
56: {
57: $this->xoops = \Xoops::getInstance();
58: $this->httpRequest = HttpRequest::getInstance();
59: $this->sessionUser = new SessionUser($this);
60: $this->fingerprint = new Fingerprint;
61: }
62:
63: /**
64: * Configure and start the session
65: *
66: * @return void
67: */
68: public function sessionStart()
69: {
70: /**
71: * Revisit this once basics are working
72: *
73: * grab session_id from https login form
74: *
75: * if ($xoops->getConfig('use_ssl')
76: * && isset($_POST[$xoops->getConfig('sslpost_name')])
77: * && $_POST[$xoops->getConfig('sslpost_name')] != ''
78: * ) {
79: * session_id($_POST[$xoops->getConfig('sslpost_name')]);
80: * } else { set session_name...}
81: */
82:
83: $name = $this->xoops->getConfig('session_name');
84: $name = (empty($name)) ? 'xoops_session' : $name;
85: $expire = (int)($this->xoops->getConfig('session_expire'));
86: $expire = ($expire>0) ? $expire : 300;
87:
88: $path = \XoopsBaseConfig::get('cookie-path');
89: $domain = \XoopsBaseConfig::get('cookie-domain');
90: $secure = $this->httpRequest->is('ssl');
91: session_name($name);
92: session_cache_expire($expire);
93:
94: session_set_cookie_params(0, $path, $domain, $secure, true);
95:
96: $sessionHandler = new Handler;
97: session_set_save_handler($sessionHandler);
98:
99: //session_register_shutdown();
100: register_shutdown_function(array($this, 'sessionShutdown'));
101:
102: session_start();
103:
104: // if session is empty, make sure it isn't using a passed in id
105: if (empty($_SESSION)) {
106: $this->regenerateSession();
107: }
108:
109: // Make sure the session hasn't expired, and destroy it if it has
110: if (!$this->validateSession()) {
111: $this->clearSession();
112: return;
113: }
114:
115: // Check to see if the session shows sign of hijacking attempt
116: if (!$this->fingerprint->checkSessionPrint($this)) {
117: $this->regenerateSession(); // session data already cleared, just needs new id
118: return;
119: }
120:
121: // establish valid user data in session, possibly clearing or adding from
122: // RememberMe mechanism as needed
123: $this->sessionUser->establish();
124:
125: // Give a 5% chance of the session id changing on any authenticated request
126: //if ($this->has('xoopsUserId') && (rand(1, 100) <= 5)) {
127: if ((rand(1, 100) <= 5)) {
128: $this->expireSession();
129: }
130: }
131:
132: /**
133: * Clear the current session and reset fingerprint
134: *
135: * @return void
136: */
137: public function clearSession()
138: {
139: $this->clear();
140: $this->fingerprint->checkSessionPrint($this);
141: $this->regenerateSession();
142: }
143:
144: /**
145: * Expire the current session and replace with a fresh one.
146: *
147: * @return void
148: */
149: public function expireSession()
150: {
151: // If this session is obsolete it means there already is a new id
152: if ($this->has('SESSION_MANAGER_OBSOLETE')) {
153: return;
154: }
155:
156: // Set current session to expire in 10 seconds
157: $this->set('SESSION_MANAGER_OBSOLETE', true);
158: $this->set('SESSION_MANAGER_EXPIRES', time() + 10);
159:
160: // Grab current session ID and close it
161: $sessionId = session_id();
162: session_write_close();
163:
164: // reopen the old session
165: session_id($sessionId);
166: session_start();
167:
168: // Create new session without destroying the old one
169: session_regenerate_id(false);
170:
171: // Now we unset the obsolete and expiration values since we ant to keep this one
172: $this->remove('SESSION_MANAGER_OBSOLETE');
173: $this->remove('SESSION_MANAGER_EXPIRES');
174: }
175:
176: /**
177: * Generate a new id and delete the old session.
178: *
179: * This should be called whenever permission levels for a user change.
180: *
181: * @return void
182: */
183: public function regenerateSession()
184: {
185: session_regenerate_id(true);
186: }
187:
188: /**
189: * Validate that the session has not expired.
190: *
191: * @return boolean true is session is valid and not expired, otherwise false
192: */
193: protected function validateSession()
194: {
195: // invalid to have obsolete and not expires
196: if ($this->has('SESSION_MANAGER_OBSOLETE') && !$this->has('SESSION_MANAGER_EXPIRES')) {
197: return false;
198: }
199:
200: // if we don't have the expires key, use a future value for test
201: if ($this->get('SESSION_MANAGER_EXPIRES', time()+10) < time()) {
202: return false;
203: }
204:
205: return true;
206: }
207:
208: /**
209: * Get the user object used by this session.
210: *
211: * @return SessionUser
212: */
213: public function user()
214: {
215: return $this->sessionUser;
216: }
217:
218: /**
219: * shutdown function
220: */
221: public function sessionShutdown()
222: {
223: \Xoops::getInstance()->events()->triggerEvent('core.session.shutdown');
224: session_write_close();
225: }
226:
227: // access session variables as attribute object
228:
229: /**
230: * Retrieve a session variable value.
231: *
232: * @param string $name Name of an session variable
233: * @param mixed $default A default value returned if the requested
234: * named session variable is not set.
235: *
236: * @return mixed The value of the session variable, or $default if not set.
237: */
238: public function get($name, $default = null)
239: {
240: return (isset($_SESSION[$name])) ? $_SESSION[$name] : $default;
241: }
242:
243: /**
244: * Set an attribute value.
245: *
246: * @param string $name Name of the attribute option
247: * @param mixed $value Value of the attribute option
248: *
249: * @return void
250: */
251: public function set($name, $value)
252: {
253: $_SESSION[$name] = $value;
254: }
255:
256: /**
257: * Determine if an attribute exists.
258: *
259: * @param string $name An attribute name.
260: *
261: * @return boolean TRUE if the given attribute exists, otherwise FALSE.
262: */
263: public function has($name)
264: {
265: return isset($_SESSION[$name]);
266: }
267:
268: /**
269: * Remove an attribute.
270: *
271: * @param string $name An attribute name.
272: *
273: * @return mixed An attribute value, if the named attribute existed and
274: * has been removed, otherwise NULL.
275: */
276: public function remove($name)
277: {
278: $value = (isset($_SESSION[$name])) ? $_SESSION[$name] : null;
279: unset($_SESSION[$name]);
280:
281: return $value;
282: }
283:
284: /**
285: * Remove all attributes.
286: *
287: * @return array old values
288: */
289: public function clear()
290: {
291: $oldValues = $_SESSION;
292: $_SESSION = array();
293: return $oldValues;
294: }
295: }
296: