commit
a234852f32
|
@ -6,20 +6,14 @@
|
|||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
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\User;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Web\Controller;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* Application wide controller for authentication
|
||||
*/
|
||||
class AuthenticationController extends ActionController
|
||||
class AuthenticationController extends Controller
|
||||
{
|
||||
/**
|
||||
* This controller does not require authentication
|
||||
|
@ -34,118 +28,19 @@ class AuthenticationController extends ActionController
|
|||
public function loginAction()
|
||||
{
|
||||
$icinga = Icinga::app();
|
||||
if ($icinga->setupTokenExists() && $icinga->requiresSetup()) {
|
||||
if (($requiresSetup = $icinga->requiresSetup()) && $icinga->setupTokenExists()) {
|
||||
$this->redirectNow(Url::fromPath('setup'));
|
||||
}
|
||||
|
||||
$triedOnlyExternalAuth = null;
|
||||
$auth = $this->Auth();
|
||||
$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 ($auth->isAuthenticated()) {
|
||||
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||
}
|
||||
|
||||
try {
|
||||
$config = Config::app('authentication');
|
||||
} 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());
|
||||
$form = new LoginForm();
|
||||
if ($this->Auth()->isAuthenticated()) {
|
||||
$this->redirectNow($form->getRedirectUrl());
|
||||
}
|
||||
|
||||
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && ! $auth->isAuthenticated();
|
||||
$this->view->requiresSetup = Icinga::app()->requiresSetup();
|
||||
if (! $requiresSetup) {
|
||||
$form->handleRequest();
|
||||
}
|
||||
$this->view->form = $form;
|
||||
$this->view->title = $this->translate('Icinga Web 2 Login');
|
||||
$this->view->requiresSetup = $requiresSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,10 +52,12 @@ class AuthenticationController extends ActionController
|
|||
if (! $auth->isAuthenticated()) {
|
||||
$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();
|
||||
if ($isRemoteUser === true) {
|
||||
$this->_response->setHttpResponseCode(401);
|
||||
if ($isExternalUser) {
|
||||
$this->getResponse()->setHttpResponseCode(401);
|
||||
} else {
|
||||
$this->redirectToLogin();
|
||||
}
|
||||
|
|
|
@ -3,16 +3,24 @@
|
|||
|
||||
namespace Icinga\Forms\Authentication;
|
||||
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Authentication\User\ExternalBackend;
|
||||
use Icinga\User;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* Class LoginForm
|
||||
* Form for user authentication
|
||||
*/
|
||||
class LoginForm extends Form
|
||||
{
|
||||
/**
|
||||
* Initialize this login form
|
||||
* Redirect URL
|
||||
*/
|
||||
const REDIRECT_URL = 'dashboard';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
|
@ -22,7 +30,7 @@ class LoginForm extends Form
|
|||
}
|
||||
|
||||
/**
|
||||
* @see Form::createElements()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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.'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Icinga\Forms;
|
|||
use Exception;
|
||||
use DateTimeZone;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\User\Preferences;
|
||||
use Icinga\User\Preferences\PreferencesStore;
|
||||
use Icinga\Util\TimezoneDetect;
|
||||
|
@ -123,7 +123,7 @@ class PreferenceForm extends Form
|
|||
*/
|
||||
public function onRequest()
|
||||
{
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
$values = $auth->getUser()->getPreferences()->get('icingaweb');
|
||||
|
||||
if (! isset($values['language'])) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Authentication\Manager as Auth;
|
||||
use Icinga\Authentication\Auth;
|
||||
|
||||
|
||||
if (Auth::getInstance()->isAuthenticated()):
|
||||
|
@ -51,7 +51,7 @@ if (Auth::getInstance()->isAuthenticated()):
|
|||
<ul id="notifications"><?php
|
||||
$notifications = Notification::getInstance();
|
||||
if ($notifications->hasMessages()) {
|
||||
foreach ($notifications->getMessages() as $m) {
|
||||
foreach ($notifications->popMessages() as $m) {
|
||||
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
'<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
|
||||
'</a>'
|
||||
); ?></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 ?>
|
||||
<?= $this->form ?>
|
||||
<div class="footer">Icinga Web 2 © 2013-2015<br><a href="https://www.icinga.org"><?= $this->translate('The Icinga Project'); ?></a></div>
|
||||
|
|
|
@ -12,7 +12,7 @@ use Zend_Layout;
|
|||
use Zend_Paginator;
|
||||
use Zend_View_Helper_PaginationControl;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\User;
|
||||
use Icinga\Util\TimezoneDetect;
|
||||
use Icinga\Util\Translator;
|
||||
|
@ -55,6 +55,13 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Session object
|
||||
*
|
||||
|
@ -91,11 +98,12 @@ class Web extends ApplicationBootstrap
|
|||
->setupResourceFactory()
|
||||
->setupSession()
|
||||
->setupNotifications()
|
||||
->setupRequest()
|
||||
->setupResponse()
|
||||
->setupUser()
|
||||
->setupTimezone()
|
||||
->setupLogger()
|
||||
->setupInternationalization()
|
||||
->setupRequest()
|
||||
->setupZendMvc()
|
||||
->setupFormNamespace()
|
||||
->setupModuleManager()
|
||||
|
@ -136,6 +144,26 @@ class Web extends ApplicationBootstrap
|
|||
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
|
||||
*
|
||||
|
@ -151,7 +179,7 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
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()
|
||||
{
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
if ($auth->isAuthenticated()) {
|
||||
$this->user = $auth->getUser();
|
||||
$user = $auth->getUser();
|
||||
$this->request->setUser($user);
|
||||
$this->user = $user;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
@ -209,16 +239,24 @@ class Web extends ApplicationBootstrap
|
|||
}
|
||||
|
||||
/**
|
||||
* Inject dependencies into request
|
||||
* Set the request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function setupRequest()
|
||||
{
|
||||
$this->request = new Request();
|
||||
if ($this->user instanceof User) {
|
||||
$this->request->setUser($this->user);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function setupResponse()
|
||||
{
|
||||
$this->response = new Response();
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -282,7 +320,7 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
protected function detectTimezone()
|
||||
{
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
if (! $auth->isAuthenticated()
|
||||
|| ($timezone = $auth->getUser()->getPreferences()->getValue('icingaweb', 'timezone')) === null
|
||||
) {
|
||||
|
@ -303,7 +341,7 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
protected function detectLocale()
|
||||
{
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
if ($auth->isAuthenticated()
|
||||
&& ($locale = $auth->getUser()->getPreferences()->getValue('icingaweb', 'language')) !== null
|
||||
) {
|
||||
|
|
|
@ -4,18 +4,20 @@
|
|||
namespace Icinga\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Authentication\UserGroup\UserGroupBackend;
|
||||
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\Exception\IcingaException;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\User;
|
||||
use Icinga\User\Preferences;
|
||||
use Icinga\User\Preferences\PreferencesStore;
|
||||
use Icinga\Web\Session;
|
||||
|
||||
class Manager
|
||||
class Auth
|
||||
{
|
||||
/**
|
||||
* Singleton instance
|
||||
|
@ -24,6 +26,20 @@ class Manager
|
|||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Request
|
||||
*
|
||||
* @var \Icinga\Web\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* @var \Icinga\Web\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Authenticated user
|
||||
*
|
||||
|
@ -32,6 +48,9 @@ class Manager
|
|||
private $user;
|
||||
|
||||
|
||||
/**
|
||||
* @see getInstance()
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
@ -44,11 +63,39 @@ class Manager
|
|||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new static();
|
||||
self::$instance = new self();
|
||||
}
|
||||
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)
|
||||
{
|
||||
$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
|
||||
* authentication is no longer in effect
|
||||
* @return \Icinga\Web\Request
|
||||
*/
|
||||
public function authenticateFromSession()
|
||||
public function getRequest()
|
||||
{
|
||||
$this->user = Session::getSession()->get('user');
|
||||
if ($this->user !== null && $this->user->isRemoteUser() === true) {
|
||||
list($originUsername, $field) = $this->user->getRemoteUserInformation();
|
||||
if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
|
||||
$this->removeAuthorization();
|
||||
}
|
||||
if ($this->request === null) {
|
||||
$this->request = Icinga::app()->getRequest();
|
||||
}
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is authenticated
|
||||
* Get the response
|
||||
*
|
||||
* @param bool $ignoreSession True to prevent session authentication
|
||||
*
|
||||
* @return bool
|
||||
* @return \Icinga\Web\Response
|
||||
*/
|
||||
public function isAuthenticated($ignoreSession = false)
|
||||
public function getResponse()
|
||||
{
|
||||
if ($this->user === null && ! $ignoreSession) {
|
||||
$this->authenticateFromSession();
|
||||
if ($this->response === null) {
|
||||
$this->response = Icinga::app()->getResponse();
|
||||
}
|
||||
return is_object($this->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,15 +221,6 @@ class Manager
|
|||
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
|
||||
*
|
||||
|
@ -212,13 +232,124 @@ class Manager
|
|||
}
|
||||
|
||||
/**
|
||||
* Getter for groups belonged to authenticated user
|
||||
* Try to authenticate the user with the current session
|
||||
*
|
||||
* @return array
|
||||
* @see User::getGroups
|
||||
* Authentication for externally-authenticated users will be revoked if the username changed or external
|
||||
* 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();
|
||||
}
|
||||
}
|
|
@ -4,46 +4,180 @@
|
|||
namespace Icinga\Authentication;
|
||||
|
||||
use Iterator;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Authentication\User\UserBackend;
|
||||
use Icinga\Authentication\User\UserBackendInterface;
|
||||
use Icinga\Application\Config;
|
||||
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\NotReadableError;
|
||||
use Icinga\User;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The consecutive user backend while looping
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @param Config $config User backends configuration
|
||||
*/
|
||||
public function __construct(Config $config)
|
||||
public function __construct(Config $config = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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
|
||||
*
|
||||
* @return ConfigObject
|
||||
* @return ConfigObject
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
|
@ -52,7 +186,7 @@ class AuthChain implements Iterator
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the current user backend
|
||||
* Get the current user backend
|
||||
*
|
||||
* @return UserBackendInterface
|
||||
*/
|
||||
|
@ -62,9 +196,9 @@ 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
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
|
@ -74,7 +208,7 @@ class AuthChain implements Iterator
|
|||
/**
|
||||
* Move forward to the next user backend config
|
||||
*
|
||||
* @return ConfigObject
|
||||
* @return ConfigObject
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
|
@ -82,19 +216,20 @@ 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
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
if (!$this->config->valid()) {
|
||||
if (! $this->config->valid()) {
|
||||
// Stop when there are no more backends to check
|
||||
return false;
|
||||
}
|
||||
|
||||
$backendConfig = $this->config->current();
|
||||
if ((bool) $backendConfig->get('disabled', false) === true) {
|
||||
if ((bool) $backendConfig->get('disabled', false)) {
|
||||
$this->next();
|
||||
return $this->valid();
|
||||
}
|
||||
|
@ -105,15 +240,20 @@ class AuthChain implements Iterator
|
|||
} catch (ConfigurationError $e) {
|
||||
Logger::error(
|
||||
new ConfigurationError(
|
||||
'Cannot create authentication backend "%s". An exception was thrown:',
|
||||
$name,
|
||||
$e
|
||||
'Can\'t create authentication backend "%s". An exception was thrown:', $name, $e
|
||||
)
|
||||
);
|
||||
$this->next();
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
if ($this->getSkipExternalBackends()
|
||||
&& $backend instanceof ExternalBackend
|
||||
) {
|
||||
$this->next();
|
||||
return $this->valid();
|
||||
}
|
||||
|
||||
$this->currentBackend = $backend;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -36,11 +36,7 @@ class ExternalBackend implements UserBackendInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Set this backend's name
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
|
@ -49,30 +45,22 @@ class ExternalBackend implements UserBackendInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Return this backend's name
|
||||
*
|
||||
* @return string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function authenticate(User $user, $password = null)
|
||||
{
|
||||
if (isset($_SERVER['REMOTE_USER'])) {
|
||||
$username = $_SERVER['REMOTE_USER'];
|
||||
$user->setRemoteUserInformation($username, 'REMOTE_USER');
|
||||
$user->setExternalUserInformation($username, 'REMOTE_USER');
|
||||
|
||||
if ($this->stripUsernameRegexp) {
|
||||
$stripped = preg_replace($this->stripUsernameRegexp, '', $username);
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
namespace Icinga\Authentication\User;
|
||||
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Authentication\Authenticatable;
|
||||
use Icinga\User;
|
||||
|
||||
/**
|
||||
* Interface for user backends
|
||||
*/
|
||||
interface UserBackendInterface
|
||||
interface UserBackendInterface extends Authenticatable
|
||||
{
|
||||
/**
|
||||
* Set this backend's name
|
||||
|
@ -26,16 +26,4 @@ interface UserBackendInterface
|
|||
* @return string
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ class PivotTable
|
|||
*/
|
||||
protected function getPaginationParameter($axis, $param, $default = null)
|
||||
{
|
||||
$request = Icinga::app()->getFrontController()->getRequest();
|
||||
$request = Icinga::app()->getRequest();
|
||||
|
||||
$value = $request->getParam($param, '');
|
||||
if (strpos($value, ',') > 0) {
|
||||
|
|
|
@ -435,7 +435,7 @@ class SimpleQuery implements QueryInterface, Queryable, Iterator
|
|||
|
||||
if ($itemsPerPage === null || $pageNumber === null) {
|
||||
// Detect parameters from request
|
||||
$request = Icinga::app()->getFrontController()->getRequest();
|
||||
$request = Icinga::app()->getRequest();
|
||||
if ($itemsPerPage === null) {
|
||||
$itemsPerPage = $request->getParam('limit', 25);
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace Icinga\Test {
|
|||
*/
|
||||
public function getRequestMock()
|
||||
{
|
||||
return Icinga::app()->getFrontController()->getRequest();
|
||||
return Icinga::app()->getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,7 +57,7 @@ class User
|
|||
protected $additionalInformation = array();
|
||||
|
||||
/**
|
||||
* Information if the user is external authenticated
|
||||
* Information if the user is externally authenticated
|
||||
*
|
||||
* Keys:
|
||||
*
|
||||
|
@ -66,7 +66,7 @@ class User
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $remoteUserInformation = array();
|
||||
protected $externalUserInformation = array();
|
||||
|
||||
/**
|
||||
* Set of permissions
|
||||
|
@ -96,6 +96,13 @@ class User
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Icinga\Web\Controller;
|
|||
use Exception;
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Exception\Http\HttpMethodNotAllowedException;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
@ -52,7 +52,7 @@ class ActionController extends Zend_Controller_Action
|
|||
/**
|
||||
* Authentication manager
|
||||
*
|
||||
* @var Manager|null
|
||||
* @var Auth|null
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
|
@ -124,12 +124,12 @@ class ActionController extends Zend_Controller_Action
|
|||
/**
|
||||
* Get the authentication manager
|
||||
*
|
||||
* @return Manager
|
||||
* @return Auth
|
||||
*/
|
||||
public function Auth()
|
||||
{
|
||||
if ($this->auth === null) {
|
||||
$this->auth = Manager::getInstance();
|
||||
$this->auth = Auth::getInstance();
|
||||
}
|
||||
return $this->auth;
|
||||
}
|
||||
|
@ -455,7 +455,7 @@ class ActionController extends Zend_Controller_Action
|
|||
$notifications = Notification::getInstance();
|
||||
if ($notifications->hasMessages()) {
|
||||
$notificationList = array();
|
||||
foreach ($notifications->getMessages() as $m) {
|
||||
foreach ($notifications->popMessages() as $m) {
|
||||
$notificationList[] = rawurlencode($m->type . ' ' . $m->message);
|
||||
}
|
||||
$resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true);
|
||||
|
|
|
@ -8,7 +8,7 @@ use Zend_Form;
|
|||
use Zend_Form_Element;
|
||||
use Zend_View_Interface;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Security\SecurityException;
|
||||
use Icinga\Util\Translator;
|
||||
|
@ -179,7 +179,7 @@ class Form extends Zend_Form
|
|||
/**
|
||||
* Authentication manager
|
||||
*
|
||||
* @var Manager|null
|
||||
* @var Auth|null
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
|
@ -948,10 +948,17 @@ class Form extends Zend_Form
|
|||
*/
|
||||
public function addCsrfCounterMeasure()
|
||||
{
|
||||
if (! $this->tokenDisabled && $this->getElement($this->tokenElementName) === null) {
|
||||
$this->addElement(new CsrfCounterMeasure($this->tokenElementName));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -1245,7 +1252,7 @@ class Form extends Zend_Form
|
|||
public function getRequest()
|
||||
{
|
||||
if ($this->request === null) {
|
||||
$this->request = Icinga::app()->getFrontController()->getRequest();
|
||||
$this->request = Icinga::app()->getRequest();
|
||||
}
|
||||
|
||||
return $this->request;
|
||||
|
@ -1344,12 +1351,12 @@ class Form extends Zend_Form
|
|||
/**
|
||||
* Get the authentication manager
|
||||
*
|
||||
* @return Manager
|
||||
* @return Auth
|
||||
*/
|
||||
public function Auth()
|
||||
{
|
||||
if ($this->auth === null) {
|
||||
$this->auth = Manager::getInstance();
|
||||
$this->auth = Auth::getInstance();
|
||||
}
|
||||
return $this->auth;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,6 @@ class Button extends FormElement
|
|||
*/
|
||||
protected function getRequest()
|
||||
{
|
||||
return Icinga::app()->getFrontController()->getRequest();
|
||||
return Icinga::app()->getRequest();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use RecursiveIterator;
|
|||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
@ -208,7 +208,7 @@ class Menu implements RecursiveIterator
|
|||
{
|
||||
$menu = new static('menu');
|
||||
$menu->addMainMenuItems();
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
$manager = Icinga::app()->getModuleManager();
|
||||
foreach ($manager->getLoadedModules() as $module) {
|
||||
if ($auth->hasPermission($manager::MODULE_PERMISSION_NS . $module->getName())) {
|
||||
|
@ -223,7 +223,7 @@ class Menu implements RecursiveIterator
|
|||
*/
|
||||
protected function addMainMenuItems()
|
||||
{
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
|
||||
if ($auth->isAuthenticated()) {
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Icinga\Web\Menu;
|
||||
|
||||
use RecursiveFilterIterator;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Web\Menu;
|
||||
|
||||
class PermittedMenuItemFilter extends RecursiveFilterIterator
|
||||
|
@ -18,7 +18,7 @@ class PermittedMenuItemFilter extends RecursiveFilterIterator
|
|||
$item = $this->current();
|
||||
/** @var Menu $item */
|
||||
if (($permission = $item->getPermission()) !== null) {
|
||||
$auth = Manager::getInstance();
|
||||
$auth = Auth::getInstance();
|
||||
if (! $auth->isAuthenticated()) {
|
||||
// Don't accept menu item because user is not authenticated and the menu item requires a permission
|
||||
return false;
|
||||
|
|
|
@ -17,86 +17,72 @@ use Icinga\Web\Session;
|
|||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Whether the platform is CLI
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isCli = false;
|
||||
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Notification messages
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $messages = array();
|
||||
|
||||
public static function info($msg)
|
||||
{
|
||||
self::getInstance()->addMessage($msg, 'info');
|
||||
}
|
||||
|
||||
public static function success($msg)
|
||||
{
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Session
|
||||
*
|
||||
* @var Session
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Create the notification instance
|
||||
*/
|
||||
final private function __construct()
|
||||
{
|
||||
if (Platform::isCli()) {
|
||||
|
@ -105,33 +91,130 @@ class Notification
|
|||
}
|
||||
|
||||
$this->session = Session::getSession();
|
||||
|
||||
$stored = $this->session->get('messages');
|
||||
if (is_array($stored)) {
|
||||
$this->messages = $stored;
|
||||
$this->session->set('messages', array());
|
||||
$messages = $this->session->get(self::SESSION_KEY);
|
||||
if (is_array($messages)) {
|
||||
$this->messages = $messages;
|
||||
$this->session->delete(self::SESSION_KEY);
|
||||
$this->session->write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Notification instance
|
||||
*
|
||||
* @return Notification
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Notification();
|
||||
self::$instance = new self();
|
||||
}
|
||||
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()
|
||||
{
|
||||
if ($this->isCli) {
|
||||
return;
|
||||
}
|
||||
if ($this->session->get('messages') !== $this->messages) {
|
||||
$this->session->set('messages', $this->messages);
|
||||
if ($this->hasMessages() && $this->session->get('messages') !== $this->messages) {
|
||||
$this->session->set(self::SESSION_KEY, $this->messages);
|
||||
$this->session->write();
|
||||
}
|
||||
$this->session->write();
|
||||
|
||||
unset($this->session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,24 +7,46 @@ use Zend_Controller_Request_Http;
|
|||
use Icinga\User;
|
||||
|
||||
/**
|
||||
* Request to handle special attributes
|
||||
* A request
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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()
|
||||
{
|
||||
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
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for user
|
||||
*
|
||||
* @return User
|
||||
* @return User|null
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
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
|
||||
*
|
||||
* 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,
|
||||
* 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)
|
||||
{
|
||||
|
|
|
@ -8,18 +8,125 @@ use Icinga\Application\Icinga;
|
|||
|
||||
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)
|
||||
{
|
||||
if (! $url instanceof 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());
|
||||
}
|
||||
$this->setRedirectUrl($url);
|
||||
|
||||
$session = Session::getSession();
|
||||
if ($session->hasChanged()) {
|
||||
|
@ -29,4 +136,13 @@ class Response extends Zend_Controller_Response_Http
|
|||
$this->sendHeaders();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sendHeaders()
|
||||
{
|
||||
$this->prepare();
|
||||
return parent::sendHeaders();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ class Url
|
|||
if ($app->isCli()) {
|
||||
return new FakeRequest();
|
||||
} else {
|
||||
return $app->getFrontController()->getRequest();
|
||||
return $app->getRequest();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Icinga\Web;
|
|||
|
||||
use Closure;
|
||||
use Zend_View_Abstract;
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Util\Translator;
|
||||
|
||||
|
@ -39,7 +39,7 @@ class View extends Zend_View_Abstract
|
|||
/**
|
||||
* Authentication manager
|
||||
*
|
||||
* @var \Icinga\Authentication\Manager|null
|
||||
* @var Auth|null
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
|
@ -164,12 +164,12 @@ class View extends Zend_View_Abstract
|
|||
/**
|
||||
* Get the authentication manager
|
||||
*
|
||||
* @return Manager
|
||||
* @return Auth
|
||||
*/
|
||||
public function Auth()
|
||||
{
|
||||
if ($this->auth === null) {
|
||||
$this->auth = Manager::getInstance();
|
||||
$this->auth = Auth::getInstance();
|
||||
}
|
||||
return $this->auth;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
namespace Icinga\Web\View;
|
||||
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Web\Widget;
|
||||
|
||||
$this->addHelperFunction('auth', function () {
|
||||
return Manager::getInstance();
|
||||
return Auth::getInstance();
|
||||
});
|
||||
|
||||
$this->addHelperFunction('widget', function ($name, $options = null) {
|
||||
|
|
|
@ -116,7 +116,7 @@ class SortBox extends AbstractWidget
|
|||
{
|
||||
if ($this->query !== null) {
|
||||
if ($request === null) {
|
||||
$request = Icinga::app()->getFrontController()->getRequest();
|
||||
$request = Icinga::app()->getRequest();
|
||||
}
|
||||
|
||||
if (($sort = $request->getParam('sort'))) {
|
||||
|
|
|
@ -309,7 +309,7 @@ EOT;
|
|||
|
||||
private function renderRefreshTab()
|
||||
{
|
||||
$url = Icinga::app()->getFrontController()->getRequest()->getUrl();
|
||||
$url = Icinga::app()->getRequest()->getUrl();
|
||||
$tab = $this->get($this->getActiveName());
|
||||
|
||||
if ($tab !== null) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filterable;
|
||||
use Icinga\Web\Menu;
|
||||
|
@ -27,7 +27,7 @@ class MonitoringMenuItemRenderer extends MenuItemRenderer
|
|||
protected static function applyRestriction($restriction, Filterable $filterable)
|
||||
{
|
||||
$restrictions = Filter::matchAny();
|
||||
foreach (Manager::getInstance()->getRestrictions($restriction) as $filter) {
|
||||
foreach (Auth::getInstance()->getRestrictions($restriction) as $filter) {
|
||||
$restrictions->addFilter(Filter::fromQueryString($filter));
|
||||
}
|
||||
$filterable->applyFilter($restrictions);
|
||||
|
|
|
@ -19,7 +19,7 @@ $maxProgress = @max(array_keys(array_filter(
|
|||
|
||||
$notifications = Notification::getInstance();
|
||||
if ($notifications->hasMessages()) {
|
||||
foreach ($notifications->getMessages() as $m) {
|
||||
foreach ($notifications->popMessages() as $m) {
|
||||
echo '<li class="' . $m->type . '">' . $this->escape($m->message) . '</li>';
|
||||
}
|
||||
}
|
||||
|
@ -135,4 +135,4 @@ if ($notifications->hasMessages()) {
|
|||
<?= $this->render('index/parts/wizard.phtml'); ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue