Merge branch 'feature/basic-auth-9660'

resolves #9660
This commit is contained in:
Eric Lippmann 2015-07-30 15:05:07 +02:00
commit a234852f32
30 changed files with 957 additions and 407 deletions

View File

@ -6,20 +6,14 @@
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\AuthChain;
use Icinga\Authentication\User\ExternalBackend;
use Icinga\Exception\AuthenticationException;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Forms\Authentication\LoginForm; use Icinga\Forms\Authentication\LoginForm;
use Icinga\User; use Icinga\Web\Controller;
use Icinga\Web\Controller\ActionController;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
* Application wide controller for authentication * Application wide controller for authentication
*/ */
class AuthenticationController extends ActionController class AuthenticationController extends Controller
{ {
/** /**
* This controller does not require authentication * This controller does not require authentication
@ -34,118 +28,19 @@ class AuthenticationController extends ActionController
public function loginAction() public function loginAction()
{ {
$icinga = Icinga::app(); $icinga = Icinga::app();
if ($icinga->setupTokenExists() && $icinga->requiresSetup()) { if (($requiresSetup = $icinga->requiresSetup()) && $icinga->setupTokenExists()) {
$this->redirectNow(Url::fromPath('setup')); $this->redirectNow(Url::fromPath('setup'));
} }
$form = new LoginForm();
$triedOnlyExternalAuth = null; if ($this->Auth()->isAuthenticated()) {
$auth = $this->Auth(); $this->redirectNow($form->getRedirectUrl());
$this->view->form = $form = new LoginForm();
$this->view->title = $this->translate('Icingaweb Login');
try {
$redirectUrl = $this->view->form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
} }
if (! $requiresSetup) {
if ($auth->isAuthenticated()) { $form->handleRequest();
$this->rerenderLayout()->redirectNow($redirectUrl);
} }
$this->view->form = $form;
try { $this->view->title = $this->translate('Icinga Web 2 Login');
$config = Config::app('authentication'); $this->view->requiresSetup = $requiresSetup;
} catch (NotReadableError $e) {
throw new ConfigurationError(
$this->translate('Could not read your authentication.ini, no authentication methods are available.'),
0,
$e
);
}
$chain = new AuthChain($config);
$request = $this->getRequest();
if ($request->isPost() && $this->view->form->isValid($request->getPost())) {
$user = new User($this->view->form->getValue('username'));
$password = $this->view->form->getValue('password');
$backendsTried = 0;
$backendsWithError = 0;
$redirectUrl = $form->getValue('redirect');
if ($redirectUrl) {
$redirectUrl = Url::fromPath($redirectUrl);
} else {
$redirectUrl = Url::fromPath('dashboard');
}
foreach ($chain as $backend) {
if ($backend instanceof ExternalBackend) {
continue;
}
++$backendsTried;
try {
$authenticated = $backend->authenticate($user, $password);
} catch (AuthenticationException $e) {
Logger::error($e);
++$backendsWithError;
continue;
}
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow($redirectUrl);
}
}
if ($backendsTried === 0) {
$this->view->form->addError(
$this->translate(
'No authentication methods available. Did you create'
. ' authentication.ini when setting up Icinga Web 2?'
)
);
} else if ($backendsTried === $backendsWithError) {
$this->view->form->addError(
$this->translate(
'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.'
)
);
} elseif ($backendsWithError) {
$this->view->form->addError(
$this->translate(
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
)
);
}
if ($backendsTried > 0 && $backendsTried !== $backendsWithError) {
$this->view->form->getElement('password')->addError($this->translate('Incorrect username or password'));
}
} elseif ($request->isGet()) {
$user = new User('');
foreach ($chain as $backend) {
$triedOnlyExternalAuth = $triedOnlyExternalAuth === null;
if ($backend instanceof ExternalBackend) {
$authenticated = $backend->authenticate($user);
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->rerenderLayout()->redirectNow(
Url::fromPath(Url::fromRequest()->getParam('redirect', 'dashboard'))
);
}
} else {
$triedOnlyExternalAuth = false;
}
}
}
} catch (Exception $e) {
$this->view->form->addError($e->getMessage());
}
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && ! $auth->isAuthenticated();
$this->view->requiresSetup = Icinga::app()->requiresSetup();
} }
/** /**
@ -157,10 +52,12 @@ class AuthenticationController extends ActionController
if (! $auth->isAuthenticated()) { if (! $auth->isAuthenticated()) {
$this->redirectToLogin(); $this->redirectToLogin();
} }
$isRemoteUser = $auth->getUser()->isRemoteUser(); // Get info whether the user is externally authenticated before removing authorization which destroys the
// session and the user object
$isExternalUser = $auth->getUser()->isExternalUser();
$auth->removeAuthorization(); $auth->removeAuthorization();
if ($isRemoteUser === true) { if ($isExternalUser) {
$this->_response->setHttpResponseCode(401); $this->getResponse()->setHttpResponseCode(401);
} else { } else {
$this->redirectToLogin(); $this->redirectToLogin();
} }

View File

@ -3,16 +3,24 @@
namespace Icinga\Forms\Authentication; namespace Icinga\Forms\Authentication;
use Icinga\Authentication\Auth;
use Icinga\Authentication\User\ExternalBackend;
use Icinga\User;
use Icinga\Web\Form; use Icinga\Web\Form;
use Icinga\Web\Url; use Icinga\Web\Url;
/** /**
* Class LoginForm * Form for user authentication
*/ */
class LoginForm extends Form class LoginForm extends Form
{ {
/** /**
* Initialize this login form * Redirect URL
*/
const REDIRECT_URL = 'dashboard';
/**
* {@inheritdoc}
*/ */
public function init() public function init()
{ {
@ -22,7 +30,7 @@ class LoginForm extends Form
} }
/** /**
* @see Form::createElements() * {@inheritdoc}
*/ */
public function createElements(array $formData) public function createElements(array $formData)
{ {
@ -54,4 +62,83 @@ class LoginForm extends Form
) )
); );
} }
/**
* {@inheritdoc}
*/
public function getRedirectUrl()
{
$redirect = null;
if ($this->created) {
$redirect = $this->getElement('redirect')->getValue();
}
if (empty($redirect)) {
$redirect = static::REDIRECT_URL;
}
return Url::fromPath($redirect);
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
$auth = Auth::getInstance();
$authChain = $auth->getAuthChain();
$authChain->setSkipExternalBackends(true);
$user = new User($this->getElement('username')->getValue());
$password = $this->getElement('password')->getValue();
$authenticated = $authChain->authenticate($user, $password);
if ($authenticated) {
$auth->setAuthenticated($user);
$this->getResponse()->setRerenderLayout(true);
return true;
}
switch ($authChain->getError()) {
case $authChain::EEMPTY:
$this->addError($this->translate(
'No authentication methods available.'
. ' Did you create authentication.ini when setting up Icinga Web 2?'
));
break;
case $authChain::EFAIL:
$this->addError($this->translate(
'All configured authentication methods failed.'
. ' Please check the system log or Icinga Web 2 log for more information.'
));
break;
/** @noinspection PhpMissingBreakStatementInspection */
case $authChain::ENOTALL:
$this->addError($this->translate(
'Please note that not all authentication methods were available.'
. ' Check the system log or Icinga Web 2 log for more information.'
));
// Move to default
default:
$this->getElement('password')->addError($this->translate('Incorrect username or password'));
break;
}
return false;
}
/**
* {@inheritdoc}
*/
public function onRequest()
{
$auth = Auth::getInstance();
$onlyExternal = true;
// TODO(el): This may be set on the auth chain once iterated. See Auth::authExternal().
foreach ($auth->getAuthChain() as $backend) {
if (! $backend instanceof ExternalBackend) {
$onlyExternal = false;
}
}
if ($onlyExternal) {
$this->addError($this->translate(
'You\'re currently not authenticated using any of the web server\'s authentication mechanisms.'
. ' Make sure you\'ll configure such, otherwise you\'ll not be able to login.'
));
}
}
} }

View File

@ -6,7 +6,7 @@ namespace Icinga\Forms;
use Exception; use Exception;
use DateTimeZone; use DateTimeZone;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\User\Preferences; use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore; use Icinga\User\Preferences\PreferencesStore;
use Icinga\Util\TimezoneDetect; use Icinga\Util\TimezoneDetect;
@ -123,7 +123,7 @@ class PreferenceForm extends Form
*/ */
public function onRequest() public function onRequest()
{ {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
$values = $auth->getUser()->getPreferences()->get('icingaweb'); $values = $auth->getUser()->getPreferences()->get('icingaweb');
if (! isset($values['language'])) { if (! isset($values['language'])) {

View File

@ -2,7 +2,7 @@
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Authentication\Manager as Auth; use Icinga\Authentication\Auth;
if (Auth::getInstance()->isAuthenticated()): if (Auth::getInstance()->isAuthenticated()):
@ -51,7 +51,7 @@ if (Auth::getInstance()->isAuthenticated()):
<ul id="notifications"><?php <ul id="notifications"><?php
$notifications = Notification::getInstance(); $notifications = Notification::getInstance();
if ($notifications->hasMessages()) { if ($notifications->hasMessages()) {
foreach ($notifications->getMessages() as $m) { foreach ($notifications->popMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>'; echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
} }
} }

View File

@ -17,11 +17,6 @@
'<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">', '<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
'</a>' '</a>'
); ?></p> ); ?></p>
<?php elseif ($requiresExternalAuth): ?>
<p class="info-box"><?= $this->icon('info'); ?><?= $this->translate(
'You\'re currently not authenticated using any of the web server\'s authentication mechanisms.'
. ' Make sure you\'ll configure such, otherwise you\'ll not be able to login.'
); ?></p>
<?php endif ?> <?php endif ?>
<?= $this->form ?> <?= $this->form ?>
<div class="footer">Icinga Web 2 &copy; 2013-2015<br><a href="https://www.icinga.org"><?= $this->translate('The Icinga Project'); ?></a></div> <div class="footer">Icinga Web 2 &copy; 2013-2015<br><a href="https://www.icinga.org"><?= $this->translate('The Icinga Project'); ?></a></div>

View File

@ -12,7 +12,7 @@ use Zend_Layout;
use Zend_Paginator; use Zend_Paginator;
use Zend_View_Helper_PaginationControl; use Zend_View_Helper_PaginationControl;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\User; use Icinga\User;
use Icinga\Util\TimezoneDetect; use Icinga\Util\TimezoneDetect;
use Icinga\Util\Translator; use Icinga\Util\Translator;
@ -55,6 +55,13 @@ class Web extends ApplicationBootstrap
*/ */
private $request; private $request;
/**
* Response
*
* @var Response
*/
protected $response;
/** /**
* Session object * Session object
* *
@ -91,11 +98,12 @@ class Web extends ApplicationBootstrap
->setupResourceFactory() ->setupResourceFactory()
->setupSession() ->setupSession()
->setupNotifications() ->setupNotifications()
->setupRequest()
->setupResponse()
->setupUser() ->setupUser()
->setupTimezone() ->setupTimezone()
->setupLogger() ->setupLogger()
->setupInternationalization() ->setupInternationalization()
->setupRequest()
->setupZendMvc() ->setupZendMvc()
->setupFormNamespace() ->setupFormNamespace()
->setupModuleManager() ->setupModuleManager()
@ -136,6 +144,26 @@ class Web extends ApplicationBootstrap
return $this->frontController; return $this->frontController;
} }
/**
* Get the request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the response
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/** /**
* Getter for view * Getter for view
* *
@ -151,7 +179,7 @@ class Web extends ApplicationBootstrap
*/ */
public function dispatch() public function dispatch()
{ {
$this->frontController->dispatch($this->request, new Response()); $this->frontController->dispatch($this->getRequest(), $this->getResponse());
} }
/** /**
@ -179,9 +207,11 @@ class Web extends ApplicationBootstrap
*/ */
private function setupUser() private function setupUser()
{ {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$this->user = $auth->getUser(); $user = $auth->getUser();
$this->request->setUser($user);
$this->user = $user;
} }
return $this; return $this;
} }
@ -209,16 +239,24 @@ class Web extends ApplicationBootstrap
} }
/** /**
* Inject dependencies into request * Set the request
* *
* @return $this * @return $this
*/ */
private function setupRequest() private function setupRequest()
{ {
$this->request = new Request(); $this->request = new Request();
if ($this->user instanceof User) { return $this;
$this->request->setUser($this->user);
} }
/**
* Set the response
*
* @return $this
*/
protected function setupResponse()
{
$this->response = new Response();
return $this; return $this;
} }
@ -282,7 +320,7 @@ class Web extends ApplicationBootstrap
*/ */
protected function detectTimezone() protected function detectTimezone()
{ {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
if (! $auth->isAuthenticated() if (! $auth->isAuthenticated()
|| ($timezone = $auth->getUser()->getPreferences()->getValue('icingaweb', 'timezone')) === null || ($timezone = $auth->getUser()->getPreferences()->getValue('icingaweb', 'timezone')) === null
) { ) {
@ -303,7 +341,7 @@ class Web extends ApplicationBootstrap
*/ */
protected function detectLocale() protected function detectLocale()
{ {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
if ($auth->isAuthenticated() if ($auth->isAuthenticated()
&& ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) !== null && ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) !== null
) { ) {

View File

@ -4,18 +4,20 @@
namespace Icinga\Authentication; namespace Icinga\Authentication;
use Exception; use Exception;
use Icinga\Authentication\UserGroup\UserGroupBackend;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Authentication\User\ExternalBackend;
use Icinga\Authentication\UserGroup\UserGroupBackend;
use Icinga\Data\ConfigObject; use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException; use Icinga\Exception\IcingaException;
use Icinga\Exception\NotReadableError; use Icinga\Exception\NotReadableError;
use Icinga\Application\Logger;
use Icinga\User; use Icinga\User;
use Icinga\User\Preferences; use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore; use Icinga\User\Preferences\PreferencesStore;
use Icinga\Web\Session; use Icinga\Web\Session;
class Manager class Auth
{ {
/** /**
* Singleton instance * Singleton instance
@ -24,6 +26,20 @@ class Manager
*/ */
private static $instance; private static $instance;
/**
* Request
*
* @var \Icinga\Web\Request
*/
protected $request;
/**
* Response
*
* @var \Icinga\Web\Response
*/
protected $response;
/** /**
* Authenticated user * Authenticated user
* *
@ -32,6 +48,9 @@ class Manager
private $user; private $user;
/**
* @see getInstance()
*/
private function __construct() private function __construct()
{ {
} }
@ -44,11 +63,39 @@ class Manager
public static function getInstance() public static function getInstance()
{ {
if (self::$instance === null) { if (self::$instance === null) {
self::$instance = new static(); self::$instance = new self();
} }
return self::$instance; return self::$instance;
} }
/**
* Get the auth chain
*
* @return AuthChain
*/
public function getAuthChain()
{
return new AuthChain();
}
/**
* Whether the user is authenticated
*
* @param bool $ignoreSession True to prevent session authentication
*
* @return bool
*/
public function isAuthenticated($ignoreSession = false)
{
if ($this->user === null && ! $ignoreSession) {
$this->authenticateFromSession();
}
if ($this->user === null && ! $this->authExternal()) {
return $this->authHttp();
}
return true;
}
public function setAuthenticated(User $user, $persist = true) public function setAuthenticated(User $user, $persist = true)
{ {
$username = $user->getUsername(); $username = $user->getUsername();
@ -121,58 +168,40 @@ class Manager
} }
/** /**
* Writes the current user to the session * Getter for groups belonged to authenticated user
*
* @return array
* @see User::getGroups
*/ */
public function persistCurrentUser() public function getGroups()
{ {
Session::getSession()->set('user', $this->user)->refreshId(); return $this->user->getGroups();
} }
/** /**
* Try to authenticate the user with the current session * Get the request
* *
* Authentication for externally-authenticated users will be revoked if the username changed or external * @return \Icinga\Web\Request
* authentication is no longer in effect
*/ */
public function authenticateFromSession() public function getRequest()
{ {
$this->user = Session::getSession()->get('user'); if ($this->request === null) {
if ($this->user !== null && $this->user->isRemoteUser() === true) { $this->request = Icinga::app()->getRequest();
list($originUsername, $field) = $this->user->getRemoteUserInformation();
if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization();
}
} }
return $this->request;
} }
/** /**
* Whether the user is authenticated * Get the response
* *
* @param bool $ignoreSession True to prevent session authentication * @return \Icinga\Web\Response
*
* @return bool
*/ */
public function isAuthenticated($ignoreSession = false) public function getResponse()
{ {
if ($this->user === null && ! $ignoreSession) { if ($this->response === null) {
$this->authenticateFromSession(); $this->response = Icinga::app()->getResponse();
} }
return is_object($this->user); return $this->response;
}
/**
* Whether an authenticated user has a given permission
*
* @param string $permission Permission name
*
* @return bool True if the user owns the given permission, false if not or if not authenticated
*/
public function hasPermission($permission)
{
if (! $this->isAuthenticated()) {
return false;
}
return $this->user->can($permission);
} }
/** /**
@ -192,15 +221,6 @@ class Manager
return $this->user->getRestrictions($restriction); return $this->user->getRestrictions($restriction);
} }
/**
* Purges the current authorization information and session
*/
public function removeAuthorization()
{
$this->user = null;
Session::getSession()->purge();
}
/** /**
* Returns the current user or null if no user is authenticated * Returns the current user or null if no user is authenticated
* *
@ -212,13 +232,124 @@ class Manager
} }
/** /**
* Getter for groups belonged to authenticated user * Try to authenticate the user with the current session
* *
* @return array * Authentication for externally-authenticated users will be revoked if the username changed or external
* @see User::getGroups * authentication is no longer in effect
*/ */
public function getGroups() public function authenticateFromSession()
{ {
return $this->user->getGroups(); $this->user = Session::getSession()->get('user');
if ($this->user !== null && $this->user->isExternalUser() === true) {
list($originUsername, $field) = $this->user->getExternalUserInformation();
if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization();
}
}
}
/**
* Attempt to authenticate a user from external user backends
*
* @return bool
*/
protected function authExternal()
{
$user = new User('');
foreach ($this->getAuthChain() as $userBackend) {
if ($userBackend instanceof ExternalBackend) {
if ($userBackend->authenticate($user)) {
$this->setAuthenticated($user);
return true;
}
}
}
return false;
}
/**
* Attempt to authenticate a user using HTTP authentication
*
* Supports only the Basic HTTP authentication scheme. XHR will be ignored.
*
* @return bool
*/
protected function authHttp()
{
if ($this->getRequest()->isXmlHttpRequest()) {
return false;
}
if (($header = $this->getRequest()->getHeader('Authorization')) === false) {
return false;
}
if (empty($header)) {
$this->challengeHttp();
}
list($scheme) = explode(' ', $header, 2);
if ($scheme !== 'Basic') {
$this->challengeHttp();
}
$authorization = substr($header, strlen('Basic '));
$credentials = base64_decode($authorization);
$credentials = array_filter(explode(':', $credentials, 2));
if (count($credentials) !== 2) {
// Deny empty username and/or password
$this->challengeHttp();
}
$user = new User($credentials[0]);
$password = $credentials[1];
if ($this->getAuthChain()->setSkipExternalBackends(true)->authenticate($user, $password)) {
$this->setAuthenticated($user, false);
$user->setIsHttpUser(true);
return true;
} else {
$this->challengeHttp();
}
}
/**
* Challenge client immediately for HTTP authentication
*
* Sends the response w/ the 401 Unauthorized status code and WWW-Authenticate header.
*/
protected function challengeHttp()
{
$response = $this->getResponse();
$response->setHttpResponseCode(401);
$response->setHeader('WWW-Authenticate', 'Basic realm="Icinga Web 2"');
$response->sendHeaders();
exit();
}
/**
* Whether an authenticated user has a given permission
*
* @param string $permission Permission name
*
* @return bool True if the user owns the given permission, false if not or if not authenticated
*/
public function hasPermission($permission)
{
if (! $this->isAuthenticated()) {
return false;
}
return $this->user->can($permission);
}
/**
* Writes the current user to the session
*/
public function persistCurrentUser()
{
Session::getSession()->set('user', $this->user)->refreshId();
}
/**
* Purges the current authorization information and session
*/
public function removeAuthorization()
{
$this->user = null;
Session::getSession()->purge();
} }
} }

View File

@ -4,41 +4,175 @@
namespace Icinga\Authentication; namespace Icinga\Authentication;
use Iterator; use Iterator;
use Icinga\Data\ConfigObject;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\User\UserBackendInterface;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\User\ExternalBackend;
use Icinga\Authentication\User\UserBackend;
use Icinga\Authentication\User\UserBackendInterface;
use Icinga\Data\ConfigObject;
use Icinga\Exception\AuthenticationException;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\User;
/** /**
* Iterate user backends created from config * Iterate user backends created from config
*/ */
class AuthChain implements Iterator class AuthChain implements Authenticatable, Iterator
{ {
/**
* Authentication config file
*
* @var string
*/
const AUTHENTICATION_CONFIG = 'authentication';
/**
* Error code if the authentication configuration was not readable
*
* @var int
*/
const EPERM = 1;
/**
* Error code if the authentication configuration is empty
*/
const EEMPTY = 2;
/**
* Error code if all authentication methods failed
*
* @var int
*/
const EFAIL = 3;
/**
* Error code if not all authentication methods were available
*
* @var int
*/
const ENOTALL = 4;
/** /**
* User backends configuration * User backends configuration
* *
* @var Config * @var Config
*/ */
private $config; protected $config;
/** /**
* The consecutive user backend while looping * The consecutive user backend while looping
* *
* @var UserBackendInterface * @var UserBackendInterface
*/ */
private $currentBackend; protected $currentBackend;
/**
* Last error code
*
* @var int|null
*/
protected $error;
/**
* Whether external user backends should be skipped on iteration
*
* @var bool
*/
protected $skipExternalBackends = false;
/** /**
* Create a new authentication chain from config * Create a new authentication chain from config
* *
* @param Config $config User backends configuration * @param Config $config User backends configuration
*/ */
public function __construct(Config $config) public function __construct(Config $config = null)
{ {
if ($config === null) {
try {
$this->config = Config::app(static::AUTHENTICATION_CONFIG);
} catch (NotReadableError $e) {
$this->config = new Config();
$this->error = static::EPERM;
}
} else {
$this->config = $config; $this->config = $config;
} }
}
/**
* {@inheritdoc}
*/
public function authenticate(User $user, $password)
{
$this->error = null;
$backendsTried = 0;
$backendsWithError = 0;
foreach ($this as $backend) {
++$backendsTried;
try {
$authenticated = $backend->authenticate($user, $password);
} catch (AuthenticationException $e) {
Logger::error($e);
++$backendsWithError;
continue;
}
if ($authenticated) {
return true;
}
}
if ($backendsTried === 0) {
$this->error = static::EEMPTY;
} elseif ($backendsTried === $backendsWithError) {
$this->error = static::EFAIL;
} elseif ($backendsWithError) {
$this->error = static::ENOTALL;
}
return false;
}
/**
* Get the last error code
*
* @return int|null
*/
public function getError()
{
return $this->error;
}
/**
* Whether authentication had errors
*
* @return bool
*/
public function hasError()
{
return $this->error !== null;
}
/**
* Get whether to skip external user backends on iteration
*
* @return bool
*/
public function getSkipExternalBackends()
{
return $this->skipExternalBackends;
}
/**
* Set whether to skip external user backends on iteration
*
* @param bool $skipExternalBackends
*
* @return $this
*/
public function setSkipExternalBackends($skipExternalBackends = true)
{
$this->skipExternalBackends = (bool) $skipExternalBackends;
return $this;
}
/** /**
* Rewind the chain * Rewind the chain
@ -52,7 +186,7 @@ class AuthChain implements Iterator
} }
/** /**
* Return the current user backend * Get the current user backend
* *
* @return UserBackendInterface * @return UserBackendInterface
*/ */
@ -62,7 +196,7 @@ class AuthChain implements Iterator
} }
/** /**
* Return the key of the current user backend config * Get the key of the current user backend config
* *
* @return string * @return string
*/ */
@ -82,7 +216,8 @@ class AuthChain implements Iterator
} }
/** /**
* Check if the current user backend is valid, i.e. it's enabled and the config is valid * Check whether the current user backend is valid, i.e. it's enabled, not an external user backend and whether its
* config is valid
* *
* @return bool * @return bool
*/ */
@ -94,7 +229,7 @@ class AuthChain implements Iterator
} }
$backendConfig = $this->config->current(); $backendConfig = $this->config->current();
if ((bool) $backendConfig->get('disabled', false) === true) { if ((bool) $backendConfig->get('disabled', false)) {
$this->next(); $this->next();
return $this->valid(); return $this->valid();
} }
@ -105,15 +240,20 @@ class AuthChain implements Iterator
} catch (ConfigurationError $e) { } catch (ConfigurationError $e) {
Logger::error( Logger::error(
new ConfigurationError( new ConfigurationError(
'Cannot create authentication backend "%s". An exception was thrown:', 'Can\'t create authentication backend "%s". An exception was thrown:', $name, $e
$name,
$e
) )
); );
$this->next(); $this->next();
return $this->valid(); return $this->valid();
} }
if ($this->getSkipExternalBackends()
&& $backend instanceof ExternalBackend
) {
$this->next();
return $this->valid();
}
$this->currentBackend = $backend; $this->currentBackend = $backend;
return true; return true;
} }

View File

@ -0,0 +1,21 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Authentication;
use Icinga\User;
interface Authenticatable
{
/**
* Authenticate a user
*
* @param User $user
* @param string $password
*
* @return bool
*
* @throws \Icinga\Exception\AuthenticationException If authentication errors
*/
public function authenticate(User $user, $password);
}

View File

@ -36,11 +36,7 @@ class ExternalBackend implements UserBackendInterface
} }
/** /**
* Set this backend's name * {@inheritdoc}
*
* @param string $name
*
* @return $this
*/ */
public function setName($name) public function setName($name)
{ {
@ -49,30 +45,22 @@ class ExternalBackend implements UserBackendInterface
} }
/** /**
* Return this backend's name * {@inheritdoc}
*
* @return string
*/ */
public function getName() public function getName()
{ {
return $this->name; return $this->name;
} }
/** /**
* Authenticate the given user * {@inheritdoc}
*
* @param User $user
* @param string $password
*
* @return bool True on success, false on failure
*
* @throws AuthenticationException In case authentication is not possible due to an error
*/ */
public function authenticate(User $user, $password = null) public function authenticate(User $user, $password = null)
{ {
if (isset($_SERVER['REMOTE_USER'])) { if (isset($_SERVER['REMOTE_USER'])) {
$username = $_SERVER['REMOTE_USER']; $username = $_SERVER['REMOTE_USER'];
$user->setRemoteUserInformation($username, 'REMOTE_USER'); $user->setExternalUserInformation($username, 'REMOTE_USER');
if ($this->stripUsernameRegexp) { if ($this->stripUsernameRegexp) {
$stripped = preg_replace($this->stripUsernameRegexp, '', $username); $stripped = preg_replace($this->stripUsernameRegexp, '', $username);

View File

@ -3,13 +3,13 @@
namespace Icinga\Authentication\User; namespace Icinga\Authentication\User;
use Icinga\Exception\AuthenticationException; use Icinga\Authentication\Authenticatable;
use Icinga\User; use Icinga\User;
/** /**
* Interface for user backends * Interface for user backends
*/ */
interface UserBackendInterface interface UserBackendInterface extends Authenticatable
{ {
/** /**
* Set this backend's name * Set this backend's name
@ -26,16 +26,4 @@ interface UserBackendInterface
* @return string * @return string
*/ */
public function getName(); public function getName();
/**
* Authenticate the given user
*
* @param User $user
* @param string $password
*
* @return bool True on success, false on failure
*
* @throws AuthenticationException In case authentication is not possible due to an error
*/
public function authenticate(User $user, $password);
} }

View File

@ -111,7 +111,7 @@ class PivotTable
*/ */
protected function getPaginationParameter($axis, $param, $default = null) protected function getPaginationParameter($axis, $param, $default = null)
{ {
$request = Icinga::app()->getFrontController()->getRequest(); $request = Icinga::app()->getRequest();
$value = $request->getParam($param, ''); $value = $request->getParam($param, '');
if (strpos($value, ',') > 0) { if (strpos($value, ',') > 0) {

View File

@ -435,7 +435,7 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator
if ($itemsPerPage === null || $pageNumber === null) { if ($itemsPerPage === null || $pageNumber === null) {
// Detect parameters from request // Detect parameters from request
$request = Icinga::app()->getFrontController()->getRequest(); $request = Icinga::app()->getRequest();
if ($itemsPerPage === null) { if ($itemsPerPage === null) {
$itemsPerPage = $request->getParam('limit', 25); $itemsPerPage = $request->getParam('limit', 25);
} }

View File

@ -180,7 +180,7 @@ namespace Icinga\Test {
*/ */
public function getRequestMock() public function getRequestMock()
{ {
return Icinga::app()->getFrontController()->getRequest(); return Icinga::app()->getRequest();
} }
/** /**

View File

@ -57,7 +57,7 @@ class User
protected $additionalInformation = array(); protected $additionalInformation = array();
/** /**
* Information if the user is external authenticated * Information if the user is externally authenticated
* *
* Keys: * Keys:
* *
@ -66,7 +66,7 @@ class User
* *
* @var array * @var array
*/ */
protected $remoteUserInformation = array(); protected $externalUserInformation = array();
/** /**
* Set of permissions * Set of permissions
@ -96,6 +96,13 @@ class User
*/ */
protected $preferences; protected $preferences;
/**
* Whether the user is authenticated using a HTTP authentication mechanism
*
* @var bool
*/
protected $isHttpUser = false;
/** /**
* Creates a user object given the provided information * Creates a user object given the provided information
* *
@ -380,34 +387,57 @@ class User
} }
/** /**
* Set additional remote user information * Set additional external user information
* *
* @param stirng $username * @param string $username
* @param string $field * @param string $field
*/ */
public function setRemoteUserInformation($username, $field) public function setExternalUserInformation($username, $field)
{ {
$this->remoteUserInformation = array($username, $field); $this->externalUserInformation = array($username, $field);
} }
/** /**
* Get additional remote user information * Get additional external user information
* *
* @return array * @return array
*/ */
public function getRemoteUserInformation() public function getExternalUserInformation()
{ {
return $this->remoteUserInformation; return $this->externalUserInformation;
} }
/** /**
* Return true if user has remote user information set * Return true if user has external user information set
* *
* @return bool * @return bool
*/ */
public function isRemoteUser() public function isExternalUser()
{ {
return ! empty($this->remoteUserInformation); return ! empty($this->externalUserInformation);
}
/**
* Get whether the user is authenticated using a HTTP authentication mechanism
*
* @return bool
*/
public function getIsHttpUser()
{
return $this->isHttpUser;
}
/**
* Set whether the user is authenticated using a HTTP authentication mechanism
*
* @param bool $isHttpUser
*
* @return $this
*/
public function setIsHttpUser($isHttpUser = true)
{
$this->isHttpUser = (bool) $isHttpUser;
return $this;
} }
/** /**

View File

@ -6,7 +6,7 @@ namespace Icinga\Web\Controller;
use Exception; use Exception;
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Exception\Http\HttpMethodNotAllowedException; use Icinga\Exception\Http\HttpMethodNotAllowedException;
use Icinga\Exception\IcingaException; use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
@ -52,7 +52,7 @@ class ActionController extends Zend_Controller_Action
/** /**
* Authentication manager * Authentication manager
* *
* @var Manager|null * @var Auth|null
*/ */
private $auth; private $auth;
@ -124,12 +124,12 @@ class ActionController extends Zend_Controller_Action
/** /**
* Get the authentication manager * Get the authentication manager
* *
* @return Manager * @return Auth
*/ */
public function Auth() public function Auth()
{ {
if ($this->auth === null) { if ($this->auth === null) {
$this->auth = Manager::getInstance(); $this->auth = Auth::getInstance();
} }
return $this->auth; return $this->auth;
} }
@ -455,7 +455,7 @@ class ActionController extends Zend_Controller_Action
$notifications = Notification::getInstance(); $notifications = Notification::getInstance();
if ($notifications->hasMessages()) { if ($notifications->hasMessages()) {
$notificationList = array(); $notificationList = array();
foreach ($notifications->getMessages() as $m) { foreach ($notifications->popMessages() as $m) {
$notificationList[] = rawurlencode($m->type . ' ' . $m->message); $notificationList[] = rawurlencode($m->type . ' ' . $m->message);
} }
$resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true); $resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true);

View File

@ -8,7 +8,7 @@ use Zend_Form;
use Zend_Form_Element; use Zend_Form_Element;
use Zend_View_Interface; use Zend_View_Interface;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Security\SecurityException; use Icinga\Security\SecurityException;
use Icinga\Util\Translator; use Icinga\Util\Translator;
@ -179,7 +179,7 @@ class Form extends Zend_Form
/** /**
* Authentication manager * Authentication manager
* *
* @var Manager|null * @var Auth|null
*/ */
private $auth; private $auth;
@ -948,10 +948,17 @@ class Form extends Zend_Form
*/ */
public function addCsrfCounterMeasure() public function addCsrfCounterMeasure()
{ {
if (! $this->tokenDisabled && $this->getElement($this->tokenElementName) === null) { if (! $this->tokenDisabled) {
$request = $this->getRequest();
if (! $request->isXmlHttpRequest()
&& $request->getIsApiRequest()
) {
return $this;
}
if ($this->getElement($this->tokenElementName) === null) {
$this->addElement(new CsrfCounterMeasure($this->tokenElementName)); $this->addElement(new CsrfCounterMeasure($this->tokenElementName));
} }
}
return $this; return $this;
} }
@ -1245,7 +1252,7 @@ class Form extends Zend_Form
public function getRequest() public function getRequest()
{ {
if ($this->request === null) { if ($this->request === null) {
$this->request = Icinga::app()->getFrontController()->getRequest(); $this->request = Icinga::app()->getRequest();
} }
return $this->request; return $this->request;
@ -1344,12 +1351,12 @@ class Form extends Zend_Form
/** /**
* Get the authentication manager * Get the authentication manager
* *
* @return Manager * @return Auth
*/ */
public function Auth() public function Auth()
{ {
if ($this->auth === null) { if ($this->auth === null) {
$this->auth = Manager::getInstance(); $this->auth = Auth::getInstance();
} }
return $this->auth; return $this->auth;
} }

View File

@ -75,6 +75,6 @@ class Button extends FormElement
*/ */
protected function getRequest() protected function getRequest()
{ {
return Icinga::app()->getFrontController()->getRequest(); return Icinga::app()->getRequest();
} }
} }

View File

@ -7,7 +7,7 @@ use RecursiveIterator;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Data\ConfigObject; use Icinga\Data\ConfigObject;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
@ -208,7 +208,7 @@ class Menu implements RecursiveIterator
{ {
$menu = new static('menu'); $menu = new static('menu');
$menu->addMainMenuItems(); $menu->addMainMenuItems();
$auth = Manager::getInstance(); $auth = Auth::getInstance();
$manager = Icinga::app()->getModuleManager(); $manager = Icinga::app()->getModuleManager();
foreach ($manager->getLoadedModules() as $module) { foreach ($manager->getLoadedModules() as $module) {
if ($auth->hasPermission($manager::MODULE_PERMISSION_NS . $module->getName())) { if ($auth->hasPermission($manager::MODULE_PERMISSION_NS . $module->getName())) {
@ -223,7 +223,7 @@ class Menu implements RecursiveIterator
*/ */
protected function addMainMenuItems() protected function addMainMenuItems()
{ {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {

View File

@ -3,7 +3,7 @@
namespace Icinga\Web\Menu; namespace Icinga\Web\Menu;
use RecursiveFilterIterator; use RecursiveFilterIterator;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Web\Menu; use Icinga\Web\Menu;
class PermittedMenuItemFilter extends RecursiveFilterIterator class PermittedMenuItemFilter extends RecursiveFilterIterator
@ -18,7 +18,7 @@ class PermittedMenuItemFilter extends RecursiveFilterIterator
$item = $this->current(); $item = $this->current();
/** @var Menu $item */ /** @var Menu $item */
if (($permission = $item->getPermission()) !== null) { if (($permission = $item->getPermission()) !== null) {
$auth = Manager::getInstance(); $auth = Auth::getInstance();
if (! $auth->isAuthenticated()) { if (! $auth->isAuthenticated()) {
// Don't accept menu item because user is not authenticated and the menu item requires a permission // Don't accept menu item because user is not authenticated and the menu item requires a permission
return false; return false;

View File

@ -17,86 +17,72 @@ use Icinga\Web\Session;
*/ */
class Notification class Notification
{ {
/**
* Notification type info
*
* @var string
*/
const INFO = 'info';
/**
* Notification type error
*
* @var string
*/
const ERROR = 'error';
/**
* Notification type success
*
* @var string
*/
const SUCCESS = 'success';
/**
* Notification type warning
*
* @var string
*/
const WARNING = 'warning';
/**
* Name of the session key for notification messages
*
* @var string
*/
const SESSION_KEY = 'session';
/**
* Singleton instance
*
* @var self
*/
protected static $instance; protected static $instance;
/**
* Whether the platform is CLI
*
* @var bool
*/
protected $isCli = false; protected $isCli = false;
protected $session; /**
* Notification messages
*
* @var array
*/
protected $messages = array(); protected $messages = array();
public static function info($msg) /**
{ * Session
self::getInstance()->addMessage($msg, 'info'); *
} * @var Session
*/
public static function success($msg) protected $session;
{
self::getInstance()->addMessage($msg, 'success');
}
public static function warning($msg)
{
self::getInstance()->addMessage($msg, 'warning');
}
public static function error($msg)
{
self::getInstance()->addMessage($msg, 'error');
}
protected function addMessage($message, $type = 'info')
{
if (! in_array(
$type,
array(
'info',
'error',
'warning',
'success'
)
)) {
throw new ProgrammingError(
'"%s" is not a valid notification type',
$type
);
}
if ($this->isCli) {
$msg = sprintf('[%s] %s', $type, $message);
switch ($type) {
case 'info':
case 'success':
Logger::info($msg);
break;
case 'warning':
Logger::warn($msg);
break;
case 'error':
Logger::error($msg);
break;
}
return;
}
$this->messages[] = (object) array(
'type' => $type,
'message' => $message,
);
}
public function hasMessages()
{
return false === empty($this->messages);
}
public function getMessages()
{
$messages = $this->messages;
$this->messages = array();
return $messages;
}
/**
* Create the notification instance
*/
final private function __construct() final private function __construct()
{ {
if (Platform::isCli()) { if (Platform::isCli()) {
@ -105,33 +91,130 @@ class Notification
} }
$this->session = Session::getSession(); $this->session = Session::getSession();
$messages = $this->session->get(self::SESSION_KEY);
$stored = $this->session->get('messages'); if (is_array($messages)) {
if (is_array($stored)) { $this->messages = $messages;
$this->messages = $stored; $this->session->delete(self::SESSION_KEY);
$this->session->set('messages', array());
$this->session->write(); $this->session->write();
} }
} }
/**
* Get the Notification instance
*
* @return Notification
*/
public static function getInstance() public static function getInstance()
{ {
if (self::$instance === null) { if (self::$instance === null) {
self::$instance = new Notification(); self::$instance = new self();
} }
return self::$instance; return self::$instance;
} }
/**
* Add info notification
*
* @param string $msg
*/
public static function info($msg)
{
self::getInstance()->addMessage($msg, self::INFO);
}
/**
* Add error notification
*
* @param string $msg
*/
public static function error($msg)
{
self::getInstance()->addMessage($msg, self::ERROR);
}
/**
* Add success notification
*
* @param string $msg
*/
public static function success($msg)
{
self::getInstance()->addMessage($msg, self::SUCCESS);
}
/**
* Add warning notification
*
* @param string $msg
*/
public static function warning($msg)
{
self::getInstance()->addMessage($msg, self::WARNING);
}
/**
* Add a notification message
*
* @param string $message
* @param string $type
*/
protected function addMessage($message, $type = self::INFO)
{
if ($this->isCli) {
$msg = sprintf('[%s] %s', $type, $message);
switch ($type) {
case self::INFO:
case self::SUCCESS:
Logger::info($msg);
break;
case self::ERROR:
Logger::error($msg);
break;
case self::WARNING:
Logger::warning($msg);
break;
}
} else {
$this->messages[] = (object) array(
'type' => $type,
'message' => $message,
);
}
}
/**
* Pop the notification messages
*
* @return array
*/
public function popMessages()
{
$messages = $this->messages;
$this->messages = array();
return $messages;
}
/**
* Get whether notification messages have been added
*
* @return bool
*/
public function hasMessages()
{
return ! empty($this->messages);
}
/**
* Destroy the notification instance
*/
final public function __destruct() final public function __destruct()
{ {
if ($this->isCli) { if ($this->isCli) {
return; return;
} }
if ($this->session->get('messages') !== $this->messages) { if ($this->hasMessages() && $this->session->get('messages') !== $this->messages) {
$this->session->set('messages', $this->messages); $this->session->set(self::SESSION_KEY, $this->messages);
}
$this->session->write(); $this->session->write();
}
unset($this->session);
} }
} }

View File

@ -7,24 +7,46 @@ use Zend_Controller_Request_Http;
use Icinga\User; use Icinga\User;
/** /**
* Request to handle special attributes * A request
*/ */
class Request extends Zend_Controller_Request_Http class Request extends Zend_Controller_Request_Http
{ {
/** /**
* User object * User if authenticated
* *
* @var User * @var User|null
*/ */
private $user; protected $user;
/** /**
* Unique identifier
*
* @var string * @var string
*/ */
private $uniqueId; protected $uniqueId;
private $url; /**
* Request URL
*
* @var Url
*/
protected $url;
/**
* Get whether the request seems to be an API request
*
* @return bool
*/
public function getIsApiRequest()
{
return $this->getHeader('Accept') === 'application/json';
}
/**
* Get the request URL
*
* @return Url
*/
public function getUrl() public function getUrl()
{ {
if ($this->url === null) { if ($this->url === null) {
@ -34,31 +56,38 @@ class Request extends Zend_Controller_Request_Http
} }
/** /**
* Setter for user * Get the user if authenticated
* *
* @param User $user * @return User|null
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* Getter for user
*
* @return User
*/ */
public function getUser() public function getUser()
{ {
return $this->user; return $this->user;
} }
/**
* Set the authenticated user
*
* @param User $user
*
* @return $this
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/** /**
* Makes an ID unique to this request, to prevent id collisions in different containers * Makes an ID unique to this request, to prevent id collisions in different containers
* *
* Call this whenever an ID might show up multiple times in different containers. This function is useful * Call this whenever an ID might show up multiple times in different containers. This function is useful
* for ensuring unique ids on sites, even if we combine the HTML of different requests into one site, * for ensuring unique ids on sites, even if we combine the HTML of different requests into one site,
* while still being able to reference elements uniquely in the same request. * while still being able to reference elements uniquely in the same request.
*
* @param string $id
*
* @return string The id suffixed w/ an identifier unique to this request
*/ */
public function protectId($id) public function protectId($id)
{ {

View File

@ -8,18 +8,125 @@ use Icinga\Application\Icinga;
class Response extends Zend_Controller_Response_Http class Response extends Zend_Controller_Response_Http
{ {
/**
* Redirect URL
*
* @var Url|null
*/
protected $redirectUrl;
/**
* Request
*
* @var Request
*/
protected $request;
/**
* Whether to send the rerender layout header on XHR
*
* @var bool
*/
protected $rerenderLayout = false;
/**
* Get the redirect URL
*
* @return Url|null
*/
protected function getRedirectUrl()
{
return $this->redirectUrl;
}
/**
* Set the redirect URL
*
* Unlike {@link setRedirect()} this method only sets a redirect URL on the response for later usage.
* {@link prepare()} will take care of the correct redirect handling and HTTP headers on XHR and "normal" browser
* requests.
*
* @param string|Url $redirectUrl
*
* @return $this
*/
protected function setRedirectUrl($redirectUrl)
{
if (! $redirectUrl instanceof Url) {
$redirectUrl = Url::fromPath((string) $redirectUrl);
}
$redirectUrl->getParams()->setSeparator('&');
$this->redirectUrl = $redirectUrl;
return $this;
}
/**
* Get the request
*
* @return Request
*/
public function getRequest()
{
if ($this->request === null) {
$this->request = Icinga::app()->getRequest();
}
return $this->request;
}
/**
* Get whether to send the rerender layout header on XHR
*
* @return bool
*/
public function getRerenderLayout()
{
return $this->rerenderLayout;
}
/**
* Get whether to send the rerender layout header on XHR
*
* @param bool $rerenderLayout
*
* @return $this
*/
public function setRerenderLayout($rerenderLayout = true)
{
$this->rerenderLayout = (bool) $rerenderLayout;
return $this;
}
/**
* Prepare the request before sending
*/
protected function prepare()
{
$redirectUrl = $this->getRedirectUrl();
if ($this->getRequest()->isXmlHttpRequest()) {
if ($redirectUrl !== null) {
$this->setHeader('X-Icinga-Redirect', rawurlencode($redirectUrl->getAbsoluteUrl()), true);
if ($this->getRerenderLayout()) {
$this->setHeader('X-Icinga-Rerender-Layout', 'yes', true);
}
}
if ($this->getRerenderLayout()) {
$this->setHeader('X-Icinga-Container', 'layout', true);
}
} else {
if ($redirectUrl !== null) {
$this->setRedirect($redirectUrl->getAbsoluteUrl());
}
}
}
/**
* Redirect to the given URL and exit immediately
*
* @param string|Url $url
*/
public function redirectAndExit($url) public function redirectAndExit($url)
{ {
if (! $url instanceof Url) { $this->setRedirectUrl($url);
$url = Url::fromPath($url);
}
$url->getParams()->setSeparator('&');
if (Icinga::app()->getFrontController()->getRequest()->isXmlHttpRequest()) {
$this->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl()));
} else {
$this->setRedirect($url->getAbsoluteUrl());
}
$session = Session::getSession(); $session = Session::getSession();
if ($session->hasChanged()) { if ($session->hasChanged()) {
@ -29,4 +136,13 @@ class Response extends Zend_Controller_Response_Http
$this->sendHeaders(); $this->sendHeaders();
exit; exit;
} }
/**
* {@inheritdoc}
*/
public function sendHeaders()
{
$this->prepare();
return parent::sendHeaders();
}
} }

View File

@ -102,7 +102,7 @@ class Url
if ($app->isCli()) { if ($app->isCli()) {
return new FakeRequest(); return new FakeRequest();
} else { } else {
return $app->getFrontController()->getRequest(); return $app->getRequest();
} }
} }

View File

@ -5,7 +5,7 @@ namespace Icinga\Web;
use Closure; use Closure;
use Zend_View_Abstract; use Zend_View_Abstract;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Util\Translator; use Icinga\Util\Translator;
@ -39,7 +39,7 @@ class View extends Zend_View_Abstract
/** /**
* Authentication manager * Authentication manager
* *
* @var \Icinga\Authentication\Manager|null * @var Auth|null
*/ */
private $auth; private $auth;
@ -164,12 +164,12 @@ class View extends Zend_View_Abstract
/** /**
* Get the authentication manager * Get the authentication manager
* *
* @return Manager * @return Auth
*/ */
public function Auth() public function Auth()
{ {
if ($this->auth === null) { if ($this->auth === null) {
$this->auth = Manager::getInstance(); $this->auth = Auth::getInstance();
} }
return $this->auth; return $this->auth;
} }

View File

@ -3,11 +3,11 @@
namespace Icinga\Web\View; namespace Icinga\Web\View;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Web\Widget; use Icinga\Web\Widget;
$this->addHelperFunction('auth', function () { $this->addHelperFunction('auth', function () {
return Manager::getInstance(); return Auth::getInstance();
}); });
$this->addHelperFunction('widget', function ($name, $options = null) { $this->addHelperFunction('widget', function ($name, $options = null) {

View File

@ -116,7 +116,7 @@ class SortBox extends AbstractWidget
{ {
if ($this->query !== null) { if ($this->query !== null) {
if ($request === null) { if ($request === null) {
$request = Icinga::app()->getFrontController()->getRequest(); $request = Icinga::app()->getRequest();
} }
if (($sort = $request->getParam('sort'))) { if (($sort = $request->getParam('sort'))) {

View File

@ -309,7 +309,7 @@ EOT;
private function renderRefreshTab() private function renderRefreshTab()
{ {
$url = Icinga::app()->getFrontController()->getRequest()->getUrl(); $url = Icinga::app()->getRequest()->getUrl();
$tab = $this->get($this->getActiveName()); $tab = $this->get($this->getActiveName());
if ($tab !== null) { if ($tab !== null) {

View File

@ -3,7 +3,7 @@
namespace Icinga\Module\Monitoring\Web\Menu; namespace Icinga\Module\Monitoring\Web\Menu;
use Icinga\Authentication\Manager; use Icinga\Authentication\Auth;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Filterable; use Icinga\Data\Filterable;
use Icinga\Web\Menu; use Icinga\Web\Menu;
@ -27,7 +27,7 @@ class MonitoringMenuItemRenderer extends MenuItemRenderer
protected static function applyRestriction($restriction, Filterable $filterable) protected static function applyRestriction($restriction, Filterable $filterable)
{ {
$restrictions = Filter::matchAny(); $restrictions = Filter::matchAny();
foreach (Manager::getInstance()->getRestrictions($restriction) as $filter) { foreach (Auth::getInstance()->getRestrictions($restriction) as $filter) {
$restrictions->addFilter(Filter::fromQueryString($filter)); $restrictions->addFilter(Filter::fromQueryString($filter));
} }
$filterable->applyFilter($restrictions); $filterable->applyFilter($restrictions);

View File

@ -19,7 +19,7 @@ $maxProgress = @max(array_keys(array_filter(
$notifications = Notification::getInstance(); $notifications = Notification::getInstance();
if ($notifications->hasMessages()) { if ($notifications->hasMessages()) {
foreach ($notifications->getMessages() as $m) { foreach ($notifications->popMessages() as $m) {
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>'; echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
} }
} }