/**
  * Require basic authentication.  Will request a username and password if none is given.
  *
  * Used by {@link Controller::init()}.
  *
  * @throws HTTPResponse_Exception
  *
  * @param string $realm
  * @param string|array $permissionCode Optional
  * @param boolean $tryUsingSessionLogin If true, then the method with authenticate against the
  *  session log-in if those credentials are disabled.
  * @return Member|bool $member
  */
 public static function requireLogin($realm, $permissionCode = null, $tryUsingSessionLogin = true)
 {
     $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test();
     if (!Security::database_is_ready() || Director::is_cli() && !$isRunningTests) {
         return true;
     }
     /*
      * Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache
      * Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or
      * REDIRECT_HTTP_AUTHORIZATION
      *
      * The follow rewrite rule must be in the sites .htaccess file to enable this workaround
      * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
      */
     $authHeader = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : null);
     $matches = array();
     if ($authHeader && preg_match('/Basic\\s+(.*)$/i', $authHeader, $matches)) {
         list($name, $password) = explode(':', base64_decode($matches[1]));
         $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
         $_SERVER['PHP_AUTH_PW'] = strip_tags($password);
     }
     $member = null;
     if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
         $member = MemberAuthenticator::authenticate(array('Email' => $_SERVER['PHP_AUTH_USER'], 'Password' => $_SERVER['PHP_AUTH_PW']), null);
     }
     if (!$member && $tryUsingSessionLogin) {
         $member = Member::currentUser();
     }
     // If we've failed the authentication mechanism, then show the login form
     if (!$member) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTREC', "That username / password isn't recognised"));
         } else {
             $response->setBody(_t('BasicAuth.ENTERINFO', "Please enter a username and password."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     if ($permissionCode && !Permission::checkMember($member->ID, $permissionCode)) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTADMIN', "That user is not an administrator."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     return $member;
 }
 protected function init()
 {
     parent::init();
     // We allow access to this controller regardless of live-status or ADMIN permission only
     // if on CLI or with the database not ready. The latter makes it less errorprone to do an
     // initial schema build without requiring a default-admin login.
     // Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
     $isRunningTests = class_exists('SapphireTest', false) && SapphireTest::is_running_test();
     $canAccess = Director::isDev() || !Security::database_is_ready() || Director::is_cli() && !$isRunningTests || Permission::check("ADMIN");
     if (!$canAccess) {
         return Security::permissionFailure($this, "This page is secured and you need administrator rights to access it. " . "Enter your credentials below and we will send you right along.");
     }
 }
Ejemplo n.º 3
0
    require_once 'core/Core.php';
    // Connect to database
    global $databaseConfig;
    if ($databaseConfig) {
        DB::connect($databaseConfig);
    }
    // Check if a token is requesting a redirect
    if (!$reloadToken) {
        return;
    }
    // Otherwise, we start up the session if needed
    if (!isset($_SESSION) && Session::request_contains_session_id()) {
        Session::start();
    }
    // Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
    if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
        return $reloadToken->reloadWithToken();
    }
    // Fail and redirect the user to the login page
    $loginPage = Director::absoluteURL(Security::config()->login_url);
    $loginPage .= "?BackURL=" . urlencode($_SERVER['REQUEST_URI']);
    header('location: ' . $loginPage, true, 302);
    die;
})->thenIfErrored(function () use($reloadToken) {
    if ($reloadToken) {
        $reloadToken->reloadWithToken();
    }
})->execute();
global $databaseConfig;
// Redirect to the installer if no database is selected
if (!isset($databaseConfig) || !isset($databaseConfig['database']) || !$databaseConfig['database']) {
 public function setUp()
 {
     //nest config and injector for each test so they are effectively sandboxed per test
     Config::nest();
     Injector::nest();
     $this->originalReadingMode = Versioned::get_reading_mode();
     // We cannot run the tests on this abstract class.
     if (get_class($this) == "SapphireTest") {
         $this->markTestSkipped(sprintf('Skipping %s ', get_class($this)));
         return;
     }
     // Mark test as being run
     $this->originalIsRunningTest = self::$is_running_test;
     self::$is_running_test = true;
     // i18n needs to be set to the defaults or tests fail
     i18n::set_locale(Config::inst()->get('i18n', 'default_locale'));
     i18n::config()->date_format = null;
     i18n::config()->time_format = null;
     // Set default timezone consistently to avoid NZ-specific dependencies
     date_default_timezone_set('UTC');
     // Remove password validation
     $this->originalMemberPasswordValidator = Member::password_validator();
     $this->originalRequirements = Requirements::backend();
     Member::set_password_validator(null);
     Config::inst()->update('Cookie', 'report_errors', false);
     if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) {
         RootURLController::reset();
     }
     if (class_exists('Translatable')) {
         Translatable::reset();
     }
     Versioned::reset();
     DataObject::reset();
     if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) {
         SiteTree::reset();
     }
     Hierarchy::reset();
     if (Controller::has_curr()) {
         Controller::curr()->setSession(Injector::inst()->create('Session', array()));
     }
     Security::$database_is_ready = null;
     // Add controller-name auto-routing
     Config::inst()->update('Director', 'rules', array('$Controller//$Action/$ID/$OtherID' => '*'));
     $fixtureFile = static::get_fixture_file();
     $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
     // Todo: this could be a special test model
     $this->model = DataModel::inst();
     // Set up fixture
     if ($fixtureFile || $this->usesDatabase) {
         if (!self::using_temp_db()) {
             self::create_temp_db();
         }
         singleton('SilverStripe\\ORM\\DataObject')->flushCache();
         self::empty_temp_db();
         foreach ($this->requireDefaultRecordsFrom as $className) {
             $instance = singleton($className);
             if (method_exists($instance, 'requireDefaultRecords')) {
                 $instance->requireDefaultRecords();
             }
             if (method_exists($instance, 'augmentDefaultRecords')) {
                 $instance->augmentDefaultRecords();
             }
         }
         if ($fixtureFile) {
             $pathForClass = $this->getCurrentAbsolutePath();
             $fixtureFiles = is_array($fixtureFile) ? $fixtureFile : array($fixtureFile);
             $i = 0;
             foreach ($fixtureFiles as $fixtureFilePath) {
                 // Support fixture paths relative to the test class, rather than relative to webroot
                 // String checking is faster than file_exists() calls.
                 $isRelativeToFile = strpos('/', $fixtureFilePath) === false || preg_match('/^\\.\\./', $fixtureFilePath);
                 if ($isRelativeToFile) {
                     $resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath);
                     if ($resolvedPath) {
                         $fixtureFilePath = $resolvedPath;
                     }
                 }
                 $fixture = Injector::inst()->create('YamlFixture', $fixtureFilePath);
                 $fixture->writeInto($this->getFixtureFactory());
                 $this->fixtures[] = $fixture;
                 // backwards compatibility: Load first fixture into $this->fixture
                 if ($i == 0) {
                     $this->fixture = $fixture;
                 }
                 $i++;
             }
         }
         $this->logInWithPermission("ADMIN");
     }
     // Preserve memory settings
     $this->originalMemoryLimit = ini_get('memory_limit');
     // turn off template debugging
     Config::inst()->update('SSViewer', 'source_file_comments', false);
     // Clear requirements
     Requirements::clear();
     // Set up email
     $this->originalMailer = Email::mailer();
     $this->mailer = new TestMailer();
     Injector::inst()->registerService($this->mailer, 'Mailer');
     Config::inst()->remove('Email', 'send_all_emails_to');
 }
 public function setUp()
 {
     //nest config and injector for each test so they are effectively sandboxed per test
     Config::nest();
     Injector::nest();
     $this->originalReadingMode = Versioned::get_reading_mode();
     // We cannot run the tests on this abstract class.
     if (get_class($this) == __CLASS__) {
         $this->markTestSkipped(sprintf('Skipping %s ', get_class($this)));
         return;
     }
     // Mark test as being run
     $this->originalIsRunningTest = self::$is_running_test;
     self::$is_running_test = true;
     // i18n needs to be set to the defaults or tests fail
     i18n::set_locale(i18n::config()->get('default_locale'));
     i18n::config()->date_format = null;
     i18n::config()->time_format = null;
     // Set default timezone consistently to avoid NZ-specific dependencies
     date_default_timezone_set('UTC');
     // Remove password validation
     $this->originalMemberPasswordValidator = Member::password_validator();
     $this->originalRequirements = Requirements::backend();
     Member::set_password_validator(null);
     Cookie::config()->update('report_errors', false);
     if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) {
         RootURLController::reset();
     }
     if (class_exists('Translatable')) {
         Translatable::reset();
     }
     Versioned::reset();
     DataObject::reset();
     if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) {
         SiteTree::reset();
     }
     Hierarchy::reset();
     if (Controller::has_curr()) {
         Controller::curr()->setSession(Session::create(array()));
     }
     Security::$database_is_ready = null;
     // Add controller-name auto-routing
     // @todo Fix to work with namespaced controllers
     Director::config()->update('rules', array('$Controller//$Action/$ID/$OtherID' => '*'));
     $fixtureFiles = $this->getFixturePaths();
     // Todo: this could be a special test model
     $this->model = DataModel::inst();
     // Set up fixture
     if ($fixtureFiles || $this->usesDatabase) {
         if (!self::using_temp_db()) {
             self::create_temp_db();
         }
         DataObject::singleton()->flushCache();
         self::empty_temp_db();
         foreach ($this->requireDefaultRecordsFrom as $className) {
             $instance = singleton($className);
             if (method_exists($instance, 'requireDefaultRecords')) {
                 $instance->requireDefaultRecords();
             }
             if (method_exists($instance, 'augmentDefaultRecords')) {
                 $instance->augmentDefaultRecords();
             }
         }
         foreach ($fixtureFiles as $fixtureFilePath) {
             $fixture = YamlFixture::create($fixtureFilePath);
             $fixture->writeInto($this->getFixtureFactory());
         }
         $this->logInWithPermission("ADMIN");
     }
     // Preserve memory settings
     $this->originalMemoryLimit = ini_get('memory_limit');
     // turn off template debugging
     SSViewer::config()->update('source_file_comments', false);
     // Clear requirements
     Requirements::clear();
     // Set up email
     $this->mailer = new TestMailer();
     Injector::inst()->registerService($this->mailer, 'SilverStripe\\Control\\Email\\Mailer');
     Email::config()->remove('send_all_emails_to');
 }
 public function testDatabaseIsReadyWithInsufficientMemberColumns()
 {
     $old = Security::$force_database_is_ready;
     Security::$force_database_is_ready = null;
     Security::$database_is_ready = false;
     DBClassName::clear_classname_cache();
     // Assumption: The database has been built correctly by the test runner,
     // and has all columns present in the ORM
     /** @skipUpgrade */
     DB::get_schema()->renameField('Member', 'Email', 'Email_renamed');
     // Email column is now missing, which means we're not ready to do permission checks
     $this->assertFalse(Security::database_is_ready());
     // Rebuild the database (which re-adds the Email column), and try again
     $this->resetDBSchema(true);
     $this->assertTrue(Security::database_is_ready());
     Security::$force_database_is_ready = $old;
 }
Ejemplo n.º 7
0
 /**
  * Log the user in if the "remember login" cookie is set
  *
  * The <i>remember login token</i> will be changed on every successful
  * auto-login.
  */
 public static function autoLogin()
 {
     // Don't bother trying this multiple times
     if (!class_exists('SapphireTest', false) || !SapphireTest::is_running_test()) {
         self::$_already_tried_to_auto_log_in = true;
     }
     if (strpos(Cookie::get('alc_enc'), ':') === false || Session::get("loggedInAs") || !Security::database_is_ready()) {
         return;
     }
     if (strpos(Cookie::get('alc_enc'), ':') && Cookie::get('alc_device') && !Session::get("loggedInAs")) {
         list($uid, $token) = explode(':', Cookie::get('alc_enc'), 2);
         if (!$uid || !$token) {
             return;
         }
         $deviceID = Cookie::get('alc_device');
         $member = Member::get()->byID($uid);
         $rememberLoginHash = null;
         // check if autologin token matches
         if ($member) {
             $hash = $member->encryptWithUserSettings($token);
             $rememberLoginHash = RememberLoginHash::get()->filter(array('MemberID' => $member->ID, 'DeviceID' => $deviceID, 'Hash' => $hash))->First();
             if (!$rememberLoginHash) {
                 $member = null;
             } else {
                 // Check for expired token
                 $expiryDate = new DateTime($rememberLoginHash->ExpiryDate);
                 $now = DBDatetime::now();
                 $now = new DateTime($now->Rfc2822());
                 if ($now > $expiryDate) {
                     $member = null;
                 }
             }
         }
         if ($member) {
             self::session_regenerate_id();
             Session::set("loggedInAs", $member->ID);
             // This lets apache rules detect whether the user has logged in
             if (Member::config()->login_marker_cookie) {
                 Cookie::set(Member::config()->login_marker_cookie, 1, 0, null, null, false, true);
             }
             if ($rememberLoginHash) {
                 $rememberLoginHash->renew();
                 $tokenExpiryDays = Config::inst()->get('SilverStripe\\Security\\RememberLoginHash', 'token_expiry_days');
                 Cookie::set('alc_enc', $member->ID . ':' . $rememberLoginHash->getToken(), $tokenExpiryDays, null, null, false, true);
             }
             $member->write();
             // Audit logging hook
             $member->extend('memberAutoLoggedIn');
         }
     }
 }