Merge branch 'bugfix/logout-external-8626'

fixes #8626
This commit is contained in:
Eric Lippmann 2015-03-12 16:47:49 +01:00
commit 29d5fd351b
5 changed files with 94 additions and 96 deletions

View File

@ -102,21 +102,21 @@ class AuthenticationController extends ActionController
$this->view->form->addError( $this->view->form->addError(
$this->translate( $this->translate(
'No authentication methods available. Did you create' 'No authentication methods available. Did you create'
. ' authentication.ini when setting up Icinga Web 2?' . ' authentication.ini when setting up Icinga Web 2?'
) )
); );
} else if ($backendsTried === $backendsWithError) { } else if ($backendsTried === $backendsWithError) {
$this->view->form->addError( $this->view->form->addError(
$this->translate( $this->translate(
'All configured authentication methods failed.' 'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.' . ' Please check the system log or Icinga Web 2 log for more information.'
) )
); );
} elseif ($backendsWithError) { } elseif ($backendsWithError) {
$this->view->form->addError( $this->view->form->addError(
$this->translate( $this->translate(
'Please note that not all authentication methods were available.' 'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.' . ' Check the system log or Icinga Web 2 log for more information.'
) )
); );
} }
@ -144,7 +144,7 @@ class AuthenticationController extends ActionController
$this->view->form->addError($e->getMessage()); $this->view->form->addError($e->getMessage());
} }
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated(); $this->view->requiresExternalAuth = $triedOnlyExternalAuth && ! $auth->isAuthenticated();
$this->view->requiresSetup = Icinga::app()->requiresSetup(); $this->view->requiresSetup = Icinga::app()->requiresSetup();
} }

View File

@ -9,39 +9,39 @@
--> -->
<div class="content"> <div class="content">
<div class="alert alert-warning" id="logout-status"> <div class="alert alert-warning" id="logout-status">
<b> <?= t('Logging out...'); ?> </b> <br /> <b><?= $this->translate('Logging out...'); ?></b>
<?= t( <br>
'If this message does not disappear, it might be necessary to quit the ' . <?= $this->translate(
'current session manually by clearing the cache, or by closing the current ' . 'If this message does not disappear, it might be necessary to quit the'
'browser session.' . ' current session manually by clearing the cache, or by closing the current'
. ' browser session.'
); ?> ); ?>
</div> </div>
<div class="container" > <div class="container">
<a href="<?= $this->href('dashboard/index'); ?>"> <?= t('Login'); ?></a> <a href="<?= $this->href('dashboard/index?renderLayout'); ?>"><?= $this->translate('Login'); ?></a>
</div> </div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
/** /*
* When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it * When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it
* before it is able to finish. This will cause the browser to show a new authentication prompt in the next * before it is able to finish. This will cause the browser to show a new authentication prompt in the next
* request. * request.
*/ */
$(document).ready(function() { document.addEventListener('DOMContentLoaded', function () {
msg = $('#logout-status'); var msg = document.getElementById('logout-status');
try { try {
if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) { if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
document.execCommand('ClearAuthenticationCache'); document.execCommand('ClearAuthenticationCache');
} else { } else {
var xhttp = getXMLHttpRequest(); var xhttp = new XMLHttpRequest();
xhttp.open('GET', 'arbitrary url', true, 'logout', 'logout'); xhttp.open('GET', 'arbitrary url', true, 'logout', 'logout');
xhttp.send(''); xhttp.send('');
xhttp.abort(); xhttp.abort();
} }
} catch (e) { } catch (e) {
} }
msg.html('<?= t('Logout successful!'); ?>'); msg.innerHTML = '<?= $this->translate('Logout successful!'); ?>';
msg.removeClass(); msg.className = 'alert alert-success';
msg.addClass('alert alert-success');
}); });
</script> </script>

View File

@ -5,28 +5,22 @@ namespace Icinga\Application;
require_once __DIR__ . '/ApplicationBootstrap.php'; require_once __DIR__ . '/ApplicationBootstrap.php';
use Icinga\Authentication\Manager as AuthenticationManager; use Zend_Controller_Action_HelperBroker;
use Icinga\Authentication\Manager; use Zend_Controller_Front;
use Icinga\Exception\ConfigurationError; use Zend_Controller_Router_Route;
use Icinga\Exception\NotReadableError;
use Icinga\Application\Logger;
use Icinga\Util\TimezoneDetect;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\View;
use Icinga\Web\Session\Session as BaseSession;
use Icinga\Web\Session;
use Icinga\User;
use Icinga\Util\Translator;
use Icinga\Util\DateTimeFactory;
use DateTimeZone;
use Exception;
use Zend_Layout; use Zend_Layout;
use Zend_Paginator; use Zend_Paginator;
use Zend_View_Helper_PaginationControl; use Zend_View_Helper_PaginationControl;
use Zend_Controller_Action_HelperBroker as ActionHelperBroker; use Icinga\Application\Logger;
use Zend_Controller_Router_Route; use Icinga\Authentication\Manager;
use Zend_Controller_Front; use Icinga\User;
use Icinga\Util\TimezoneDetect;
use Icinga\Util\Translator;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\Session;
use Icinga\Web\Session\Session as BaseSession;
use Icinga\Web\View;
/** /**
* Use this if you want to make use of Icinga functionality in other web projects * Use this if you want to make use of Icinga functionality in other web projects
@ -84,7 +78,7 @@ class Web extends ApplicationBootstrap
/** /**
* Initialize all together * Initialize all together
* *
* @return self * @return $this
*/ */
protected function bootstrap() protected function bootstrap()
{ {
@ -112,7 +106,7 @@ class Web extends ApplicationBootstrap
/** /**
* Prepare routing * Prepare routing
* *
* @return self * @return $this
*/ */
private function setupRoute() private function setupRoute()
{ {
@ -161,44 +155,39 @@ class Web extends ApplicationBootstrap
/** /**
* Prepare Zend MVC Base * Prepare Zend MVC Base
* *
* @return self * @return $this
*/ */
private function setupZendMvc() private function setupZendMvc()
{ {
// TODO: Replace Zend_Application:
Zend_Layout::startMvc( Zend_Layout::startMvc(
array( array(
'layout' => 'layout', 'layout' => 'layout',
'layoutPath' => $this->getApplicationDir('/layouts/scripts') 'layoutPath' => $this->getApplicationDir('/layouts/scripts')
) )
); );
$this->setupFrontController(); $this->setupFrontController();
$this->setupViewRenderer(); $this->setupViewRenderer();
return $this; return $this;
} }
/** /**
* Create user object * Create user object
* *
* @return self * @return $this
*/ */
private function setupUser() private function setupUser()
{ {
$authenticationManager = AuthenticationManager::getInstance(); $auth = Manager::getInstance();
if ($auth->isAuthenticated()) {
if ($authenticationManager->isAuthenticated() === true) { $this->user = $auth->getUser();
$this->user = $authenticationManager->getUser();
} }
return $this; return $this;
} }
/** /**
* Initialize a session provider * Initialize a session provider
* *
* @return self * @return $this
*/ */
private function setupSession() private function setupSession()
{ {
@ -209,82 +198,68 @@ class Web extends ApplicationBootstrap
/** /**
* Inject dependencies into request * Inject dependencies into request
* *
* @return self * @return $this
*/ */
private function setupRequest() private function setupRequest()
{ {
$this->request = new Request(); $this->request = new Request();
if ($this->user instanceof User) { if ($this->user instanceof User) {
$this->request->setUser($this->user); $this->request->setUser($this->user);
} }
return $this; return $this;
} }
/** /**
* Instantiate front controller * Instantiate front controller
* *
* @return self * @return $this
*/ */
private function setupFrontController() private function setupFrontController()
{ {
$this->frontController = Zend_Controller_Front::getInstance(); $this->frontController = Zend_Controller_Front::getInstance();
$this->frontController->setRequest($this->request); $this->frontController->setRequest($this->request);
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers')); $this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
$this->frontController->setParams( $this->frontController->setParams(
array( array(
'displayExceptions' => true 'displayExceptions' => true
) )
); );
return $this; return $this;
} }
/** /**
* Register helper paths and views for renderer * Register helper paths and views for renderer
* *
* @return self * @return $this
*/ */
private function setupViewRenderer() private function setupViewRenderer()
{ {
$view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
/** @var \Zend_Controller_Action_Helper_ViewRenderer $view */ /** @var \Zend_Controller_Action_Helper_ViewRenderer $view */
$view = ActionHelperBroker::getStaticHelper('viewRenderer');
$view->setView(new View()); $view->setView(new View());
$view->view->addHelperPath($this->getApplicationDir('/views/helpers')); $view->view->addHelperPath($this->getApplicationDir('/views/helpers'));
$view->view->setEncoding('UTF-8'); $view->view->setEncoding('UTF-8');
$view->view->headTitle()->prepend($this->config->get('global', 'project', 'Icinga')); $view->view->headTitle()->prepend($this->config->get('global', 'project', 'Icinga'));
$view->view->headTitle()->setSeparator(' :: '); $view->view->headTitle()->setSeparator(' :: ');
$this->viewRenderer = $view; $this->viewRenderer = $view;
return $this; return $this;
} }
/** /**
* Configure pagination settings * Configure pagination settings
* *
* @return self * @return $this
*/ */
private function setupPagination() private function setupPagination()
{ {
Zend_Paginator::addScrollingStylePrefixPath( Zend_Paginator::addScrollingStylePrefixPath(
'Icinga_Web_Paginator_ScrollingStyle', 'Icinga_Web_Paginator_ScrollingStyle',
'Icinga/Web/Paginator/ScrollingStyle' 'Icinga/Web/Paginator/ScrollingStyle'
); );
Zend_Paginator::setDefaultScrollingStyle('SlidingWithBorder'); Zend_Paginator::setDefaultScrollingStyle('SlidingWithBorder');
Zend_View_Helper_PaginationControl::setDefaultViewPartial( Zend_View_Helper_PaginationControl::setDefaultViewPartial(
array('mixedPagination.phtml', 'default') array('mixedPagination.phtml', 'default')
); );
return $this; return $this;
} }
@ -330,7 +305,7 @@ class Web extends ApplicationBootstrap
/** /**
* Setup an autoloader namespace for Icinga\Forms * Setup an autoloader namespace for Icinga\Forms
* *
* @return self * @return $this
*/ */
private function setupFormNamespace() private function setupFormNamespace()
{ {
@ -341,4 +316,3 @@ class Web extends ApplicationBootstrap
return $this; return $this;
} }
} }
// @codeCoverageIgnoreEnd

View File

@ -293,34 +293,31 @@ class ActionController extends Zend_Controller_Action
} }
/** /**
* Redirect to the login path * Redirect to login
* *
* @param Url $afterLogin The action to call when the login was successful. Defaults to '/index/welcome' * XHR will always redirect to __SELF__ if an URL to redirect to after successful login is set. __SELF__ instructs
* JavaScript to redirect to the current window's URL if it's an auto-refresh request or to redirect to the URL
* which required login if it's not an auto-refreshing one.
* *
* @throws \Exception * XHR will respond with HTTP status code 403 Forbidden.
*
* @param Url|string $redirect URL to redirect to after successful login
*/ */
protected function redirectToLogin($afterLogin = null) protected function redirectToLogin($redirect = null)
{ {
$redir = null; $login = Url::fromPath('authentication/login');
if ($afterLogin !== null) { if ($this->isXhr()) {
if (! $afterLogin instanceof Url) { if ($redirect !== null) {
$afterLogin = Url::fromPath($afterLogin); $login->setParam('redirect', '__SELF__');
} }
if ($this->isXhr()) { $this->_response->setHttpResponseCode(403);
$redir = '__SELF__'; } elseif ($redirect !== null) {
} else { if (! $redirect instanceof Url) {
// TODO: Ignore /? $redirect = Url::fromPath($redirect);
$redir = $afterLogin->getRelativeUrl();
} }
$login->setParam('redirect', $redirect->getRelativeUrl());
} }
$this->rerenderLayout()->redirectNow($login);
$url = Url::fromPath('authentication/login');
if ($redir) {
$url->setParam('redirect', $redir);
}
$this->rerenderLayout()->redirectNow($url);
} }
protected function rerenderLayout() protected function rerenderLayout()

View File

@ -245,14 +245,41 @@
} }
}, },
/**
* Process the X-Icinga-Redirect HTTP Response Header
*
* If the response includes the X-Icinga-Redirect header, redirects to the URL associated with the header.
*
* @param {object} req Current request
*
* @returns {boolean} Whether we're about to redirect
*/
processRedirectHeader: function(req) { processRedirectHeader: function(req) {
var icinga = this.icinga; var icinga = this.icinga,
var redirect = req.getResponseHeader('X-Icinga-Redirect'); redirect = req.getResponseHeader('X-Icinga-Redirect');
if (! redirect) return false;
if (! redirect) {
return false;
}
redirect = decodeURIComponent(redirect); redirect = decodeURIComponent(redirect);
if (redirect.match(/__SELF__/)) { if (redirect.match(/__SELF__/)) {
redirect = redirect.replace(/__SELF__/, encodeURIComponent(document.location.pathname + document.location.search + document.location.hash)); if (req.autorefresh) {
// Redirect to the current window's URL in case it's an auto-refresh request. If authenticated
// externally this ensures seamless re-login if the session's expired
redirect = redirect.replace(
/__SELF__/,
encodeURIComponent(
document.location.pathname + document.location.search + document.location.hash
)
);
} else {
// Redirect to the URL which required authentication. When clicking a link this ensures that we
// redirect to the link's URL instead of the current window's URL (see above)
redirect = redirect.replace(/__SELF__/, req.url);
}
} }
icinga.logger.debug( icinga.logger.debug(
'Got redirect for ', req.$target, ', URL was ' + redirect 'Got redirect for ', req.$target, ', URL was ' + redirect
); );