get('frame_options'); if ($frameOptions) { $this->getResponse()->addHeader('X-Frame-Options', $frameOptions); } // Prevent search engines from indexing the login page $robotsTag = static::config()->get('robots_tag'); if ($robotsTag) { $this->getResponse()->addHeader('X-Robots-Tag', $robotsTag); } } /** * @inheritdoc */ public function index() { return $this->httpError(404); // no-op } /** * Get the selected authenticator for this request * * @param $name string The identifier of the authenticator in your config * @return Authenticator Class name of Authenticator * @throws LogicException */ protected function getAuthenticator($name) { $authenticators = self::config()->get('authenticators'); $name = $name ?: 'default'; if (isset($authenticators[$name])) { return Injector::inst()->get($authenticators[$name]); } throw new LogicException('No valid authenticator found'); } /** * Get all registered authenticators * * @return array Return an array of Authenticator objects */ public static function getAuthenticators($service = Authenticator::LOGIN) { $authenticators = self::config()->get('authenticators'); foreach ($authenticators as $name => &$class) { /** @var Authenticator $authenticator */ $authenticator = Injector::inst()->get($class); if ($authenticator->supportedServices() & $service) { $class = $authenticator; } else { unset($authenticators[$name]); } } return $authenticators; } /** * Check if a given authenticator is registered * * @param string $authenticator The configured identifier of the authenicator * @return bool Returns TRUE if the authenticator is registered, FALSE * otherwise. */ public static function hasAuthenticator($authenticator) { $authenticators = self::config()->get('authenticators'); return !empty($authenticators[$authenticator]); } /** * Register that we've had a permission failure trying to view the given page * * This will redirect to a login page. * If you don't provide a messageSet, a default will be used. * * @param Controller $controller The controller that you were on to cause the permission * failure. * @param string|array $messageSet The message to show to the user. This * can be a string, or a map of different * messages for different contexts. * If you pass an array, you can use the * following keys: * - default: The default message * - alreadyLoggedIn: The message to * show if the user * is already logged * in and lacks the * permission to * access the item. * * The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link * to log in. * @return HTTPResponse */ public static function permissionFailure($controller = null, $messageSet = null) { self::set_ignore_disallowed_actions(true); if (!$controller) { $controller = Controller::curr(); } if (Director::is_ajax()) { $response = ($controller) ? $controller->getResponse() : new HTTPResponse(); $response->setStatusCode(403); if (!static::getCurrentUser()) { $response->setBody(_t('SilverStripe\\CMS\\Controllers\\ContentController.NOTLOGGEDIN', 'Not logged in')); $response->setStatusDescription(_t('SilverStripe\\CMS\\Controllers\\ContentController.NOTLOGGEDIN', 'Not logged in')); // Tell the CMS to allow re-aunthentication if (CMSSecurity::enabled()) { $response->addHeader('X-Reauthenticate', '1'); } } return $response; } // Prepare the messageSet provided if (!$messageSet) { if ($configMessageSet = static::config()->get('default_message_set')) { $messageSet = $configMessageSet; } else { $messageSet = array( 'default' => _t( 'SilverStripe\\Security\\Security.NOTEPAGESECURED', "That page is secured. Enter your credentials below and we will send " . "you right along." ), 'alreadyLoggedIn' => _t( 'SilverStripe\\Security\\Security.ALREADYLOGGEDIN', "You don't have access to this page. If you have another account that " . "can access that page, you can log in again below.", "%s will be replaced with a link to log in." ) ); } } if (!is_array($messageSet)) { $messageSet = array('default' => $messageSet); } $member = static::getCurrentUser(); // Work out the right message to show if ($member && $member->exists()) { $response = ($controller) ? $controller->getResponse() : new HTTPResponse(); $response->setStatusCode(403); //If 'alreadyLoggedIn' is not specified in the array, then use the default //which should have been specified in the lines above if (isset($messageSet['alreadyLoggedIn'])) { $message = $messageSet['alreadyLoggedIn']; } else { $message = $messageSet['default']; } Security::setLoginMessage($message, ValidationResult::TYPE_WARNING); $loginResponse = (new Security())->login(new HTTPRequest('GET', '/')); if ($loginResponse instanceof HTTPResponse) { return $loginResponse; } $response->setBody((string)$loginResponse); $controller->extend('permissionDenied', $member); return $response; } else { $message = $messageSet['default']; } static::setLoginMessage($message, ValidationResult::TYPE_WARNING); Session::set("BackURL", $_SERVER['REQUEST_URI']); // TODO AccessLogEntry needs an extension to handle permission denied errors // Audit logging hook $controller->extend('permissionDenied', $member); return $controller->redirect(Controller::join_links( Security::config()->uninherited('login_url'), "?BackURL=" . urlencode($_SERVER['REQUEST_URI']) )); } public static function setCurrentUser($currentUser) { self::$currentUser = $currentUser; } public static function getCurrentUser() { return self::$currentUser; } /** * Get the login forms for all available authentication methods * * @return array Returns an array of available login forms (array of Form * objects). * * @todo Check how to activate/deactivate authentication methods */ public function getLoginForms() { return array_map( function ($authenticator) { return $authenticator->getLoginHandler($this->Link())->handleRequest($this->getRequest(), DataModel::inst()); }, Security::getAuthenticators() ); } /** * Get a link to a security action * * @param string $action Name of the action * @return string Returns the link to the given action */ public function Link($action = null) { /** @skipUpgrade */ return Controller::join_links(Director::baseURL(), "Security", $action); } /** * This action is available as a keep alive, so user * sessions don't timeout. A common use is in the admin. */ public function ping() { return 1; } /** * Log the currently logged in user out * * Logging out without ID-parameter in the URL, will log the user out of all applicable Authenticators. * * Adding an ID will only log the user out of that Authentication method. * * Logging out of Default will always completely log out the user. * * @param bool $redirect Redirect the user back to where they came. * - If it's false, the code calling logout() is * responsible for sending the user where-ever * they should go. * @return HTTPResponse|null */ public function logout($redirect = true) { $this->extend('beforeMemberLoggedOut'); $member = static::getCurrentUser(); if ($member) { // If we don't have a member, there's not much to log out. /** @var Authenticator $authenticator */ $authenticator = $this->getAuthenticator('default'); // Always use the default authenticator to log out $handler = $authenticator->getLogOutHandler(Controller::join_links($this->Link(), 'logout')); $result = $this->delegateToHandler($handler, 'default', []); if ($result !== true) { $this->extend('failureMemberLoggedOut', $authenticator); return $this->redirectBack(); } $this->extend('successMemberLoggedOut', $authenticator); // Member is successfully logged out. Write possible changes to the database. $member->write(); } $this->extend('afterMemberLoggedOut'); if ($redirect && (!$this->getResponse()->isFinished())) { return $this->redirectBack(); } return null; } /** * Perform pre-login checking and prepare a response if available prior to login * * @return HTTPResponse Substitute response object if the login process should be curcumvented. * Returns null if should proceed as normal. */ protected function preLogin() { // Event handler for pre-login, with an option to let it break you out of the login form $eventResults = $this->extend('onBeforeSecurityLogin'); // If there was a redirection, return if ($this->redirectedTo()) { return $this->getResponse(); } // If there was an HTTPResponse object returned, then return that if ($eventResults) { foreach ($eventResults as $result) { if ($result instanceof HTTPResponse) { return $result; } } } // If arriving on the login page already logged in, with no security error, and a ReturnURL then redirect // back. The login message check is neccesary to prevent infinite loops where BackURL links to // an action that triggers Security::permissionFailure. // This step is necessary in cases such as automatic redirection where a user is authenticated // upon landing on an SSL secured site and is automatically logged in, or some other case // where the user has permissions to continue but is not given the option. if ($this->getRequest()->requestVar('BackURL') && !$this->getLoginMessage() && ($member = static::getCurrentUser()) && $member->exists() ) { return $this->redirectBack(); } return null; } /** * Prepare the controller for handling the response to this request * * @param string $title Title to use * @return Controller */ protected function getResponseController($title) { // Use the default setting for which Page to use to render the security page $pageClass = $this->stat('page_class'); if (!$pageClass || !class_exists($pageClass)) { return $this; } // Create new instance of page holder /** @var Page $holderPage */ $holderPage = new $pageClass; $holderPage->Title = $title; /** @skipUpgrade */ $holderPage->URLSegment = 'Security'; // Disable ID-based caching of the log-in page by making it a random number $holderPage->ID = -1 * rand(1, 10000000); $controllerClass = $holderPage->getControllerName(); /** @var ContentController $controller */ $controller = $controllerClass::create($holderPage); $controller->setDataModel($this->model); $controller->doInit(); return $controller; } /** * Combine the given forms into a formset with a tabbed interface * * @param $forms * @return string */ protected function generateLoginFormSet($forms) { $viewData = new ArrayData(array( 'Forms' => new ArrayList($forms), )); return $viewData->renderWith( $this->getTemplatesFor('MultiAuthenticatorLogin') ); } /** * Get the HTML Content for the $Content area during login * * @param string &$messageType Type of message, if available, passed back to caller * @return string Message in HTML format */ protected function getLoginMessage(&$messageType = null) { $message = Session::get('Security.Message.message'); $messageType = null; if (empty($message)) { return null; } $messageType = Session::get('Security.Message.type'); $messageCast = Session::get('Security.Message.cast'); if ($messageCast !== ValidationResult::CAST_HTML) { $message = Convert::raw2xml($message); } return sprintf('

%s

', Convert::raw2att($messageType), $message); } /** * Set the next message to display for the security login page. Defaults to warning * * @param string $message Message * @param string $messageType Message type. One of ValidationResult::TYPE_* * @param string $messageCast Message cast. One of ValidationResult::CAST_* */ public static function setLoginMessage( $message, $messageType = ValidationResult::TYPE_WARNING, $messageCast = ValidationResult::CAST_TEXT ) { Session::set("Security.Message.message", $message); Session::set("Security.Message.type", $messageType); Session::set("Security.Message.cast", $messageCast); } /** * Clear login message */ public static function clearLoginMessage() { Session::clear("Security.Message"); } /** * Show the "login" page * * For multiple authenticators, Security_MultiAuthenticatorLogin is used. * See getTemplatesFor and getIncludeTemplate for how to override template logic * * @param $request * @return HTTPResponse|string Returns the "login" page as HTML code. * @throws HTTPResponse_Exception */ public function login($request, $service = Authenticator::LOGIN) { // Check pre-login process if ($response = $this->preLogin()) { return $response; } $link = $this->link("login"); // Delegate to a single handler - Security/login//... if (($name = $request->param('ID')) && self::hasAuthenticator($request->param('ID'))) { $request->shift(); $authenticator = $this->getAuthenticator($name); // @todo handle different Authenticator situations if (!$authenticator->supportedServices() & $service) { throw new HTTPResponse_Exception('Invalid Authenticator "' . $name . '" for login action', 418); } $authenticators = [ $name => $authenticator ]; // Delegate to all of them, building a tabbed view - Security/login } else { $authenticators = static::getAuthenticators($service); } $handlers = $authenticators; array_walk( $handlers, function (&$auth, $name) use ($link) { $auth = $auth->getLoginHandler(Controller::join_links($link, $name)); } ); return $this->delegateToMultipleHandlers( $handlers, _t('Security.LOGIN', 'Log in'), $this->getTemplatesFor('login') ); } /** * Delegate to an number of handlers, extracting their forms and rendering a tabbed form-set. * This is used to built the log-in page where there are multiple authenticators active. * * If a single handler is passed, delegateToHandler() will be called instead * * @param string $title The title of the form * @param array $templates * @return array|HTTPResponse|RequestHandler|\SilverStripe\ORM\FieldType\DBHTMLText|string */ protected function delegateToMultipleHandlers(array $handlers, $title, array $templates) { // Simpler case for a single authenticator if (count($handlers) === 1) { return $this->delegateToHandler(array_values($handlers)[0], $title, $templates); } // Process each of the handlers $results = array_map( function ($handler) { return $handler->handleRequest($this->getRequest(), \SilverStripe\ORM\DataModel::inst()); }, $handlers ); // Aggregate all their forms, assuming they all return $forms = []; foreach ($results as $authName => $singleResult) { // The result *must* be an array with a Form key if (!is_array($singleResult) || !isset($singleResult['Form'])) { user_error('Authenticator "' . $authName . '" doesn\'t support a tabbed login', E_USER_WARNING); continue; } $forms[] = $singleResult['Form']; } if (!$forms) { throw new \LogicException("No authenticators found compatible with a tabbed login"); } return $this->renderWrappedController( $title, [ 'Form' => $this->generateLoginFormSet($forms), ], $templates ); } /** * Delegate to another RequestHandler, rendering any fragment arrays into an appropriate. * controller. * * @param string $title The title of the form * @param array $templates * @return array|HTTPResponse|RequestHandler|\SilverStripe\ORM\FieldType\DBHTMLText|string */ protected function delegateToHandler(RequestHandler $handler, $title, array $templates) { $result = $handler->handleRequest($this->getRequest(), DataModel::inst()); // Return the customised controller - used to render in a Form // Post requests are expected to be login posts, so they'll be handled downstairs if (is_array($result)) { $result = $this->renderWrappedController($title, $result, $templates); } return $result; } /** * Render the given fragments into a security page controller with the given title. * @param string $title string The title to give the security page * @param array $fragments A map of objects to render into the page, e.g. "Form" * @param array $templates An array of templates to use for the render * @return HTTPResponse|\SilverStripe\ORM\FieldType\DBHTMLText */ protected function renderWrappedController($title, array $fragments, array $templates) { $controller = $this->getResponseController($title); // if the controller calls Director::redirect(), this will break early if (($response = $controller->getResponse()) && $response->isFinished()) { return $response; } // Handle any form messages from validation, etc. $messageType = ''; $message = $this->getLoginMessage($messageType); // We've displayed the message in the form output, so reset it for the next run. static::clearLoginMessage(); if ($message) { $messageResult = [ 'Content' => DBField::create_field('HTMLFragment', $message), 'Message' => DBField::create_field('HTMLFragment', $message), 'MessageType' => $messageType ]; $fragments = array_merge($fragments, $messageResult); } return $controller->customise($fragments)->renderWith($templates); } public function basicauthlogin() { $member = BasicAuth::requireLogin($this->getRequest(), "SilverStripe login", 'ADMIN'); static::setCurrentUser($member); } /** * Show the "lost password" page * * @return string Returns the "lost password" page as HTML code. */ public function lostpassword() { $handler = $this->getAuthenticator('default')->getLostPasswordHandler( Controller::join_links($this->link(), 'lostpassword') ); return $this->delegateToHandler( $handler, _t('SilverStripe\\Security\\Security.LOSTPASSWORDHEADER', 'Lost Password'), $this->getTemplatesFor('lostpassword') ); } /** * Show the "change password" page. * This page can either be called directly by logged-in users * (in which case they need to provide their old password), * or through a link emailed through {@link lostpassword()}. * In this case no old password is required, authentication is ensured * through the Member.AutoLoginHash property. * * @see ChangePasswordForm * * @return string|HTTPRequest Returns the "change password" page as HTML code, or a redirect response */ public function changepassword() { $controller = $this->getResponseController(_t('SilverStripe\\Security\\Security.CHANGEPASSWORDHEADER', 'Change your password')); // if the controller calls Director::redirect(), this will break early if (($response = $controller->getResponse()) && $response->isFinished()) { return $response; } // Extract the member from the URL. /** @var Member $member */ $member = null; if (isset($_REQUEST['m'])) { $member = Member::get()->filter('ID', (int)$_REQUEST['m'])->first(); } // Check whether we are merely changin password, or resetting. if (isset($_REQUEST['t']) && $member && $member->validateAutoLoginToken($_REQUEST['t'])) { // On first valid password reset request redirect to the same URL without hash to avoid referrer leakage. // if there is a current member, they should be logged out if ($curMember = static::getCurrentUser()) { /** @var LogoutHandler $handler */ $handler = $this->getAuthenticator('default')->getLogoutHandler($this->Link('logout')); $handler->doLogOut($curMember); } // Store the hash for the change password form. Will be unset after reload within the ChangePasswordForm. Session::set('AutoLoginHash', $member->encryptWithUserSettings($_REQUEST['t'])); return $this->redirect($this->Link('changepassword')); } elseif (Session::get('AutoLoginHash')) { // Subsequent request after the "first load with hash" (see previous if clause). $customisedController = $controller->customise(array( 'Content' => DBField::create_field( 'HTMLFragment', '

' . _t('SilverStripe\\Security\\Security.ENTERNEWPASSWORD', 'Please enter a new password.') . '

' ), 'Form' => $this->ChangePasswordForm(), )); } elseif (static::getCurrentUser()) { // Logged in user requested a password change form. $customisedController = $controller->customise(array( 'Content' => DBField::create_field( 'HTMLFragment', '

' . _t('SilverStripe\\Security\\Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '

' ), 'Form' => $this->ChangePasswordForm())); } else { // Show friendly message if it seems like the user arrived here via password reset feature. if (isset($_REQUEST['m']) || isset($_REQUEST['t'])) { $customisedController = $controller->customise( array('Content' => DBField::create_field( 'HTMLFragment', _t( 'SilverStripe\\Security\\Security.NOTERESETLINKINVALID', '

The password reset link is invalid or expired.

' . '

You can request a new one here or change your password after' . ' you logged in.

', [ 'link1' => $this->Link('lostpassword'), 'link2' => $this->Link('login') ] ) )) ); } else { return self::permissionFailure( $this, _t('SilverStripe\\Security\\Security.ERRORPASSWORDPERMISSION', 'You must be logged in in order to change your password!') ); } } return $customisedController->renderWith($this->getTemplatesFor('changepassword')); } /** * Create a link to the password reset form. * * GET parameters used: * - m: member ID * - t: plaintext token * * @param Member $member Member object associated with this link. * @param string $autologinToken The auto login token. * @return string */ public static function getPasswordResetLink($member, $autologinToken) { $autologinToken = urldecode($autologinToken); $selfControllerClass = __CLASS__; /** @var static $selfController */ $selfController = new $selfControllerClass(); return $selfController->Link('changepassword') . "?m={$member->ID}&t=$autologinToken"; } /** * Factory method for the lost password form * * @skipUpgrade * @return MemberAuthenticator\ChangePasswordForm */ public function ChangePasswordForm() { return MemberAuthenticator\ChangePasswordForm::create($this, 'ChangePasswordForm'); } /** * Determine the list of templates to use for rendering the given action. * * @skipUpgrade * @param string $action * @return array Template list */ public function getTemplatesFor($action) { $templates = SSViewer::get_templates_by_class(static::class, "_{$action}", __CLASS__); return array_merge( $templates, [ "Security_{$action}", "Security", $this->stat("template_main"), "BlankPage" ] ); } /** * Return an existing member with administrator privileges, or create one of necessary. * * Will create a default 'Administrators' group if no group is found * with an ADMIN permission. Will create a new 'Admin' member with administrative permissions * if no existing Member with these permissions is found. * * Important: Any newly created administrator accounts will NOT have valid * login credentials (Email/Password properties), which means they can't be used for login * purposes outside of any default credentials set through {@link Security::setDefaultAdmin()}. * * @return Member */ public static function findAnAdministrator() { // coupling to subsites module $origSubsite = null; if (is_callable('Subsite::changeSubsite')) { $origSubsite = Subsite::currentSubsiteID(); Subsite::changeSubsite(0); } /** @var Member $member */ $member = null; // find a group with ADMIN permission $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); if (is_callable('Subsite::changeSubsite')) { Subsite::changeSubsite($origSubsite); } if ($adminGroup) { $member = $adminGroup->Members()->First(); } if (!$adminGroup) { Group::singleton()->requireDefaultRecords(); $adminGroup = Permission::get_groups_by_permission('ADMIN')->first(); } if (!$member) { Member::singleton()->requireDefaultRecords(); $member = Permission::get_members_by_permission('ADMIN')->first(); } if (!$member) { $member = Member::default_admin(); } if (!$member) { // Failover to a blank admin $member = Member::create(); $member->FirstName = _t('SilverStripe\\Security\\Member.DefaultAdminFirstname', 'Default Admin'); $member->write(); // Add member to group instead of adding group to member // This bypasses the privilege escallation code in Member_GroupSet $adminGroup ->DirectMembers() ->add($member); } return $member; } /** * Flush the default admin credentials */ public static function clear_default_admin() { self::$default_username = null; self::$default_password = null; } /** * Set a default admin in dev-mode * * This will set a static default-admin which is not existing * as a database-record. By this workaround we can test pages in dev-mode * with a unified login. Submitted login-credentials are first checked * against this static information in {@link Security::authenticate()}. * * @param string $username The user name * @param string $password The password (in cleartext) * @return bool True if successfully set */ public static function setDefaultAdmin($username, $password) { // don't overwrite if already set if (self::$default_username || self::$default_password) { return false; } self::$default_username = $username; self::$default_password = $password; return true; } /** * Checks if the passed credentials are matching the default-admin. * Compares cleartext-password set through Security::setDefaultAdmin(). * * @param string $username * @param string $password * @return bool */ public static function check_default_admin($username, $password) { return ( self::$default_username === $username && self::$default_password === $password && self::has_default_admin() ); } /** * Check that the default admin account has been set. * @todo Check if we _actually_ only want this to work on dev */ public static function has_default_admin() { return !empty(self::$default_username) && !empty(self::$default_password) && (Director::get_environment_type() === 'dev'); } /** * Get default admin username * * @return string */ public static function default_admin_username() { return self::$default_username; } /** * Get default admin password * * @return string */ public static function default_admin_password() { return self::$default_password; } /** * Encrypt a password according to the current password encryption settings. * If the settings are so that passwords shouldn't be encrypted, the * result is simple the clear text password with an empty salt except when * a custom algorithm ($algorithm parameter) was passed. * * @param string $password The password to encrypt * @param string $salt Optional: The salt to use. If it is not passed, but * needed, the method will automatically create a * random salt that will then be returned as return value. * @param string $algorithm Optional: Use another algorithm to encrypt the * password (so that the encryption algorithm can be changed over the time). * @param Member $member Optional * @return mixed Returns an associative array containing the encrypted * password and the used salt in the form: * * array( * 'password' => string, * 'salt' => string, * 'algorithm' => string, * 'encryptor' => PasswordEncryptor instance * ) * * If the passed algorithm is invalid, FALSE will be returned. * * @see encrypt_passwords() */ public static function encrypt_password($password, $salt = null, $algorithm = null, $member = null) { // Fall back to the default encryption algorithm if (!$algorithm) { $algorithm = self::config()->get('password_encryption_algorithm'); } $e = PasswordEncryptor::create_for_algorithm($algorithm); // New salts will only need to be generated if the password is hashed for the first time $salt = ($salt) ? $salt : $e->salt($password); return array( 'password' => $e->encrypt($password, $salt, $member), 'salt' => $salt, 'algorithm' => $algorithm, 'encryptor' => $e ); } /** * Checks the database is in a state to perform security checks. * See {@link DatabaseAdmin->init()} for more information. * * @return bool */ public static function database_is_ready() { // Used for unit tests if (self::$force_database_is_ready !== null) { return self::$force_database_is_ready; } if (self::$database_is_ready) { return self::$database_is_ready; } $requiredClasses = ClassInfo::dataClassesFor(Member::class); $requiredClasses[] = Group::class; $requiredClasses[] = Permission::class; $schema = DataObject::getSchema(); foreach ($requiredClasses as $class) { // Skip test classes, as not all test classes are scaffolded at once if (is_a($class, TestOnly::class, true)) { continue; } // if any of the tables aren't created in the database $table = $schema->tableName($class); if (!ClassInfo::hasTable($table)) { return false; } // HACK: DataExtensions aren't applied until a class is instantiated for // the first time, so create an instance here. singleton($class); // if any of the tables don't have all fields mapped as table columns $dbFields = DB::field_list($table); if (!$dbFields) { return false; } $objFields = $schema->databaseFields($class, false); $missingFields = array_diff_key($objFields, $dbFields); if ($missingFields) { return false; } } self::$database_is_ready = true; return true; } /** * Resets the database_is_ready cache */ public static function clear_database_is_ready() { self::$database_is_ready = null; self::$force_database_is_ready = null; } /** * For the database_is_ready call to return a certain value - used for testing */ public static function force_database_is_ready($isReady) { self::$force_database_is_ready = $isReady; } /** * Enable or disable recording of login attempts * through the {@link LoginRecord} object. * * @deprecated 4.0 Use the "Security.login_recording" config setting instead * @param boolean $bool */ public static function set_login_recording($bool) { Deprecation::notice('4.0', 'Use the "Security.login_recording" config setting instead'); self::$login_recording = (bool)$bool; } /** * @deprecated 4.0 Use the "Security.login_recording" config setting instead * @return boolean */ public static function login_recording() { Deprecation::notice('4.0', 'Use the "Security.login_recording" config setting instead'); return self::$login_recording; } /** * @config * @var string Set the default login dest * This is the URL that users will be redirected to after they log in, * if they haven't logged in en route to access a secured page. * By default, this is set to the homepage. */ private static $default_login_dest = ""; protected static $ignore_disallowed_actions = false; /** * Set to true to ignore access to disallowed actions, rather than returning permission failure * Note that this is just a flag that other code needs to check with Security::ignore_disallowed_actions() * @param $flag True or false */ public static function set_ignore_disallowed_actions($flag) { self::$ignore_disallowed_actions = $flag; } public static function ignore_disallowed_actions() { return self::$ignore_disallowed_actions; } /** * Get the URL of the log-in page. * * To update the login url use the "Security.login_url" config setting. * * @return string */ public static function login_url() { return Controller::join_links(Director::baseURL(), self::config()->get('login_url')); } /** * Get the URL of the logout page. * * To update the logout url use the "Security.logout_url" config setting. * * @return string */ public static function logout_url() { return Controller::join_links(Director::baseURL(), self::config()->get('logout_url')); } /** * Get the URL of the logout page. * * To update the logout url use the "Security.logout_url" config setting. * * @return string */ public static function lost_password_url() { return Controller::join_links(Director::baseURL(), self::config()->get('lost_password_url')); } /** * Defines global accessible templates variables. * * @return array */ public static function get_template_global_variables() { return array( "LoginURL" => "login_url", "LogoutURL" => "logout_url", "LostPasswordURL" => "lost_password_url", "CurrentMember" => "getCurrentUser", "currentUser" => "getCurrentUser" ); } }