1<?php 2 3use Elgg\Config; 4use Elgg\Database; 5use Elgg\Http\DatabaseSessionHandler; 6use Symfony\Component\HttpFoundation\Session\Session; 7use Symfony\Component\HttpFoundation\Session\SessionInterface; 8use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; 9use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; 10 11/** 12 * Elgg Session Management 13 * 14 * Reserved keys: last_forward_from, msg, sticky_forms, user, guid, id, code, name, username 15 * 16 * @see elgg_get_session() 17 */ 18class ElggSession { 19 20 /** 21 * @var SessionInterface 22 */ 23 protected $storage; 24 25 /** 26 * @var \ElggUser|null 27 */ 28 protected $logged_in_user; 29 30 /** 31 * @var bool 32 */ 33 protected $ignore_access = false; 34 35 /** 36 * @var bool 37 */ 38 protected $show_disabled_entities = false; 39 40 /** 41 * Constructor 42 * 43 * @param SessionInterface $storage The underlying Session implementation 44 */ 45 public function __construct(SessionInterface $storage) { 46 $this->storage = $storage; 47 } 48 49 /** 50 * Start the session 51 * 52 * @return boolean 53 * @throws RuntimeException If session fails to start. 54 * @since 1.9 55 */ 56 public function start() { 57 58 if ($this->storage->getId()) { 59 return true; 60 } 61 62 $result = $this->storage->start(); 63 $this->generateSessionToken(); 64 return $result; 65 } 66 67 /** 68 * Migrates the session to a new session id while maintaining session attributes 69 * 70 * @param boolean $destroy Whether to delete the session or let gc handle clean up 71 * @return boolean 72 * @since 1.9 73 */ 74 public function migrate($destroy = false) { 75 return $this->storage->migrate($destroy); 76 } 77 78 /** 79 * Invalidates the session 80 * 81 * Deletes session data and session persistence. Starts a new session. 82 * 83 * @return boolean 84 * @since 1.9 85 */ 86 public function invalidate() { 87 $this->storage->clear(); 88 $this->logged_in_user = null; 89 $result = $this->migrate(true); 90 $this->generateSessionToken(); 91 _elgg_services()->sessionCache->clear(); 92 return $result; 93 } 94 95 /** 96 * Save the session data and closes the session 97 * 98 * @return void 99 * @since 3.0 100 */ 101 public function save() { 102 $this->storage->save(); 103 } 104 105 /** 106 * Has the session been started 107 * 108 * @return boolean 109 * @since 1.9 110 */ 111 public function isStarted() { 112 return $this->storage->isStarted(); 113 } 114 115 /** 116 * Get the session ID 117 * 118 * @return string 119 * @since 1.9 120 */ 121 public function getID() { 122 return $this->storage->getId(); 123 } 124 125 /** 126 * Set the session ID 127 * 128 * @param string $id Session ID 129 * @return void 130 * @since 1.9 131 */ 132 public function setId($id) { 133 $this->storage->setId($id); 134 } 135 136 /** 137 * Get the session name 138 * 139 * @return string 140 * @since 1.9 141 */ 142 public function getName() { 143 return $this->storage->getName(); 144 } 145 146 /** 147 * Set the session name 148 * 149 * @param string $name Session name 150 * @return void 151 * @since 1.9 152 */ 153 public function setName($name) { 154 $this->storage->setName($name); 155 } 156 157 /** 158 * Get an attribute of the session 159 * 160 * @param string $name Name of the attribute to get 161 * @param mixed $default Value to return if attribute is not set (default is null) 162 * @return mixed 163 */ 164 public function get($name, $default = null) { 165 return $this->storage->get($name, $default); 166 } 167 168 /** 169 * Set an attribute 170 * 171 * @param string $name Name of the attribute to set 172 * @param mixed $value Value to be set 173 * @return void 174 */ 175 public function set($name, $value) { 176 $this->storage->set($name, $value); 177 } 178 179 /** 180 * Remove an attribute 181 * 182 * @param string $name The name of the attribute to remove 183 * @return mixed The removed attribute 184 * @since 1.9 185 */ 186 public function remove($name) { 187 return $this->storage->remove($name); 188 } 189 190 /** 191 * Has the attribute been defined 192 * 193 * @param string $name Name of the attribute 194 * @return bool 195 * @since 1.9 196 */ 197 public function has($name) { 198 return $this->storage->has($name); 199 } 200 201 /** 202 * Sets the logged in user 203 * 204 * @param \ElggUser $user The user who is logged in 205 * @return void 206 * @since 1.9 207 */ 208 public function setLoggedInUser(\ElggUser $user) { 209 $current_user = $this->getLoggedInUser(); 210 if ($current_user != $user) { 211 $this->set('guid', $user->guid); 212 $this->logged_in_user = $user; 213 _elgg_services()->sessionCache->clear(); 214 _elgg_services()->translator->setCurrentLanguage($user->language); 215 } 216 } 217 218 /** 219 * Gets the logged in user 220 * 221 * @return \ElggUser|null 222 * @since 1.9 223 */ 224 public function getLoggedInUser() { 225 return $this->logged_in_user; 226 } 227 228 /** 229 * Return the current logged in user by guid. 230 * 231 * @see elgg_get_logged_in_user_entity() 232 * @return int 233 */ 234 public function getLoggedInUserGuid() { 235 $user = $this->getLoggedInUser(); 236 return $user ? $user->guid : 0; 237 } 238 239 /** 240 * Returns whether or not the viewer is currently logged in and an admin user. 241 * 242 * @return bool 243 */ 244 public function isAdminLoggedIn() { 245 $user = $this->getLoggedInUser(); 246 247 return $user && $user->isAdmin(); 248 } 249 250 /** 251 * Returns whether or not the user is currently logged in 252 * 253 * @return bool 254 */ 255 public function isLoggedIn() { 256 return (bool) $this->getLoggedInUser(); 257 } 258 259 /** 260 * Remove the logged in user 261 * 262 * @return void 263 * @since 1.9 264 */ 265 public function removeLoggedInUser() { 266 $this->logged_in_user = null; 267 $this->remove('guid'); 268 _elgg_services()->sessionCache->clear(); 269 } 270 271 /** 272 * Get current ignore access setting. 273 * 274 * @return bool 275 */ 276 public function getIgnoreAccess() { 277 return $this->ignore_access; 278 } 279 280 /** 281 * Set ignore access. 282 * 283 * @param bool $ignore Ignore access 284 * 285 * @return bool Previous setting 286 */ 287 public function setIgnoreAccess($ignore = true) { 288 $prev = $this->ignore_access; 289 $this->ignore_access = $ignore; 290 291 return $prev; 292 } 293 294 /** 295 * Are disabled entities shown? 296 * 297 * @return bool 298 */ 299 public function getDisabledEntityVisibility() { 300 global $ENTITY_SHOW_HIDDEN_OVERRIDE; 301 if (isset($ENTITY_SHOW_HIDDEN_OVERRIDE)) { 302 return $ENTITY_SHOW_HIDDEN_OVERRIDE; 303 } 304 305 return $this->show_disabled_entities; 306 } 307 308 /** 309 * Include disabled entities in queries 310 * 311 * @param bool $show Visibility status 312 * 313 * @return bool Previous setting 314 */ 315 public function setDisabledEntityVisibility($show = true) { 316 global $ENTITY_SHOW_HIDDEN_OVERRIDE; 317 $ENTITY_SHOW_HIDDEN_OVERRIDE = $show; 318 319 $prev = $this->show_disabled_entities; 320 $this->show_disabled_entities = $show; 321 322 return $prev; 323 } 324 325 /** 326 * Adds a token to the session 327 * 328 * This is used in creation of CSRF token, and is passed to the client to allow validating tokens 329 * later, even if the PHP session was destroyed. 330 * 331 * @return void 332 */ 333 protected function generateSessionToken() { 334 // Generate a simple token that we store server side 335 if (!$this->has('__elgg_session')) { 336 $this->set('__elgg_session', _elgg_services()->crypto->getRandomString(22)); 337 } 338 } 339 340 /** 341 * Get an isolated ElggSession that does not persist between requests 342 * 343 * @return self 344 * 345 * @internal 346 */ 347 public static function getMock() { 348 $storage = new MockArraySessionStorage(); 349 $session = new Session($storage); 350 return new self($session); 351 } 352 353 /** 354 * Create a session stored in the DB. 355 * 356 * @param Config $config Config 357 * @param Database $db Database 358 * 359 * @return ElggSession 360 * 361 * @internal 362 */ 363 public static function fromDatabase(Config $config, Database $db) { 364 $params = $config->getCookieConfig()['session']; 365 $options = [ 366 // session.cache_limiter is unfortunately set to "" by the NativeSessionStorage 367 // constructor, so we must capture and inject it directly. 368 'cache_limiter' => session_cache_limiter(), 369 370 'name' => $params['name'], 371 'cookie_path' => $params['path'], 372 'cookie_domain' => $params['domain'], 373 'cookie_secure' => $params['secure'], 374 'cookie_httponly' => $params['httponly'], 375 'cookie_lifetime' => $params['lifetime'], 376 ]; 377 378 $handler = new DatabaseSessionHandler($db); 379 $storage = new NativeSessionStorage($options, $handler); 380 $session = new Session($storage); 381 return new self($session); 382 } 383 384 /** 385 * Create a session stored in files 386 * 387 * @param Config $config Config 388 * 389 * @return ElggSession 390 * 391 * @internal 392 */ 393 public static function fromFiles(Config $config) { 394 $params = $config->getCookieConfig()['session']; 395 $options = [ 396 // session.cache_limiter is unfortunately set to "" by the NativeSessionStorage 397 // constructor, so we must capture and inject it directly. 398 'cache_limiter' => session_cache_limiter(), 399 400 'name' => $params['name'], 401 'cookie_path' => $params['path'], 402 'cookie_domain' => $params['domain'], 403 'cookie_secure' => $params['secure'], 404 'cookie_httponly' => $params['httponly'], 405 'cookie_lifetime' => $params['lifetime'], 406 ]; 407 408 $storage = new NativeSessionStorage($options); 409 $session = new Session($storage); 410 return new self($session); 411 } 412} 413