silverstripe-framework/src/Security/CMSSecurity.php

214 lines
5.5 KiB
PHP
Raw Normal View History

<?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;
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
/**
* Provides a security interface functionality within the cms
*/
class CMSSecurity extends Security {
private static $casting = array(
'Title' => 'HTMLFragment'
);
private static $allowed_actions = array(
'LoginForm',
'success'
);
/**
* Enable in-cms reauthentication
*
* @var boolean
* @config
*/
private static $reauth_enabled = true;
protected function init() {
parent::init();
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');
}
public function Link($action = null) {
2016-06-23 01:37:22 +02:00
/** @skipUpgrade */
return Controller::join_links(Director::baseURL(), "CMSSecurity", $action);
}
/**
* Get known logged out member
*
* @return Member
*/
public function getTargetMember() {
if($tempid = $this->getRequest()->requestVar('tempid')) {
return Member::member_from_tempid($tempid);
}
return null;
}
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)
?: _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
*/
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)
);
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setBody(<<<PHP
<!DOCTYPE html>
<html><body>
$message
2016-01-24 06:12:45 +01:00
<script type="application/javascript">
setTimeout(function(){top.location.href = "$loginURLJS";}, 0);
</script>
</body></html>
PHP
);
$this->setResponse($response);
return $response;
}
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();
}
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() {
// Disable shortcut
if(!static::config()->reauth_enabled) return false;
// 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
*/
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'));
$backURLs = array(
$this->getRequest()->requestVar('BackURL'),
Session::get('BackURL'),
Director::absoluteURL(AdminRootController::config()->url_base, true),
);
$backURL = null;
foreach ($backURLs as $backURL) {
if ($backURL && Director::is_site_url($backURL)) {
break;
}
}
// 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)
)
));
return $controller->renderWith($this->getTemplatesFor('success'));
}
}