2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Implements a basic security model
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2008-02-25 03:10:37 +01:00
|
|
|
* @subpackage security
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
|
|
|
class Security extends Controller {
|
2011-02-13 23:14:51 +01:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
private static $allowed_actions = array(
|
|
|
|
'index',
|
|
|
|
'login',
|
|
|
|
'logout',
|
|
|
|
'basicauthlogin',
|
|
|
|
'lostpassword',
|
|
|
|
'passwordsent',
|
|
|
|
'changepassword',
|
2012-11-05 03:41:10 +01:00
|
|
|
'ping',
|
2011-02-21 22:53:58 +01:00
|
|
|
'LoginForm',
|
|
|
|
'ChangePasswordForm',
|
|
|
|
'LostPasswordForm',
|
2012-12-08 12:20:20 +01:00
|
|
|
);
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-10-02 23:57:12 +02:00
|
|
|
* Default user name. Only used in dev-mode by {@link setDefaultAdmin()}
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @see setDefaultAdmin()
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-16 19:39:41 +02:00
|
|
|
protected static $default_username;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-10-02 23:57:12 +02:00
|
|
|
* Default password. Only used in dev-mode by {@link setDefaultAdmin()}
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
* @see setDefaultAdmin()
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-16 19:39:41 +02:00
|
|
|
protected static $default_password;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-15 23:51:37 +02:00
|
|
|
/**
|
|
|
|
* If set to TRUE to prevent sharing of the session across several sites
|
|
|
|
* in the domain.
|
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @config
|
2007-09-15 23:51:37 +02:00
|
|
|
* @var bool
|
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
protected static $strict_path_checking = false;
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-15 23:51:37 +02:00
|
|
|
/**
|
2009-11-06 03:23:21 +01:00
|
|
|
* The password encryption algorithm to use by default.
|
|
|
|
* This is an arbitrary code registered through {@link PasswordEncryptor}.
|
2007-09-15 23:51:37 +02:00
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @config
|
2007-09-15 23:51:37 +02:00
|
|
|
* @var string
|
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $password_encryption_algorithm = 'blowfish';
|
2007-09-15 23:51:37 +02:00
|
|
|
|
2008-10-08 04:00:12 +02:00
|
|
|
/**
|
|
|
|
* Showing "Remember me"-checkbox
|
|
|
|
* on loginform, and saving encrypted credentials to a cookie.
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @config
|
2008-10-08 04:00:12 +02:00
|
|
|
* @var bool
|
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $autologin_enabled = true;
|
2008-10-08 04:00:12 +02:00
|
|
|
|
2014-04-09 01:41:25 +02:00
|
|
|
/**
|
|
|
|
* Determine if login username may be remembered between login sessions
|
|
|
|
* If set to false this will disable autocomplete and prevent username persisting in the session
|
|
|
|
*
|
|
|
|
* @config
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
private static $remember_username = true;
|
|
|
|
|
2007-10-25 03:51:53 +02:00
|
|
|
/**
|
|
|
|
* Location of word list to use for generating passwords
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @config
|
2007-10-25 03:51:53 +02:00
|
|
|
* @var string
|
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
protected static $word_list = './wordlist.txt';
|
2007-10-25 03:51:53 +02:00
|
|
|
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $template = 'BlankPage';
|
2011-03-22 23:43:49 +01:00
|
|
|
|
2008-04-05 01:04:16 +02:00
|
|
|
/**
|
|
|
|
* Template thats used to render the pages.
|
|
|
|
*
|
|
|
|
* @var string
|
2013-03-21 19:48:54 +01:00
|
|
|
* @config
|
2008-04-05 01:04:16 +02:00
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $template_main = 'Page';
|
2008-04-05 01:04:16 +02:00
|
|
|
|
2008-06-25 06:05:28 +02:00
|
|
|
/**
|
|
|
|
* Default message set used in permission failures.
|
|
|
|
*
|
2015-03-11 15:20:57 +01:00
|
|
|
* @config
|
2008-06-25 06:05:28 +02:00
|
|
|
* @var array|string
|
|
|
|
*/
|
2015-03-11 15:20:57 +01:00
|
|
|
private static $default_message_set;
|
2012-12-06 16:25:45 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Random secure token, can be used as a crypto key internally.
|
|
|
|
* Generate one through 'sake dev/generatesecuretoken'.
|
2013-04-09 01:48:13 +02:00
|
|
|
*
|
|
|
|
* @config
|
2012-12-06 16:25:45 +01:00
|
|
|
* @var String
|
|
|
|
*/
|
2013-04-09 01:48:13 +02:00
|
|
|
private static $token;
|
2008-06-25 06:05:28 +02:00
|
|
|
|
2007-10-25 03:51:53 +02:00
|
|
|
/**
|
|
|
|
* Get location of word list file
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @deprecated 3.2 Use the "Security.word_list" config setting instead
|
2007-10-25 03:51:53 +02:00
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function get_word_list() {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.word_list" config setting instead');
|
|
|
|
return self::config()->word_list;
|
2007-10-25 03:51:53 +02:00
|
|
|
}
|
|
|
|
|
2008-08-11 02:14:48 +02:00
|
|
|
/**
|
|
|
|
* Enable or disable recording of login attempts
|
|
|
|
* through the {@link LoginRecord} object.
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @config
|
2008-08-11 02:14:48 +02:00
|
|
|
* @var boolean $login_recording
|
|
|
|
*/
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $login_recording = false;
|
2008-08-11 02:14:48 +02:00
|
|
|
|
2010-10-15 05:43:30 +02:00
|
|
|
/**
|
|
|
|
* @var boolean If set to TRUE or FALSE, {@link database_is_ready()}
|
|
|
|
* will always return FALSE. Used for unit testing.
|
|
|
|
*/
|
|
|
|
static $force_database_is_ready = null;
|
|
|
|
|
2012-04-11 14:29:35 +02:00
|
|
|
/**
|
|
|
|
* When the database has once been verified as ready, it will not do the
|
|
|
|
* checks again.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
2012-04-11 17:22:55 +02:00
|
|
|
static $database_is_ready = false;
|
2012-04-11 14:29:35 +02:00
|
|
|
|
2007-10-25 03:51:53 +02:00
|
|
|
/**
|
|
|
|
* Set location of word list file
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @deprecated 3.2 Use the "Security.word_list" config setting instead
|
2007-10-25 03:51:53 +02:00
|
|
|
* @param string $wordListFile Location of word list file
|
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function set_word_list($wordListFile) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.word_list" config setting instead');
|
|
|
|
self::config()->word_list = $wordListFile;
|
2007-10-25 03:51:53 +02:00
|
|
|
}
|
2008-06-25 06:05:28 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the default message set used in permissions failures.
|
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.default_message_set" config setting instead
|
2008-06-25 06:05:28 +02:00
|
|
|
* @param string|array $messageSet
|
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function set_default_message_set($messageSet) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.default_message_set" config setting instead');
|
|
|
|
self::config()->default_message_set = $messageSet;
|
2008-06-25 06:05:28 +02:00
|
|
|
}
|
2007-09-15 23:51:37 +02:00
|
|
|
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Register that we've had a permission failure trying to view the given page
|
|
|
|
*
|
2007-07-19 12:40:28 +02:00
|
|
|
* This will redirect to a login page.
|
|
|
|
* If you don't provide a messageSet, a default will be used.
|
2007-09-14 21:10:18 +02:00
|
|
|
*
|
2007-11-07 03:33:09 +01:00
|
|
|
* @param Controller $controller The controller that you were on to cause the permission
|
2012-12-08 12:20:20 +01:00
|
|
|
* failure.
|
2007-09-14 21:10:18 +02:00
|
|
|
* @param string|array $messageSet The message to show to the user. This
|
2012-12-08 12:20:20 +01:00
|
|
|
* 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.
|
2009-07-09 05:20:32 +02:00
|
|
|
*
|
|
|
|
* The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
|
|
|
|
* to log in.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function permissionFailure($controller = null, $messageSet = null) {
|
2012-06-15 05:17:32 +02:00
|
|
|
self::set_ignore_disallowed_actions(true);
|
|
|
|
|
2009-07-09 05:20:32 +02:00
|
|
|
if(!$controller) $controller = Controller::curr();
|
|
|
|
|
2008-11-03 14:50:06 +01:00
|
|
|
if(Director::is_ajax()) {
|
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
|
|
|
$response = ($controller) ? $controller->getResponse() : new SS_HTTPResponse();
|
2008-11-03 14:50:06 +01:00
|
|
|
$response->setStatusCode(403);
|
2013-08-06 03:10:55 +02:00
|
|
|
if(!Member::currentUser()) {
|
|
|
|
$response->setBody(_t('ContentController.NOTLOGGEDIN','Not logged in'));
|
|
|
|
$response->setStatusDescription(_t('ContentController.NOTLOGGEDIN','Not logged in'));
|
2014-10-06 05:01:33 +02:00
|
|
|
// Tell the CMS to allow re-aunthentication
|
|
|
|
if(CMSSecurity::enabled()) {
|
|
|
|
$response->addHeader('X-Reauthenticate', '1');
|
|
|
|
}
|
2013-08-06 03:10:55 +02:00
|
|
|
}
|
2008-11-03 14:50:06 +01:00
|
|
|
return $response;
|
|
|
|
} else {
|
|
|
|
// Prepare the messageSet provided
|
|
|
|
if(!$messageSet) {
|
2015-03-11 15:20:57 +01:00
|
|
|
if($configMessageSet = static::config()->get('default_message_set')) {
|
|
|
|
$messageSet = $configMessageSet;
|
2008-11-03 14:50:06 +01:00
|
|
|
} else {
|
|
|
|
$messageSet = array(
|
|
|
|
'default' => _t(
|
|
|
|
'Security.NOTEPAGESECURED',
|
2009-07-09 05:20:32 +02:00
|
|
|
"That page is secured. Enter your credentials below and we will send "
|
|
|
|
. "you right along."
|
2008-11-03 14:50:06 +01:00
|
|
|
),
|
|
|
|
'alreadyLoggedIn' => _t(
|
|
|
|
'Security.ALREADYLOGGEDIN',
|
2009-07-09 05:20:32 +02:00
|
|
|
"You don't have access to this page. If you have another account that "
|
2010-10-19 02:55:20 +02:00
|
|
|
. "can access that page, you can log in again below.",
|
2012-04-14 00:16:22 +02:00
|
|
|
|
2009-07-09 05:20:32 +02:00
|
|
|
"%s will be replaced with a link to log in."
|
2008-11-03 14:50:06 +01:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2008-06-25 06:05:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2008-11-03 14:50:06 +01:00
|
|
|
if(!is_array($messageSet)) {
|
|
|
|
$messageSet = array('default' => $messageSet);
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2013-04-12 00:59:00 +02:00
|
|
|
$member = Member::currentUser();
|
|
|
|
|
2008-11-03 14:50:06 +01:00
|
|
|
// Work out the right message to show
|
2013-04-12 00:59:00 +02:00
|
|
|
if($member && $member->exists()) {
|
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
|
|
|
$response = ($controller) ? $controller->getResponse() : new SS_HTTPResponse();
|
2009-07-09 05:20:32 +02:00
|
|
|
$response->setStatusCode(403);
|
|
|
|
|
2010-04-12 02:51:21 +02:00
|
|
|
//If 'alreadyLoggedIn' is not specified in the array, then use the default
|
|
|
|
//which should have been specified in the lines above
|
2013-04-12 00:59:00 +02:00
|
|
|
if(isset($messageSet['alreadyLoggedIn'])) {
|
|
|
|
$message = $messageSet['alreadyLoggedIn'];
|
|
|
|
} else {
|
|
|
|
$message = $messageSet['default'];
|
|
|
|
}
|
2010-04-12 02:51:21 +02:00
|
|
|
|
2010-10-19 02:55:20 +02:00
|
|
|
// Somewhat hackish way to render a login form with an error message.
|
|
|
|
$me = new Security();
|
|
|
|
$form = $me->LoginForm();
|
|
|
|
$form->sessionMessage($message, 'warning');
|
|
|
|
Session::set('MemberLoginForm.force_message',1);
|
|
|
|
$formText = $me->login();
|
2013-04-12 00:59:00 +02:00
|
|
|
|
2010-10-19 02:55:20 +02:00
|
|
|
$response->setBody($formText);
|
2013-04-12 00:59:00 +02:00
|
|
|
|
|
|
|
$controller->extend('permissionDenied', $member);
|
|
|
|
|
2009-07-09 05:20:32 +02:00
|
|
|
return $response;
|
2008-11-03 14:50:06 +01:00
|
|
|
} else {
|
|
|
|
$message = $messageSet['default'];
|
|
|
|
}
|
|
|
|
|
|
|
|
Session::set("Security.Message.message", $message);
|
|
|
|
Session::set("Security.Message.type", 'warning');
|
|
|
|
|
|
|
|
Session::set("BackURL", $_SERVER['REQUEST_URI']);
|
|
|
|
|
|
|
|
// TODO AccessLogEntry needs an extension to handle permission denied errors
|
|
|
|
// Audit logging hook
|
2012-05-23 11:50:02 +02:00
|
|
|
$controller->extend('permissionDenied', $member);
|
2008-11-28 06:29:52 +01:00
|
|
|
|
2011-03-16 04:13:14 +01:00
|
|
|
$controller->redirect(
|
|
|
|
Config::inst()->get('Security', 'login_url')
|
2013-02-18 15:43:52 +01:00
|
|
|
. "?BackURL=" . urlencode($_SERVER['REQUEST_URI'])
|
2011-03-16 04:13:14 +01:00
|
|
|
);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-08-17 05:09:46 +02:00
|
|
|
return;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2013-07-11 10:29:21 +02:00
|
|
|
public function init() {
|
|
|
|
parent::init();
|
|
|
|
|
|
|
|
// Prevent clickjacking, see https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options
|
|
|
|
$this->response->addHeader('X-Frame-Options', 'SAMEORIGIN');
|
|
|
|
}
|
|
|
|
|
2013-08-20 21:12:55 +02:00
|
|
|
public function index() {
|
|
|
|
return $this->httpError(404); // no-op
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2012-12-08 12:20:20 +01:00
|
|
|
/**
|
2014-10-06 05:01:33 +02:00
|
|
|
* Get the selected authenticator for this request
|
|
|
|
*
|
|
|
|
* @return string Class name of Authenticator
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2014-10-06 05:01:33 +02:00
|
|
|
protected function getAuthenticator() {
|
|
|
|
$authenticator = $this->request->requestVar('AuthenticationMethod');
|
|
|
|
if($authenticator) {
|
2007-09-16 02:44:30 +02:00
|
|
|
$authenticators = Authenticator::get_authenticators();
|
2007-09-15 02:08:23 +02:00
|
|
|
if(in_array($authenticator, $authenticators)) {
|
2014-10-06 05:01:33 +02:00
|
|
|
return $authenticator;
|
2010-10-13 03:36:12 +02:00
|
|
|
}
|
2014-10-06 05:01:33 +02:00
|
|
|
} else {
|
|
|
|
return Authenticator::get_default_authenticator();
|
2010-10-13 03:36:12 +02:00
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
/**
|
|
|
|
* Get the login form to process according to the submitted data
|
|
|
|
*
|
|
|
|
* @return Form
|
|
|
|
*/
|
|
|
|
public function LoginForm() {
|
|
|
|
$authenticator = $this->getAuthenticator();
|
|
|
|
if($authenticator) return $authenticator::get_login_form($this);
|
|
|
|
throw new Exception('Passed invalid authentication method');
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2012-12-08 12:20:20 +01:00
|
|
|
/**
|
2007-09-14 19:04:11 +02:00
|
|
|
* Get the login forms for all available authentication methods
|
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return array Returns an array of available login forms (array of Form
|
|
|
|
* objects).
|
|
|
|
*
|
2007-09-14 19:04:11 +02:00
|
|
|
* @todo Check how to activate/deactivate authentication methods
|
|
|
|
*/
|
2014-10-06 05:01:33 +02:00
|
|
|
public function GetLoginForms() {
|
2007-09-14 19:04:11 +02:00
|
|
|
$forms = array();
|
2007-09-15 02:08:23 +02:00
|
|
|
|
2007-09-16 02:44:30 +02:00
|
|
|
$authenticators = Authenticator::get_authenticators();
|
2007-09-15 02:08:23 +02:00
|
|
|
foreach($authenticators as $authenticator) {
|
2014-10-06 05:01:33 +02:00
|
|
|
$forms[] = $authenticator::get_login_form($this);
|
2007-09-15 02:08:23 +02:00
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
return $forms;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a link to a security action
|
|
|
|
*
|
|
|
|
* @param string $action Name of the action
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the link to the given action
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2012-01-10 04:23:42 +01:00
|
|
|
public function Link($action = null) {
|
2014-10-06 05:01:33 +02:00
|
|
|
return Controller::join_links(Director::baseURL(), "Security", $action);
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2012-11-05 03:41:10 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 19:04:11 +02:00
|
|
|
* Log the currently logged in user out
|
|
|
|
*
|
2007-09-14 05:12:21 +02:00
|
|
|
* @param bool $redirect Redirect the user back to where they came.
|
2012-12-08 12:20:20 +01:00
|
|
|
* - If it's false, the code calling logout() is
|
|
|
|
* responsible for sending the user where-ever
|
|
|
|
* they should go.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public function logout($redirect = true) {
|
2010-10-19 02:47:05 +02:00
|
|
|
$member = Member::currentUser();
|
|
|
|
if($member) $member->logOut();
|
2007-09-14 21:10:18 +02:00
|
|
|
|
2013-06-26 23:49:10 +02:00
|
|
|
if($redirect && (!$this->response || !$this->response->isFinished())) {
|
|
|
|
$this->redirectBack();
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
/**
|
2014-10-06 05:01:33 +02:00
|
|
|
* Perform pre-login checking and prepare a response if available prior to login
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2014-10-06 05:01:33 +02:00
|
|
|
* @return SS_HTTPResponse Substitute response object if the login process should be curcumvented.
|
|
|
|
* Returns null if should proceed as normal.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2014-10-06 05:01:33 +02:00
|
|
|
protected function preLogin() {
|
2009-03-04 04:44:11 +01:00
|
|
|
// 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
|
2014-10-06 05:01:33 +02:00
|
|
|
if($this->redirectedTo()) return $this->response;
|
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
|
|
|
// If there was an SS_HTTPResponse object returned, then return that
|
2014-10-06 05:01:33 +02:00
|
|
|
if($eventResults) {
|
2009-03-04 04:44:11 +01:00
|
|
|
foreach($eventResults as $result) {
|
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
|
|
|
if($result instanceof SS_HTTPResponse) return $result;
|
2009-03-04 04:44:11 +01:00
|
|
|
}
|
|
|
|
}
|
2014-10-06 05:01:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare the controller for handling the response to this request
|
|
|
|
*
|
|
|
|
* @param string $title Title to use
|
|
|
|
* @return Controller
|
|
|
|
*/
|
|
|
|
protected function getResponseController($title) {
|
|
|
|
if(!class_exists('SiteTree')) return $this;
|
|
|
|
|
|
|
|
// Use sitetree pages to render the security page
|
|
|
|
$tmpPage = new Page();
|
|
|
|
$tmpPage->Title = $title;
|
|
|
|
$tmpPage->URLSegment = "Security";
|
|
|
|
// Disable ID-based caching of the log-in page by making it a random number
|
|
|
|
$tmpPage->ID = -1 * rand(1,10000000);
|
|
|
|
|
|
|
|
$controller = Page_Controller::create($tmpPage);
|
|
|
|
$controller->setDataModel($this->model);
|
|
|
|
$controller->init();
|
|
|
|
return $controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine the list of templates to use for rendering the given action
|
|
|
|
*
|
|
|
|
* @param string $action
|
|
|
|
* @return array Template list
|
|
|
|
*/
|
|
|
|
protected function getTemplatesFor($action) {
|
|
|
|
return array("Security_{$action}", 'Security', $this->stat('template_main'), 'BlankPage');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Combine the given forms into a formset with a tabbed interface
|
|
|
|
*
|
|
|
|
* @param array $forms List of LoginForm instances
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function generateLoginFormSet($forms) {
|
|
|
|
// Include resources
|
|
|
|
Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js');
|
|
|
|
Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
|
|
|
|
Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
|
|
|
|
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
|
|
|
|
Requirements::css(FRAMEWORK_DIR . '/css/Security_login.css');
|
|
|
|
Requirements::javascript(FRAMEWORK_DIR . '/javascript/TabSet.js');
|
2009-03-04 04:44:11 +01:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
$content = '<div id="Form_EditForm">';
|
|
|
|
$content .= '<div class="ss-tabset">';
|
|
|
|
$content .= '<ul>';
|
|
|
|
$contentForms = '';
|
|
|
|
|
|
|
|
foreach($forms as $form) {
|
|
|
|
$content .= "<li><a href=\"#{$form->FormName()}_tab\">"
|
|
|
|
. $form->getAuthenticator()->get_name()
|
|
|
|
. "</a></li>\n";
|
|
|
|
|
|
|
|
$contentForms .= '<div class="tab" id="' . $form->FormName() . '_tab">'
|
|
|
|
. $form->forTemplate() . "</div>\n";
|
2007-09-14 21:13:12 +02:00
|
|
|
}
|
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
$content .= "</ul>\n" . $contentForms . "\n</div>\n</div>\n";
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the HTML Content for the $Content area during login
|
|
|
|
*
|
|
|
|
* @return string Message in HTML format
|
|
|
|
*/
|
|
|
|
protected function getLoginMessage() {
|
|
|
|
$message = Session::get('Security.Message.message');
|
|
|
|
if(empty($message)) return null;
|
2011-03-22 23:43:49 +01:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
$messageType = Session::get('Security.Message.type');
|
|
|
|
if($messageType === 'bad') {
|
|
|
|
return "<p class=\"message $messageType\">$message</p>";
|
2011-03-22 23:43:49 +01:00
|
|
|
} else {
|
2014-10-06 05:01:33 +02:00
|
|
|
return "<p>$message</p>";
|
2011-03-22 23:43:49 +01:00
|
|
|
}
|
2014-10-06 05:01:33 +02:00
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
/**
|
|
|
|
* Show the "login" page
|
|
|
|
*
|
|
|
|
* @return string Returns the "login" page as HTML code.
|
|
|
|
*/
|
|
|
|
public function login() {
|
|
|
|
// Check pre-login process
|
|
|
|
if($response = $this->preLogin()) return $response;
|
|
|
|
|
|
|
|
// Legacy: Allow projects to use custom mysite/css/tabs.css here
|
|
|
|
$customCSS = project() . '/css/tabs.css';
|
|
|
|
if(Director::fileExists($customCSS)) {
|
|
|
|
Requirements::css($customCSS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get response handler
|
|
|
|
$controller = $this->getResponseController(_t('Security.LOGIN', 'Log in'));
|
|
|
|
|
2007-09-27 23:13:59 +02:00
|
|
|
$forms = $this->GetLoginForms();
|
|
|
|
if(!count($forms)) {
|
2012-09-26 23:34:00 +02:00
|
|
|
user_error('No login-forms found, please use Authenticator::register_authenticator() to add one',
|
|
|
|
E_USER_ERROR);
|
2007-09-27 23:13:59 +02:00
|
|
|
}
|
2014-10-06 05:01:33 +02:00
|
|
|
|
|
|
|
// if the controller calls Director::redirect(), this will break early
|
|
|
|
if(($response = $controller->getResponse()) && $response->isFinished()) return $response;
|
2007-09-27 23:13:59 +02:00
|
|
|
|
|
|
|
// only display tabs when more than one authenticator is provided
|
|
|
|
// to save bandwidth and reduce the amount of custom styling needed
|
|
|
|
if(count($forms) > 1) {
|
2014-10-06 05:01:33 +02:00
|
|
|
$content = $this->generateLoginFormSet($forms);
|
2007-09-27 23:13:59 +02:00
|
|
|
} else {
|
2014-10-06 05:01:33 +02:00
|
|
|
$content = $forms[0]->forTemplate();
|
2007-09-27 23:13:59 +02:00
|
|
|
}
|
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
if($message = $this->getLoginMessage()) {
|
2007-09-27 23:13:59 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
|
|
|
"Content" => $message,
|
2007-12-02 22:19:54 +01:00
|
|
|
"Form" => $content,
|
2007-09-27 23:13:59 +02:00
|
|
|
));
|
|
|
|
} else {
|
|
|
|
$customisedController = $controller->customise(array(
|
2009-06-18 10:22:27 +02:00
|
|
|
"Form" => $content,
|
2007-09-27 23:13:59 +02:00
|
|
|
));
|
|
|
|
}
|
2008-11-10 04:51:35 +01:00
|
|
|
|
|
|
|
Session::clear('Security.Message');
|
2007-09-27 23:13:59 +02:00
|
|
|
|
|
|
|
// custom processing
|
2014-10-06 05:01:33 +02:00
|
|
|
return $customisedController->renderWith($this->getTemplatesFor('login'));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-10-02 06:32:11 +02:00
|
|
|
|
2012-09-19 12:07:39 +02:00
|
|
|
public function basicauthlogin() {
|
2007-10-02 06:32:11 +02:00
|
|
|
$member = BasicAuth::requireLogin("SilverStripe login", 'ADMIN');
|
|
|
|
$member->LogIn();
|
|
|
|
}
|
2008-09-24 06:17:33 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Show the "lost password" page
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-16 02:32:48 +02:00
|
|
|
* @return string Returns the "lost password" page as HTML code.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public function lostpassword() {
|
2014-10-06 05:01:33 +02:00
|
|
|
$controller = $this->getResponseController(_t('Security.LOSTPASSWORDHEADER', 'Lost Password'));
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2011-08-18 13:32:02 +02:00
|
|
|
// if the controller calls Director::redirect(), this will break early
|
|
|
|
if(($response = $controller->getResponse()) && $response->isFinished()) return $response;
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
2007-10-25 04:47:45 +02:00
|
|
|
'Content' =>
|
|
|
|
'<p>' .
|
|
|
|
_t(
|
|
|
|
'Security.NOTERESETPASSWORD',
|
|
|
|
'Enter your e-mail address and we will send you a link with which you can reset your password'
|
|
|
|
) .
|
|
|
|
'</p>',
|
2007-09-16 02:32:48 +02:00
|
|
|
'Form' => $this->LostPasswordForm(),
|
2007-07-19 12:40:28 +02:00
|
|
|
));
|
|
|
|
|
2007-08-23 08:43:40 +02:00
|
|
|
//Controller::$currentController = $controller;
|
2014-10-06 05:01:33 +02:00
|
|
|
return $customisedController->renderWith($this->getTemplatesFor('lostpassword'));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-09-16 02:32:48 +02:00
|
|
|
/**
|
|
|
|
* Factory method for the lost password form
|
|
|
|
*
|
|
|
|
* @return Form Returns the lost password form
|
|
|
|
*/
|
|
|
|
public function LostPasswordForm() {
|
2012-04-04 16:59:30 +02:00
|
|
|
return MemberLoginForm::create( $this,
|
2008-10-16 13:08:52 +02:00
|
|
|
'LostPasswordForm',
|
2011-05-11 09:51:54 +02:00
|
|
|
new FieldList(
|
2009-02-02 00:49:53 +01:00
|
|
|
new EmailField('Email', _t('Member.EMAIL', 'Email'))
|
2008-10-16 13:08:52 +02:00
|
|
|
),
|
2011-05-11 09:51:54 +02:00
|
|
|
new FieldList(
|
2008-10-16 13:08:52 +02:00
|
|
|
new FormAction(
|
|
|
|
'forgotPassword',
|
|
|
|
_t('Security.BUTTONSEND', 'Send me the password reset link')
|
|
|
|
)
|
|
|
|
),
|
|
|
|
false
|
|
|
|
);
|
2007-09-16 02:32:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
/**
|
2008-10-24 04:23:53 +02:00
|
|
|
* Show the "password sent" page, after a user has requested
|
|
|
|
* to reset their password.
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
|
|
|
* @param SS_HTTPRequest $request The SS_HTTPRequest for this action.
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return string Returns the "password sent" page as HTML code.
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2008-09-19 01:00:36 +02:00
|
|
|
public function passwordsent($request) {
|
2014-10-06 05:01:33 +02:00
|
|
|
$controller = $this->getResponseController(_t('Security.LOSTPASSWORDHEADER', 'Lost Password'));
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2011-08-18 13:32:02 +02:00
|
|
|
// if the controller calls Director::redirect(), this will break early
|
|
|
|
if(($response = $controller->getResponse()) && $response->isFinished()) return $response;
|
|
|
|
|
2011-03-25 00:01:50 +01:00
|
|
|
$email = Convert::raw2xml(rawurldecode($request->param('ID')) . '.' . $request->getExtension());
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
2012-09-26 23:34:00 +02:00
|
|
|
'Title' => _t('Security.PASSWORDSENTHEADER', "Password reset link sent to '{email}'",
|
|
|
|
array('email' => $email)),
|
2007-09-16 02:32:48 +02:00
|
|
|
'Content' =>
|
2012-09-26 23:34:00 +02:00
|
|
|
"<p>"
|
|
|
|
. _t('Security.PASSWORDSENTTEXT',
|
|
|
|
"Thank you! A reset link has been sent to '{email}', provided an account exists for this email"
|
|
|
|
. " address.",
|
|
|
|
array('email' => $email))
|
|
|
|
. "</p>",
|
2010-10-15 05:06:53 +02:00
|
|
|
'Email' => $email
|
2007-07-19 12:40:28 +02:00
|
|
|
));
|
|
|
|
|
2007-08-23 08:43:40 +02:00
|
|
|
//Controller::$currentController = $controller;
|
2014-10-06 05:01:33 +02:00
|
|
|
return $customisedController->renderWith($this->getTemplatesFor('passwordsent'));
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
|
|
|
|
2007-09-16 02:32:48 +02:00
|
|
|
/**
|
2012-11-08 04:33:19 +01:00
|
|
|
* Create a link to the password reset form.
|
2007-09-16 02:32:48 +02:00
|
|
|
*
|
2012-11-08 04:33:19 +01:00
|
|
|
* GET parameters used:
|
|
|
|
* - m: member ID
|
|
|
|
* - t: plaintext token
|
|
|
|
*
|
|
|
|
* @param Member $member Member object associated with this link.
|
|
|
|
* @param string $autoLoginHash The auto login token.
|
2007-09-16 02:32:48 +02:00
|
|
|
*/
|
2012-11-08 04:33:19 +01:00
|
|
|
public static function getPasswordResetLink($member, $autologinToken) {
|
|
|
|
$autologinToken = urldecode($autologinToken);
|
2012-04-12 02:09:39 +02:00
|
|
|
$selfControllerClass = __CLASS__;
|
|
|
|
$selfController = new $selfControllerClass();
|
2012-11-08 04:33:19 +01:00
|
|
|
return $selfController->Link('changepassword') . "?m={$member->ID}&t=$autologinToken";
|
2007-09-16 02:32:48 +02:00
|
|
|
}
|
2007-09-16 19:39:41 +02:00
|
|
|
|
2007-09-16 02:32:48 +02:00
|
|
|
/**
|
2010-12-09 22:18:49 +01:00
|
|
|
* 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
|
2007-09-16 02:32:48 +02:00
|
|
|
*
|
|
|
|
* @return string Returns the "change password" page as HTML code.
|
|
|
|
*/
|
|
|
|
public function changepassword() {
|
2014-10-06 05:01:33 +02:00
|
|
|
$controller = $this->getResponseController(_t('Security.CHANGEPASSWORDHEADER', 'Change your password'));
|
2007-09-16 02:32:48 +02:00
|
|
|
|
2011-08-18 13:32:02 +02:00
|
|
|
// if the controller calls Director::redirect(), this will break early
|
|
|
|
if(($response = $controller->getResponse()) && $response->isFinished()) return $response;
|
|
|
|
|
2012-11-08 04:33:19 +01:00
|
|
|
// Extract the member from the URL.
|
|
|
|
$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.
|
|
|
|
|
|
|
|
// Store the hash for the change password form. Will be unset after reload within the ChangePasswordForm.
|
|
|
|
Session::set('AutoLoginHash', $member->encryptWithUserSettings($_REQUEST['t']));
|
2010-12-09 22:18:49 +01:00
|
|
|
|
|
|
|
return $this->redirect($this->Link('changepassword'));
|
|
|
|
} elseif(Session::get('AutoLoginHash')) {
|
2012-11-08 04:33:19 +01:00
|
|
|
// Subsequent request after the "first load with hash" (see previous if clause).
|
2007-09-16 02:32:48 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
|
|
|
'Content' =>
|
2007-10-25 04:47:45 +02:00
|
|
|
'<p>' .
|
|
|
|
_t('Security.ENTERNEWPASSWORD', 'Please enter a new password.') .
|
|
|
|
'</p>',
|
2007-09-16 02:32:48 +02:00
|
|
|
'Form' => $this->ChangePasswordForm(),
|
|
|
|
));
|
|
|
|
} elseif(Member::currentUser()) {
|
2012-11-08 04:33:19 +01:00
|
|
|
// Logged in user requested a password change form.
|
2007-09-16 02:32:48 +02:00
|
|
|
$customisedController = $controller->customise(array(
|
2012-09-26 23:34:00 +02:00
|
|
|
'Content' => '<p>'
|
|
|
|
. _t('Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '</p>',
|
2007-09-16 02:32:48 +02:00
|
|
|
'Form' => $this->ChangePasswordForm()));
|
|
|
|
|
|
|
|
} else {
|
2013-10-24 22:29:21 +02:00
|
|
|
// Show friendly message if it seems like the user arrived here via password reset feature.
|
|
|
|
if(isset($_REQUEST['m']) || isset($_REQUEST['t'])) {
|
2007-10-25 04:47:45 +02:00
|
|
|
$customisedController = $controller->customise(
|
|
|
|
array('Content' =>
|
2012-05-01 21:44:54 +02:00
|
|
|
_t(
|
|
|
|
'Security.NOTERESETLINKINVALID',
|
2012-09-26 23:34:00 +02:00
|
|
|
'<p>The password reset link is invalid or expired.</p>'
|
|
|
|
. '<p>You can request a new one <a href="{link1}">here</a> or change your password after'
|
|
|
|
. ' you <a href="{link2}">logged in</a>.</p>',
|
2012-05-01 21:44:54 +02:00
|
|
|
array('link1' => $this->Link('lostpassword'), 'link2' => $this->link('login'))
|
2007-10-25 04:47:45 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
2007-09-16 02:32:48 +02:00
|
|
|
} else {
|
2007-10-25 04:47:45 +02:00
|
|
|
self::permissionFailure(
|
|
|
|
$this,
|
|
|
|
_t('Security.ERRORPASSWORDPERMISSION', 'You must be logged in in order to change your password!')
|
|
|
|
);
|
2007-09-16 17:31:44 +02:00
|
|
|
return;
|
2007-09-16 02:32:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
return $customisedController->renderWith($this->getTemplatesFor('changepassword'));
|
2007-09-16 02:32:48 +02:00
|
|
|
}
|
2008-09-24 06:17:33 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Factory method for the lost password form
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return Form Returns the lost password form
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public function ChangePasswordForm() {
|
2013-08-21 08:54:05 +02:00
|
|
|
return Object::create('ChangePasswordForm', $this, 'ChangePasswordForm');
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-04-12 23:16:26 +02:00
|
|
|
* 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
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function findAnAdministrator() {
|
2009-12-16 06:43:35 +01:00
|
|
|
// coupling to subsites module
|
2010-05-25 05:45:27 +02:00
|
|
|
$origSubsite = null;
|
|
|
|
if(is_callable('Subsite::changeSubsite')) {
|
|
|
|
$origSubsite = Subsite::currentSubsiteID();
|
|
|
|
Subsite::changeSubsite(0);
|
|
|
|
}
|
|
|
|
|
2009-11-22 06:16:38 +01:00
|
|
|
$member = null;
|
|
|
|
|
2009-12-16 06:43:35 +01:00
|
|
|
// find a group with ADMIN permission
|
2014-11-18 03:36:34 +01:00
|
|
|
$adminGroup = Permission::get_groups_by_permission('ADMIN')->First();
|
2010-05-25 05:45:27 +02:00
|
|
|
|
|
|
|
if(is_callable('Subsite::changeSubsite')) {
|
|
|
|
Subsite::changeSubsite($origSubsite);
|
|
|
|
}
|
2009-11-22 06:16:38 +01:00
|
|
|
|
2009-12-16 06:43:35 +01:00
|
|
|
if ($adminGroup) {
|
2012-12-08 12:20:20 +01:00
|
|
|
$member = $adminGroup->Members()->First();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
if(!$adminGroup) {
|
2010-08-03 03:05:27 +02:00
|
|
|
singleton('Group')->requireDefaultRecords();
|
2014-11-18 03:36:34 +01:00
|
|
|
$adminGroup = Permission::get_groups_by_permission('ADMIN')->First();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 06:16:38 +01:00
|
|
|
if(!$member) {
|
2010-08-03 03:05:27 +02:00
|
|
|
singleton('Member')->requireDefaultRecords();
|
2009-11-22 06:16:38 +01:00
|
|
|
$member = Permission::get_members_by_permission('ADMIN')->First();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
if(!$member) {
|
|
|
|
$member = Member::default_admin();
|
|
|
|
}
|
|
|
|
|
2014-11-18 03:36:34 +01:00
|
|
|
if(!$member) {
|
|
|
|
// Failover to a blank admin
|
|
|
|
$member = Member::create();
|
|
|
|
$member->FirstName = _t('Member.DefaultAdminFirstname', 'Default Admin');
|
|
|
|
$member->write();
|
|
|
|
$member->Groups()->add($adminGroup);
|
|
|
|
}
|
|
|
|
|
2007-09-14 05:12:21 +02:00
|
|
|
return $member;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
/**
|
|
|
|
* Flush the default admin credentials
|
|
|
|
*/
|
|
|
|
public static function clear_default_admin() {
|
|
|
|
self::$default_username = null;
|
|
|
|
self::$default_password = null;
|
|
|
|
}
|
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-10-02 23:57:12 +02:00
|
|
|
* Set a default admin in dev-mode
|
|
|
|
*
|
2010-04-12 23:16:26 +02:00
|
|
|
* This will set a static default-admin which is not existing
|
2007-09-14 21:10:18 +02:00
|
|
|
* as a database-record. By this workaround we can test pages in dev-mode
|
|
|
|
* with a unified login. Submitted login-credentials are first checked
|
2010-04-12 23:16:26 +02:00
|
|
|
* against this static information in {@link Security::authenticate()}.
|
2007-09-14 05:12:21 +02:00
|
|
|
*
|
2007-10-02 23:57:12 +02:00
|
|
|
* @param string $username The user name
|
2010-04-12 23:16:26 +02:00
|
|
|
* @param string $password The password (in cleartext)
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public static function setDefaultAdmin($username, $password) {
|
2007-09-16 19:39:41 +02:00
|
|
|
// don't overwrite if already set
|
2007-10-02 23:57:12 +02:00
|
|
|
if(self::$default_username || self::$default_password) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-16 19:39:41 +02:00
|
|
|
self::$default_username = $username;
|
|
|
|
self::$default_password = $password;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-17 23:51:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2007-09-27 23:13:59 +02:00
|
|
|
public static function check_default_admin($username, $password) {
|
2007-09-17 23:51:42 +02:00
|
|
|
return (
|
2007-10-23 03:18:39 +02:00
|
|
|
self::$default_username === $username
|
|
|
|
&& self::$default_password === $password
|
|
|
|
&& self::has_default_admin()
|
2007-09-17 23:51:42 +02:00
|
|
|
);
|
|
|
|
}
|
2007-10-23 03:18:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check that the default admin account has been set.
|
|
|
|
*/
|
|
|
|
public static function has_default_admin() {
|
2007-10-24 03:15:53 +02:00
|
|
|
return !empty(self::$default_username) && !empty(self::$default_password);
|
2007-10-23 03:18:39 +02:00
|
|
|
}
|
2007-09-14 19:04:11 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Set strict path checking
|
|
|
|
*
|
2007-09-16 02:44:30 +02:00
|
|
|
* This prevents sharing of the session across several sites in the
|
|
|
|
* domain.
|
2007-09-14 05:12:21 +02:00
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.strict_path_checking" config setting instead
|
2007-09-14 21:10:18 +02:00
|
|
|
* @param boolean $strictPathChecking To enable or disable strict patch
|
|
|
|
* checking.
|
2007-07-19 12:40:28 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public static function setStrictPathChecking($strictPathChecking) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.strict_path_checking" config setting instead');
|
|
|
|
self::config()->strict_path_checking = $strictPathChecking;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
|
2007-09-14 19:04:11 +02:00
|
|
|
|
|
|
|
/**
|
2007-09-14 21:10:18 +02:00
|
|
|
* Get strict path checking
|
2007-09-14 19:04:11 +02:00
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.strict_path_checking" config setting instead
|
2007-09-14 21:10:18 +02:00
|
|
|
* @return boolean Status of strict path checking
|
2007-09-14 19:04:11 +02:00
|
|
|
*/
|
2007-09-16 02:32:48 +02:00
|
|
|
public static function getStrictPathChecking() {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.strict_path_checking" config setting instead');
|
|
|
|
return self::config()->strict_path_checking;
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
2007-09-15 23:51:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the password encryption algorithm
|
|
|
|
*
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.password_encryption_algorithm" config setting instead
|
2007-09-15 23:51:37 +02:00
|
|
|
* @param string $algorithm One of the available password encryption
|
2009-11-06 03:23:21 +01:00
|
|
|
* algorithms determined by {@link Security::get_encryption_algorithms()}
|
|
|
|
* @return bool Returns TRUE if the passed algorithm was valid, otherwise FALSE.
|
|
|
|
*/
|
|
|
|
public static function set_password_encryption_algorithm($algorithm) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.password_encryption_algorithm" config setting instead');
|
2009-11-06 03:23:21 +01:00
|
|
|
|
2013-04-27 13:29:13 +02:00
|
|
|
self::config()->password_encryption_algorithm = $algorithm;
|
2007-09-15 23:51:37 +02:00
|
|
|
}
|
2009-11-06 03:23:21 +01:00
|
|
|
|
2007-09-15 23:51:37 +02:00
|
|
|
/**
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.password_encryption_algorithm" config setting instead
|
2009-11-06 03:23:21 +01:00
|
|
|
* @return String
|
2007-09-15 23:51:37 +02:00
|
|
|
*/
|
2009-11-06 03:23:21 +01:00
|
|
|
public static function get_password_encryption_algorithm() {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.password_encryption_algorithm" config setting instead');
|
2013-04-27 13:29:13 +02:00
|
|
|
return self::config()->password_encryption_algorithm;
|
2007-09-15 23:51:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-11-06 03:23:21 +01:00
|
|
|
* Encrypt a password according to the current password encryption settings.
|
2007-09-15 23:51:37 +02:00
|
|
|
* 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
|
2009-11-06 03:23:21 +01:00
|
|
|
* needed, the method will automatically create a
|
|
|
|
* random salt that will then be returned as return value.
|
2007-09-16 16:27:27 +02:00
|
|
|
* @param string $algorithm Optional: Use another algorithm to encrypt the
|
2009-11-06 03:23:21 +01:00
|
|
|
* password (so that the encryption algorithm can be changed over the time).
|
|
|
|
* @param Member $member Optional
|
2007-09-15 23:51:37 +02:00
|
|
|
* @return mixed Returns an associative array containing the encrypted
|
2009-11-06 03:23:21 +01:00
|
|
|
* password and the used salt in the form:
|
|
|
|
* <code>
|
|
|
|
* array(
|
|
|
|
* 'password' => string,
|
|
|
|
* 'salt' => string,
|
|
|
|
* 'algorithm' => string,
|
|
|
|
* 'encryptor' => PasswordEncryptor instance
|
|
|
|
* )
|
|
|
|
* </code>
|
|
|
|
* If the passed algorithm is invalid, FALSE will be returned.
|
2007-09-15 23:51:37 +02:00
|
|
|
*
|
|
|
|
* @see encrypt_passwords()
|
2009-11-06 03:23:21 +01:00
|
|
|
*/
|
2012-09-19 12:07:39 +02:00
|
|
|
public static function encrypt_password($password, $salt = null, $algorithm = null, $member = null) {
|
2013-01-06 21:20:02 +01:00
|
|
|
// Fall back to the default encryption algorithm
|
2013-04-27 13:29:13 +02:00
|
|
|
if(!$algorithm) $algorithm = self::config()->password_encryption_algorithm;
|
2008-03-11 02:30:49 +01:00
|
|
|
|
2009-11-06 03:23:21 +01:00
|
|
|
$e = PasswordEncryptor::create_for_algorithm($algorithm);
|
2007-09-15 23:51:37 +02:00
|
|
|
|
2009-11-06 03:23:13 +01:00
|
|
|
// 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(
|
2010-04-12 07:00:05 +02:00
|
|
|
'password' => $e->encrypt($password, $salt, $member),
|
2009-11-06 03:23:13 +01:00
|
|
|
'salt' => $salt,
|
|
|
|
'algorithm' => $algorithm,
|
|
|
|
'encryptor' => $e
|
|
|
|
);
|
2007-09-15 23:51:37 +02:00
|
|
|
}
|
2007-11-15 23:29:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks the database is in a state to perform security checks.
|
2010-04-12 23:17:07 +02:00
|
|
|
* See {@link DatabaseAdmin->init()} for more information.
|
|
|
|
*
|
2007-11-15 23:29:10 +01:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function database_is_ready() {
|
2010-10-15 05:43:30 +02:00
|
|
|
// Used for unit tests
|
|
|
|
if(self::$force_database_is_ready !== NULL) return self::$force_database_is_ready;
|
2012-04-11 14:29:35 +02:00
|
|
|
|
|
|
|
if(self::$database_is_ready) return self::$database_is_ready;
|
2010-10-15 05:43:30 +02:00
|
|
|
|
2009-01-08 00:00:54 +01:00
|
|
|
$requiredTables = ClassInfo::dataClassesFor('Member');
|
|
|
|
$requiredTables[] = 'Group';
|
|
|
|
$requiredTables[] = 'Permission';
|
|
|
|
|
2010-04-12 23:17:07 +02:00
|
|
|
foreach($requiredTables as $table) {
|
|
|
|
// if any of the tables aren't created in the database
|
|
|
|
if(!ClassInfo::hasTable($table)) return false;
|
2012-03-20 23:53:38 +01:00
|
|
|
|
|
|
|
// HACK: DataExtensions aren't applied until a class is instantiated for
|
|
|
|
// the first time, so create an instance here.
|
|
|
|
singleton($table);
|
2010-04-12 23:17:07 +02:00
|
|
|
|
|
|
|
// if any of the tables don't have all fields mapped as table columns
|
|
|
|
$dbFields = DB::fieldList($table);
|
|
|
|
if(!$dbFields) return false;
|
|
|
|
|
|
|
|
$objFields = DataObject::database_fields($table);
|
|
|
|
$missingFields = array_diff_key($objFields, $dbFields);
|
|
|
|
|
|
|
|
if($missingFields) return false;
|
|
|
|
}
|
2012-04-11 14:29:35 +02:00
|
|
|
self::$database_is_ready = true;
|
2009-01-08 00:00:54 +01:00
|
|
|
|
2010-04-12 23:17:07 +02:00
|
|
|
return true;
|
2007-11-15 23:29:10 +01:00
|
|
|
}
|
2008-08-11 02:14:48 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable or disable recording of login attempts
|
|
|
|
* through the {@link LoginRecord} object.
|
2013-03-21 19:48:54 +01:00
|
|
|
*
|
|
|
|
* @deprecated 3.2 Use the "Security.login_recording" config setting instead
|
2008-08-11 02:14:48 +02:00
|
|
|
* @param boolean $bool
|
|
|
|
*/
|
|
|
|
public static function set_login_recording($bool) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.login_recording" config setting instead');
|
2008-08-11 02:14:48 +02:00
|
|
|
self::$login_recording = (bool)$bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-03-21 19:48:54 +01:00
|
|
|
* @deprecated 3.2 Use the "Security.login_recording" config setting instead
|
2008-08-11 02:14:48 +02:00
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public static function login_recording() {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.login_recording" config setting instead');
|
2008-08-11 02:14:48 +02:00
|
|
|
return self::$login_recording;
|
|
|
|
}
|
2008-09-18 05:53:36 +02:00
|
|
|
|
|
|
|
/**
|
2013-03-21 19:48:54 +01:00
|
|
|
* @config
|
|
|
|
* @var string Set the default login dest
|
2008-09-18 05:53:36 +02:00
|
|
|
* 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.
|
2013-03-21 19:48:54 +01:00
|
|
|
* By default, this is set to the homepage.
|
|
|
|
*/
|
|
|
|
private static $default_login_dest = "";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated 3.2 Use the "Security.default_login_dest" config setting instead
|
2008-09-18 05:53:36 +02:00
|
|
|
*/
|
|
|
|
public static function set_default_login_dest($dest) {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.default_login_dest" config setting instead');
|
|
|
|
self::config()->default_login_dest = $dest;
|
2008-09-18 05:53:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-03-21 19:48:54 +01:00
|
|
|
* Get the default login dest.
|
|
|
|
*
|
|
|
|
* @deprecated 3.2 Use the "Security.default_login_dest" config setting instead
|
2008-09-18 05:53:36 +02:00
|
|
|
*/
|
|
|
|
public static function default_login_dest() {
|
2013-03-21 19:48:54 +01:00
|
|
|
Deprecation::notice('3.2', 'Use the "Security.default_login_dest" config setting instead');
|
|
|
|
return self::config()->default_login_dest;
|
2008-09-18 05:53:36 +02:00
|
|
|
}
|
2007-09-15 23:51:37 +02:00
|
|
|
|
2012-06-15 05:17:32 +02:00
|
|
|
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;
|
|
|
|
}
|
2011-03-16 04:13:14 +01:00
|
|
|
|
2012-06-15 05:17:32 +02:00
|
|
|
public static function ignore_disallowed_actions() {
|
|
|
|
return self::$ignore_disallowed_actions;
|
|
|
|
}
|
|
|
|
|
2013-03-21 19:48:54 +01:00
|
|
|
/** @config */
|
|
|
|
private static $login_url = "Security/login";
|
2011-03-16 04:13:14 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a custom log-in URL if you have built your own log-in page.
|
|
|
|
*/
|
|
|
|
public static function set_login_url($loginUrl) {
|
2013-02-18 15:43:52 +01:00
|
|
|
self::$login_url = $loginUrl;
|
|
|
|
}
|
|
|
|
|
2011-03-16 04:13:14 +01:00
|
|
|
/**
|
|
|
|
* Get the URL of the log-in page.
|
|
|
|
* Defaults to Security/login but can be re-set with {@link set_login_url()}
|
|
|
|
*/
|
|
|
|
public static function login_url() {
|
2013-02-18 15:43:52 +01:00
|
|
|
return self::$login_url;
|
2011-03-16 04:13:14 +01:00
|
|
|
}
|
|
|
|
|
2011-03-25 00:01:50 +01:00
|
|
|
}
|