lib: Implement AuthChain::authenticate()

Right now the LoginController has all the authentication which is kind of a mess. Further, the upcoming basic access authentication has to reuse this code.
Thus AuthChain::authenticate() is introduced to handle both cases.

refs #9660
This commit is contained in:
Eric Lippmann 2015-07-29 14:11:54 +02:00
parent 4d44a0625c
commit 745e30259d
1 changed files with 161 additions and 14 deletions

View File

@ -4,40 +4,181 @@
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;
/**
* External user backends are excluded by the iterator
*
* @var int
*/
const IT_MODE_NOT_EXTERNAL = 1;
/**
* 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;
/**
* Mode of iteration
*
* @var int
*/
protected $iteratorMode;
/**
* 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 the iterator mode
*
* @return int
*/
public function getIteratorMode()
{
return $this->iteratorMode;
}
/**
* Set the iterator mode
*
* @param int $iteratorMode
*
* @return $this
*/
public function setIteratorMode($iteratorMode)
{
$this->iteratorMode = (int) $iteratorMode;
return $this;
}
/**
@ -52,7 +193,7 @@ class AuthChain implements Iterator
}
/**
* Return the current user backend
* Get the current user backend
*
* @return UserBackendInterface
*/
@ -62,7 +203,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
*/
@ -82,7 +223,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
*/
@ -105,15 +247,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->iteratorMode === static::IT_MODE_NOT_EXTERNAL
&& $backend instanceof ExternalBackend
) {
$this->next();
return $this->valid();
}
$this->currentBackend = $backend;
return true;
}