/** * Logs this member out. */ public function logOut() { $this->extend('beforeMemberLoggedOut'); Session::clear("loggedInAs"); if (Member::config()->login_marker_cookie) { Cookie::set(Member::config()->login_marker_cookie, null, 0); } Session::destroy(); $this->extend('memberLoggedOut'); // Clears any potential previous hashes for this member RememberLoginHash::clear($this, Cookie::get('alc_device')); Cookie::set('alc_enc', null); // // Clear the Remember Me cookie Cookie::force_expiry('alc_enc'); Cookie::set('alc_device', null); Cookie::force_expiry('alc_device'); // Switch back to live in order to avoid infinite loops when // redirecting to the login screen (if this login screen is versioned) Session::clear('readingMode'); $this->write(); // Audit logging hook $this->extend('memberLoggedOut'); }
public function testRememberMeMultipleDevices() { $m1 = $this->objFromFixture('SilverStripe\\Security\\Member', 'noexpiry'); // First device $m1->login(true); Cookie::set('alc_device', null); // Second device $m1->login(true); // Hash of first device $firstHash = RememberLoginHash::get()->filter('MemberID', $m1->ID)->First(); $this->assertNotNull($firstHash); // Hash of second device $secondHash = RememberLoginHash::get()->filter('MemberID', $m1->ID)->Last(); $this->assertNotNull($secondHash); // DeviceIDs are different $this->assertNotEquals($firstHash->DeviceID, $secondHash->DeviceID); // re-generates the hashes so we can get the tokens $firstHash->Hash = $firstHash->getNewHash($m1); $firstToken = $firstHash->getToken(); $firstHash->write(); $secondHash->Hash = $secondHash->getNewHash($m1); $secondToken = $secondHash->getToken(); $secondHash->write(); // Accessing the login page should show the user's name straight away $response = $this->get('Security/login', $this->session(), null, array('alc_enc' => $m1->ID . ':' . $firstToken, 'alc_device' => $firstHash->DeviceID)); $message = _t('Member.LOGGEDINAS', "You're logged in as {name}.", array('name' => $m1->FirstName)); $this->assertContains($message, $response->getBody()); $this->session()->inst_set('loggedInAs', null); // Accessing the login page from the second device $response = $this->get('Security/login', $this->session(), null, array('alc_enc' => $m1->ID . ':' . $secondToken, 'alc_device' => $secondHash->DeviceID)); $this->assertContains($message, $response->getBody()); // Logging out from the second device - only one device being logged out RememberLoginHash::config()->update('logout_across_devices', false); $response = $this->get('Security/logout', $this->session(), null, array('alc_enc' => $m1->ID . ':' . $secondToken, 'alc_device' => $secondHash->DeviceID)); $this->assertEquals(RememberLoginHash::get()->filter(array('MemberID' => $m1->ID, 'DeviceID' => $firstHash->DeviceID))->Count(), 1); // Logging out from any device when all login hashes should be removed RememberLoginHash::config()->update('logout_across_devices', true); $m1->login(true); $response = $this->get('Security/logout', $this->session()); $this->assertEquals(RememberLoginHash::get()->filter('MemberID', $m1->ID)->Count(), 0); }
public function inst_start($sid = null) { $path = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_path'); if (!$path) { $path = Director::baseURL(); } $domain = Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_domain'); $secure = Director::is_https() && Config::inst()->get('SilverStripe\\Control\\Session', 'cookie_secure'); $session_path = Config::inst()->get('SilverStripe\\Control\\Session', 'session_store_path'); $timeout = Config::inst()->get('SilverStripe\\Control\\Session', 'timeout'); if (!session_id() && !headers_sent()) { if ($domain) { session_set_cookie_params($timeout, $path, $domain, $secure, true); } else { session_set_cookie_params($timeout, $path, null, $secure, true); } // Allow storing the session in a non standard location if ($session_path) { session_save_path($session_path); } // If we want a secure cookie for HTTPS, use a seperate session name. This lets us have a // seperate (less secure) session for non-HTTPS requests if ($secure) { session_name('SECSESSID'); } if ($sid) { session_id($sid); } session_start(); $this->data = isset($_SESSION) ? $_SESSION : array(); } // Modify the timeout behaviour so it's the *inactive* time before the session expires. // By default it's the total session lifetime if ($timeout && !headers_sent()) { Cookie::set(session_name(), session_id(), $timeout / 86400, $path, $domain ? $domain : null, $secure, true); } }
/** * Choose the stage the site is currently on. * * If $_GET['stage'] is set, then it will use that stage, and store it in * the session. * * if $_GET['archiveDate'] is set, it will use that date, and store it in * the session. * * If neither of these are set, it checks the session, otherwise the stage * is set to 'Live'. */ public static function choose_site_stage() { // Check any pre-existing session mode $preexistingMode = Session::get('readingMode'); // Determine the reading mode if (isset($_GET['stage'])) { $stage = ucfirst(strtolower($_GET['stage'])); if (!in_array($stage, array(static::DRAFT, static::LIVE))) { $stage = static::LIVE; } $mode = 'Stage.' . $stage; } elseif (isset($_GET['archiveDate']) && strtotime($_GET['archiveDate'])) { $mode = 'Archive.' . $_GET['archiveDate']; } elseif ($preexistingMode) { $mode = $preexistingMode; } else { $mode = static::DEFAULT_MODE; } // Save reading mode Versioned::set_reading_mode($mode); // Try not to store the mode in the session if not needed if ($preexistingMode && $preexistingMode !== $mode || !$preexistingMode && $mode !== static::DEFAULT_MODE) { Session::set('readingMode', $mode); } if (!headers_sent() && !Director::is_cli()) { if (Versioned::get_stage() == 'Live') { // clear the cookie if it's set if (Cookie::get('bypassStaticCache')) { Cookie::force_expiry('bypassStaticCache', null, null, false, true); } } else { // set the cookie if it's cleared if (!Cookie::get('bypassStaticCache')) { Cookie::set('bypassStaticCache', '1', 0, null, null, false, true); } } } }
/** * Check we can remove cookies and we can access their original values */ public function testForceExpiry() { //load an existing cookie $cookieJar = new CookieJar(array('cookieExisting' => 'i woz here')); Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend'); //make sure it's available $this->assertEquals('i woz here', Cookie::get('cookieExisting')); //remove the cookie Cookie::force_expiry('cookieExisting'); //check it's gone $this->assertEmpty(Cookie::get('cookieExisting')); //check we can get it's original value $this->assertEquals('i woz here', Cookie::get('cookieExisting', false)); //check we can add a new cookie and remove it and it doesn't leave any phantom values Cookie::set('newCookie', 'i am new'); //check it's set by not recieved $this->assertEquals('i am new', Cookie::get('newCookie')); $this->assertEmpty(Cookie::get('newCookie', false)); //remove it Cookie::force_expiry('newCookie'); //check it's neither set nor reveived $this->assertEmpty(Cookie::get('newCookie')); $this->assertEmpty(Cookie::get('newCookie', false)); }
/** * Set an alternative database in a browser cookie, * with the cookie lifetime set to the browser session. * This is useful for integration testing on temporary databases. * * There is a strict naming convention for temporary databases to avoid abuse: * <prefix> (default: 'ss_') + tmpdb + <7 digits> * As an additional security measure, temporary databases will * be ignored in "live" mode. * * Note that the database will be set on the next request. * Set it to null to revert to the main database. * @param string $name */ public static function set_alternative_database_name($name = null) { // Skip if CLI if (Director::is_cli()) { return; } if ($name) { if (!self::valid_alternative_database_name($name)) { throw new InvalidArgumentException(sprintf('Invalid alternative database name: "%s"', $name)); } $key = Config::inst()->get('SilverStripe\\Security\\Security', 'token'); if (!$key) { throw new LogicException('"Security.token" not found, run "sake dev/generatesecuretoken"'); } if (!function_exists('mcrypt_encrypt')) { throw new LogicException('DB::set_alternative_database_name() requires the mcrypt PHP extension'); } $key = md5($key); // Ensure key is correct length for chosen cypher $ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB); $iv = mcrypt_create_iv($ivSize); $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $name, MCRYPT_MODE_CFB, $iv); // Set to browser session lifetime, and restricted to HTTP access only Cookie::set("alternativeDatabaseName", base64_encode($encrypted), 0, null, null, false, true); Cookie::set("alternativeDatabaseNameIv", base64_encode($iv), 0, null, null, false, true); } else { Cookie::force_expiry("alternativeDatabaseName", null, null, false, true); Cookie::force_expiry("alternativeDatabaseNameIv", null, null, false, true); } }