Fix authentication error handling

This commit is contained in:
Johannes Meyer 2014-03-28 14:45:03 +01:00
parent c03268414f
commit bafa8cc032
3 changed files with 64 additions and 47 deletions

View File

@ -1,5 +1,4 @@
<?php <?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}}
/** /**
* This file is part of Icinga Web 2. * This file is part of Icinga Web 2.
@ -61,19 +60,22 @@ class AuthenticationController extends ActionController
$this->view->form = new LoginForm(); $this->view->form = new LoginForm();
$this->view->form->setRequest($this->_request); $this->view->form->setRequest($this->_request);
$this->view->title = 'Icinga Web Login'; $this->view->title = 'Icinga Web Login';
try { try {
$redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard')); $redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard'));
if ($this->_request->isXmlHttpRequest()) { if ($this->_request->isXmlHttpRequest()) {
$redirectUrl->setParam('_render', 'layout'); $redirectUrl->setParam('_render', 'layout');
} }
$auth = AuthManager::getInstance(); $auth = AuthManager::getInstance();
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$this->redirectNow($redirectUrl); $this->redirectNow($redirectUrl);
} }
if ($this->view->form->isSubmittedAndValid()) { if ($this->view->form->isSubmittedAndValid()) {
$user = new User( $user = new User($this->view->form->getValue('username'));
$this->view->form->getValue('username')
);
try { try {
$config = Config::app('authentication'); $config = Config::app('authentication');
} catch (NotReadableError $e) { } catch (NotReadableError $e) {
@ -85,37 +87,33 @@ class AuthenticationController extends ActionController
. ' up. Please contact your Icinga Web administrator' . ' up. Please contact your Icinga Web administrator'
); );
} }
$backendsWithError = 0;
// TODO(el): Currently the user is only notified about authentication backend problems when all backends // TODO(el): Currently the user is only notified about authentication backend problems when all backends
// have errors. It may be the case that the authentication backend which provides the user has errors // have errors. It may be the case that the authentication backend which provides the user has errors
// but other authentication backends work. In that scenario the user is presented an error message // but other authentication backends work. In that scenario the user is presented an error message
// saying "Incorrect username or password". We must inform the user that not all authentication methods // saying "Incorrect username or password". We must inform the user that not all authentication methods
// are available. // are available.
$backendsTried = 0; $backendsTried = 0;
$backendsWithError = 0;
$chain = new AuthChain($config); $chain = new AuthChain($config);
foreach ($chain as $backend) { foreach ($chain as $backend) {
++$backendsTried; $authenticated = $backend->authenticate($user, $this->view->form->getValue('password'));
try { if ($authenticated === true) {
if ($backend->authenticate($user, $this->view->form->getValue('password'))) {
$auth->setAuthenticated($user); $auth->setAuthenticated($user);
$this->redirectNow($redirectUrl); $this->redirectNow($redirectUrl);
} elseif ($authenticated === null) {
$backendsWithError += 1;
} }
} catch (Exception $e) { $backendsTried += 1;
Logger::error(
new Exception(
'Cannot authenticate against backend "' . $backend->getName() . '".'
. ' An exception was thrown:', 0, $e
)
);
++$backendsWithError;
}
} }
if ($backendsWithError === $backendsTried) { if ($backendsWithError === $backendsTried) {
throw new ConfigurationError( throw new ConfigurationError(
'No authentication methods available. It seems that all set up authentication methods have' 'No authentication methods available. It seems that all set up authentication methods have'
. ' errors. Please contact your Icinga Web administrator' . ' errors. Please contact your Icinga Web administrator'
); );
} }
$this->view->form->getElement('password')->addError(t('Incorrect username or password')); $this->view->form->getElement('password')->addError(t('Incorrect username or password'));
} }
} catch (Exception $e) { } catch (Exception $e) {
@ -140,4 +138,3 @@ class AuthenticationController extends ActionController
} }
} }
} }
// @codingStandardsIgnoreEnd

View File

@ -29,12 +29,13 @@
namespace Icinga\Authentication\Backend; namespace Icinga\Authentication\Backend;
use Exception; use \Exception;
use Zend_Db_Expr; use \Zend_Db_Expr;
use \Zend_Db_Select;
use Icinga\Authentication\UserBackend; use Icinga\Authentication\UserBackend;
use Icinga\Data\Db\Connection; use Icinga\Data\Db\Connection;
use Icinga\Logger\Logger;
use Icinga\User; use Icinga\User;
use \Zend_Db_Select;
class DbUserBackend extends UserBackend class DbUserBackend extends UserBackend
{ {
@ -68,22 +69,22 @@ class DbUserBackend extends UserBackend
} }
/** /**
* Authenticate * Authenticate the given user and return true on success, false on failure and null on error
* *
* @param User $user * @param User $user
* @param string $password * @param string $password
* *
* @return bool * @return bool|null
* @throws \Exception If we can not fetch the salt
*/ */
public function authenticate(User $user, $password) public function authenticate(User $user, $password)
{ {
try {
$salt = $this->getSalt($user->getUsername()); $salt = $this->getSalt($user->getUsername());
if ($salt === null) { if ($salt === null) {
return false; return false;
} }
if ($salt === '') { if ($salt === '') {
throw new Exception(); throw new Exception('Cannot find salt for user ' . $user->getUsername());
} }
$select = new Zend_Db_Select($this->conn->getConnection()); $select = new Zend_Db_Select($this->conn->getConnection());
@ -94,6 +95,16 @@ class DbUserBackend extends UserBackend
->query()->fetchObject(); ->query()->fetchObject();
return ($row !== false) ? true : false; return ($row !== false) ? true : false;
} catch (Exception $e) {
Logger::error(
sprintf(
'Failed to authenticate user "%s" with backend "%s". Exception occured: %s',
$user->getUsername(),
$this->getName(),
$e->getMessage()
)
);
}
} }
/** /**

View File

@ -29,7 +29,9 @@
namespace Icinga\Authentication\Backend; namespace Icinga\Authentication\Backend;
use \Exception;
use Icinga\User; use Icinga\User;
use Icinga\Logger\Logger;
use Icinga\Authentication\UserBackend; use Icinga\Authentication\UserBackend;
use Icinga\Protocol\Ldap\Connection; use Icinga\Protocol\Ldap\Connection;
@ -87,23 +89,30 @@ class LdapUserBackend extends UserBackend
} }
/** /**
* Authenticate * Authenticate the given user and return true on success, false on failure and null on error
* *
* @param User $user * @param User $user
* @param string $password * @param string $password
* *
* @return bool * @return bool|null
*/ */
public function authenticate(User $user, $password) public function authenticate(User $user, $password)
{ {
if ($this->conn->testCredentials( try {
return $this->conn->testCredentials(
$this->conn->fetchDN($this->createQuery($user->getUsername())), $this->conn->fetchDN($this->createQuery($user->getUsername())),
$password $password
);
} catch (Exception $e) {
Logger::error(
sprintf(
'Failed to authenticate user "%s" with backend "%s". Exception occured: %s',
$user->getUsername(),
$this->getName(),
$e->getMessage()
) )
) { );
return true;
} }
return false;
} }
/** /**