2014-10-06 05:01:33 +02:00
|
|
|
<?php
|
|
|
|
|
2016-06-23 01:37:22 +02:00
|
|
|
namespace SilverStripe\Security;
|
|
|
|
|
2016-08-11 01:14:02 +02:00
|
|
|
use SilverStripe\Admin\AdminRootController;
|
2016-09-09 08:43:05 +02:00
|
|
|
use SilverStripe\Control\HTTPResponse;
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Core\Convert;
|
|
|
|
use SilverStripe\Control\Director;
|
|
|
|
use SilverStripe\Control\Controller;
|
|
|
|
use SilverStripe\Control\Session;
|
|
|
|
use SilverStripe\ORM\FieldType\DBField;
|
|
|
|
use SilverStripe\View\Requirements;
|
2016-08-11 01:14:02 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
/**
|
|
|
|
* Provides a security interface functionality within the cms
|
|
|
|
*/
|
|
|
|
class CMSSecurity extends Security {
|
|
|
|
|
|
|
|
private static $casting = array(
|
2016-06-03 10:51:02 +02:00
|
|
|
'Title' => 'HTMLFragment'
|
2014-10-06 05:01:33 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
private static $allowed_actions = array(
|
2016-08-11 01:40:23 +02:00
|
|
|
'LoginForm',
|
2014-10-06 05:01:33 +02:00
|
|
|
'success'
|
|
|
|
);
|
|
|
|
|
2014-11-21 05:40:39 +01:00
|
|
|
/**
|
|
|
|
* Enable in-cms reauthentication
|
|
|
|
*
|
|
|
|
* @var boolean
|
|
|
|
* @config
|
|
|
|
*/
|
|
|
|
private static $reauth_enabled = true;
|
|
|
|
|
2015-02-23 14:46:00 +01:00
|
|
|
protected function init() {
|
2014-10-06 05:01:33 +02:00
|
|
|
parent::init();
|
|
|
|
|
2016-09-23 06:13:09 +02:00
|
|
|
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/vendor.js');
|
API Use Webpack
The bundle is generated by running “webpack” directly - gulp is no
longer needed as an intermediary. The resulting config is a lot shorter,
although more configuration is pushed into lib.js.
Modules are shared between javascript files as global variables.
Although this global state pollution is a bit messy, I don’t think it’s
practically any worse than the previous state, and it highlights the
heavy coupling between the different packages we have in place.
Reducing the width of the coupling between the core javascript and
add-on modules would probably be a better way of dealing with this than
replacing global variables with some other kind of global state.
The web pack execution seems roughly twice as fast - if I clear out my
framework/client/dist/js folder, it takes 13.3s to rebuild. However,
it’s not rebuilding other files inside dist, only the bundle files.
CSS files are now included from javascript and incorporated into
bundle.css by the webpack. Although the style-loader is helpful in some
dev workflows (it allows live reload), it introduces a flash of
unstyled content which makes it inappropriate for production.
Instead ExtractTextPlugin is used to write all the aggregated CSS
into a single bundle.css file. A style-loader-based configuration could
be introduced for dev environments, if we make use of the webpack live
reloader in the future.
Note that the following features have been removed as they don't appear to be
necessary when using Webpack:
- UMD module generation
- thirdparty dist file copying
LeftAndMain.js deps: Without it, ssui.core.js gets loaded too late,
which leads e.g. to buttons being initialised without this added behaviour.
2016-08-21 03:17:50 +02:00
|
|
|
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/CMSSecurity.js');
|
2014-10-06 05:01:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function Link($action = null) {
|
2016-06-23 01:37:22 +02:00
|
|
|
/** @skipUpgrade */
|
2014-10-06 05:01:33 +02:00
|
|
|
return Controller::join_links(Director::baseURL(), "CMSSecurity", $action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get known logged out member
|
|
|
|
*
|
|
|
|
* @return Member
|
|
|
|
*/
|
|
|
|
public function getTargetMember() {
|
2015-04-30 01:04:08 +02:00
|
|
|
if($tempid = $this->getRequest()->requestVar('tempid')) {
|
2014-10-06 05:01:33 +02:00
|
|
|
return Member::member_from_tempid($tempid);
|
|
|
|
}
|
2016-08-19 00:51:35 +02:00
|
|
|
return null;
|
2014-10-06 05:01:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getResponseController($title) {
|
|
|
|
// Use $this to prevent use of Page to render underlying templates
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-11-18 23:05:07 +01:00
|
|
|
protected function getLoginMessage(&$messageType = null) {
|
|
|
|
return parent::getLoginMessage($messageType)
|
2014-10-06 05:01:33 +02:00
|
|
|
?: _t(
|
|
|
|
'CMSSecurity.LoginMessage',
|
|
|
|
'<p>If you have any unsaved work you can return to where you left off by logging back in below.</p>'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getTitle() {
|
|
|
|
// Check if logged in already
|
|
|
|
if(Member::currentUserID()) {
|
|
|
|
return _t('CMSSecurity.SUCCESS', 'Success');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display logged-out message
|
|
|
|
$member = $this->getTargetMember();
|
|
|
|
if($member) {
|
|
|
|
return _t(
|
|
|
|
'CMSSecurity.TimedOutTitleMember',
|
|
|
|
'Hey {name}!<br />Your session has timed out.',
|
|
|
|
'Title for CMS popup login form for a known user',
|
|
|
|
array('name' => $member->FirstName)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return _t(
|
|
|
|
'CMSSecurity.TimedOutTitleAnonymous',
|
|
|
|
'Your session has timed out.',
|
|
|
|
'Title for CMS popup login form without a known user'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirects the user to the external login page
|
|
|
|
*
|
2016-09-09 08:43:05 +02:00
|
|
|
* @return HTTPResponse
|
2014-10-06 05:01:33 +02:00
|
|
|
*/
|
|
|
|
protected function redirectToExternalLogin() {
|
|
|
|
$loginURL = Security::create()->Link('login');
|
|
|
|
$loginURLATT = Convert::raw2att($loginURL);
|
|
|
|
$loginURLJS = Convert::raw2js($loginURL);
|
|
|
|
$message = _t(
|
|
|
|
'CMSSecurity.INVALIDUSER',
|
|
|
|
'<p>Invalid user. <a target="_top" href="{link}">Please re-authenticate here</a> to continue.</p>',
|
|
|
|
'Message displayed to user if their session cannot be restored',
|
|
|
|
array('link' => $loginURLATT)
|
|
|
|
);
|
2015-08-14 02:39:33 +02:00
|
|
|
$response = $this->getResponse();
|
|
|
|
$response->setStatusCode(200);
|
|
|
|
$response->setBody(<<<PHP
|
2014-10-06 05:01:33 +02:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html><body>
|
|
|
|
$message
|
2016-01-24 06:12:45 +01:00
|
|
|
<script type="application/javascript">
|
2014-10-06 05:01:33 +02:00
|
|
|
setTimeout(function(){top.location.href = "$loginURLJS";}, 0);
|
|
|
|
</script>
|
|
|
|
</body></html>
|
|
|
|
PHP
|
|
|
|
);
|
2015-08-14 02:39:33 +02:00
|
|
|
$this->setResponse($response);
|
|
|
|
return $response;
|
2014-10-06 05:01:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function preLogin() {
|
|
|
|
// If no member has been previously logged in for this session, force a redirect to the main login page
|
|
|
|
if(!$this->getTargetMember()) {
|
|
|
|
return $this->redirectToExternalLogin();
|
|
|
|
}
|
2015-08-14 02:39:33 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
return parent::preLogin();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function GetLoginForms() {
|
|
|
|
$forms = array();
|
|
|
|
$authenticators = Authenticator::get_authenticators();
|
|
|
|
foreach($authenticators as $authenticator) {
|
|
|
|
// Get only CMS-supporting authenticators
|
|
|
|
if($authenticator::supports_cms()) {
|
|
|
|
$forms[] = $authenticator::get_cms_login_form($this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $forms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if CMSSecurity is enabled
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function enabled() {
|
2014-11-21 05:40:39 +01:00
|
|
|
// Disable shortcut
|
|
|
|
if(!static::config()->reauth_enabled) return false;
|
2015-08-14 02:39:33 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
// Count all cms-supported methods
|
|
|
|
$authenticators = Authenticator::get_authenticators();
|
|
|
|
foreach($authenticators as $authenticator) {
|
|
|
|
// Supported if at least one authenticator is supported
|
|
|
|
if($authenticator::supports_cms()) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function LoginForm() {
|
|
|
|
$authenticator = $this->getAuthenticator();
|
|
|
|
if($authenticator && $authenticator::supports_cms()) {
|
|
|
|
return $authenticator::get_cms_login_form($this);
|
|
|
|
}
|
|
|
|
user_error('Passed invalid authentication method', E_USER_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a successful login, tell the parent frame to close the dialog
|
|
|
|
*
|
2016-09-09 08:43:05 +02:00
|
|
|
* @return HTTPResponse|DBField
|
2014-10-06 05:01:33 +02:00
|
|
|
*/
|
|
|
|
public function success() {
|
|
|
|
// Ensure member is properly logged in
|
|
|
|
if(!Member::currentUserID()) {
|
|
|
|
return $this->redirectToExternalLogin();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get redirect url
|
|
|
|
$controller = $this->getResponseController(_t('CMSSecurity.SUCCESS', 'Success'));
|
2016-04-18 23:02:35 +02:00
|
|
|
$backURLs = array(
|
|
|
|
$this->getRequest()->requestVar('BackURL'),
|
|
|
|
Session::get('BackURL'),
|
|
|
|
Director::absoluteURL(AdminRootController::config()->url_base, true),
|
|
|
|
);
|
2016-08-19 00:51:35 +02:00
|
|
|
$backURL = null;
|
2016-04-18 23:02:35 +02:00
|
|
|
foreach ($backURLs as $backURL) {
|
|
|
|
if ($backURL && Director::is_site_url($backURL)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-10-06 05:01:33 +02:00
|
|
|
|
|
|
|
// Show login
|
|
|
|
$controller = $controller->customise(array(
|
|
|
|
'Content' => _t(
|
|
|
|
'CMSSecurity.SUCCESSCONTENT',
|
|
|
|
'<p>Login success. If you are not automatically redirected '.
|
|
|
|
'<a target="_top" href="{link}">click here</a></p>',
|
|
|
|
'Login message displayed in the cms popup once a user has re-authenticated themselves',
|
|
|
|
array('link' => $backURL)
|
|
|
|
)
|
|
|
|
));
|
2015-08-14 02:39:33 +02:00
|
|
|
|
2014-10-06 05:01:33 +02:00
|
|
|
return $controller->renderWith($this->getTemplatesFor('success'));
|
|
|
|
}
|
|
|
|
}
|