Auth: Connect only when a authentication backend is used. Fix log in error messages
refs #5506 refs #5638 fixes #4931
This commit is contained in:
parent
35fc451115
commit
69a482d106
|
@ -70,7 +70,7 @@ class AuthenticationController extends ActionController
|
||||||
);
|
);
|
||||||
if (!$auth->authenticate($credentials)) {
|
if (!$auth->authenticate($credentials)) {
|
||||||
$this->view->form->getElement('password')
|
$this->view->form->getElement('password')
|
||||||
->addError(t('Please provide a valid username and password'));
|
->addError(t('Incorrect username or password'));
|
||||||
} else {
|
} else {
|
||||||
$this->redirectNow($redirectUrl);
|
$this->redirectNow($redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
; authentication.ini
|
; authentication.ini
|
||||||
;
|
;
|
||||||
; Each section listed in this configuration represents a single backend
|
; Each section listed in this configuration represents a backend used to authenticate users. The backend configurations
|
||||||
; that can be used to authenticate users or groups. Each databse backend must refer
|
: must define a resource referring to a configured database or LDAP connection in the INI file resources.ini.
|
||||||
; to a resource defined in resources.ini,
|
|
||||||
;
|
;
|
||||||
; The order of entries in this configuration is used to determine the fallback
|
; The backends will be processed from top to bottom using the first backend for authentication which reports that
|
||||||
; priority in case of an error. If the resource referenced in the first
|
; the user trying to log in is available.
|
||||||
; entry is not reachable, the next lower entry will be used for authentication.
|
|
||||||
; Please be aware that this behaviour is not valid for the authentication itself.
|
|
||||||
; The authentication will only be done against the one available resource with the highest
|
|
||||||
; priority.
|
|
||||||
|
|
||||||
|
|
||||||
[internal_ldap_authentication]
|
[internal_ldap_authentication]
|
||||||
@ldap_auth_disabled@
|
@ldap_auth_disabled@
|
||||||
backend = ldap
|
backend = ldap
|
||||||
target = user
|
|
||||||
resource = internal_ldap
|
resource = internal_ldap
|
||||||
; Object class of the user
|
; Object class of the user
|
||||||
user_class = @ldap_user_objectclass@
|
user_class = @ldap_user_objectclass@
|
||||||
|
@ -25,5 +19,4 @@ user_name_attribute = @ldap_attribute_username@
|
||||||
[internal_db_authentication]
|
[internal_db_authentication]
|
||||||
@internal_auth_disabled@
|
@internal_auth_disabled@
|
||||||
backend = db
|
backend = db
|
||||||
target = "user"
|
|
||||||
resource = "internal_db"
|
resource = "internal_db"
|
||||||
|
|
|
@ -50,8 +50,6 @@ use Icinga\Exception\NotReadableError;
|
||||||
* Direct instantiation is not permitted, the AuthenticationManager
|
* Direct instantiation is not permitted, the AuthenticationManager
|
||||||
* must be created using the getInstance method. Subsequent getInstance
|
* must be created using the getInstance method. Subsequent getInstance
|
||||||
* calls return the same object and ignore any additional configuration.
|
* calls return the same object and ignore any additional configuration.
|
||||||
*
|
|
||||||
* @TODO(mh): Group support is not implemented yet (#4624)
|
|
||||||
**/
|
**/
|
||||||
class Manager
|
class Manager
|
||||||
{
|
{
|
||||||
|
@ -76,13 +74,6 @@ class Manager
|
||||||
**/
|
**/
|
||||||
private $userBackends = array();
|
private $userBackends = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of group backends
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
**/
|
|
||||||
private $groupBackends = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration
|
* The configuration
|
||||||
*
|
*
|
||||||
|
@ -125,64 +116,55 @@ class Manager
|
||||||
private function setupBackends(Zend_Config $config)
|
private function setupBackends(Zend_Config $config)
|
||||||
{
|
{
|
||||||
foreach ($config as $name => $backendConfig) {
|
foreach ($config as $name => $backendConfig) {
|
||||||
// We won't initialize disabled backends
|
if ((bool) $backendConfig->get('disabled', false) === true) {
|
||||||
if ($backendConfig->get('disabled') == '1') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($backendConfig->name === null) {
|
if ($backendConfig->name === null) {
|
||||||
$backendConfig->name = $name;
|
$backendConfig->name = $name;
|
||||||
}
|
}
|
||||||
$backend = $this->createBackend($backendConfig);
|
$backend = $this->createBackend($backendConfig);
|
||||||
if ($backend instanceof UserBackend) {
|
|
||||||
$backend->connect();
|
|
||||||
$this->userBackends[$backend->getName()] = $backend;
|
$this->userBackends[$backend->getName()] = $backend;
|
||||||
} elseif ($backend instanceof GroupBackend) {
|
|
||||||
$backend->connect();
|
|
||||||
$this->groupBackends[$backend->getName()] = $backend;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a single backend from the given Zend_Config
|
* Create a backend from the given Zend_Config
|
||||||
*
|
*
|
||||||
* @param Zend_Config $backendConfig
|
* @param Zend_Config $backendConfig
|
||||||
*
|
*
|
||||||
* @return null|UserBackend
|
* @return UserBackend
|
||||||
|
* @throws ConfigurationError
|
||||||
*/
|
*/
|
||||||
private function createBackend(Zend_Config $backendConfig)
|
private function createBackend(Zend_Config $backendConfig)
|
||||||
{
|
{
|
||||||
$target = ucwords(strtolower($backendConfig->target));
|
|
||||||
$name = $backendConfig->name;
|
|
||||||
// TODO: implement support for groups (#4624) and remove OR-Clause
|
|
||||||
if ((!$target || strtolower($target) != "user") && !$backendConfig->class) {
|
|
||||||
Logger::warn('AuthManager: Backend "%s" has no target configuration. (e.g. target=user|group)', $name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (isset($backendConfig->class)) {
|
if (isset($backendConfig->class)) {
|
||||||
// use a custom backend class, this is probably only useful for testing
|
// Use a custom backend class, this is only useful for testing
|
||||||
if (!class_exists($backendConfig->class)) {
|
if (!class_exists($backendConfig->class)) {
|
||||||
Logger::error('AuthManager: Class not found (%s) for backend %s', $backendConfig->class, $name);
|
throw new ConfigurationError(
|
||||||
return null;
|
'Authentication configuration for backend "' . $backendConfig->name . '" defines an invalid backend'
|
||||||
|
. ' class. Backend class "' . $backendConfig->class. '" not found'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$class = $backendConfig->class;
|
return new $backendConfig->class($backendConfig);
|
||||||
return new $class($backendConfig);
|
|
||||||
}
|
}
|
||||||
|
if (($type = ResourceFactory::getResourceConfig($backendConfig->resource)->type) === null) {
|
||||||
switch (ResourceFactory::getResourceConfig($backendConfig->resource)->type) {
|
throw new ConfigurationError(
|
||||||
|
'Authentication configuration for backend "%s" is missing the type directive',
|
||||||
|
$backendConfig->name,
|
||||||
|
$backendConfig->class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch (strtolower($type)) {
|
||||||
case 'db':
|
case 'db':
|
||||||
return new DbUserBackend($backendConfig);
|
return new DbUserBackend($backendConfig);
|
||||||
case 'ldap':
|
case 'ldap':
|
||||||
return new LdapUserBackend($backendConfig);
|
return new LdapUserBackend($backendConfig);
|
||||||
default:
|
default:
|
||||||
Logger::warn('AuthManager: Resource type ' . $backendConfig->type . ' not available.');
|
throw new ConfigurationError(
|
||||||
|
'Authentication configuration for backend "' . $backendConfig->name. '" defines an invalid backend'
|
||||||
|
. ' type. Backend type "' . $type . '" is not supported'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
|
||||||
Logger::warn('AuthManager: Not able to create backend. Exception was thrown: %s', $e->getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,79 +190,65 @@ class Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a group backend to the stack
|
* Find the backend which provides the user with the given credentials
|
||||||
*
|
|
||||||
* @param GroupBackend $groupBackend
|
|
||||||
*/
|
|
||||||
public function addGroupBackend(GroupBackend $groupBackend)
|
|
||||||
{
|
|
||||||
$this->groupBackends[$groupBackend->getName()] = $groupBackend;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a group backend by name
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return GroupBackend|null
|
|
||||||
*/
|
|
||||||
public function getGroupBackend($name)
|
|
||||||
{
|
|
||||||
return (isset($this->groupBackends[$name])) ? $this->groupBackends[$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a backend for the given credentials
|
|
||||||
*
|
*
|
||||||
* @param Credential $credentials
|
* @param Credential $credentials
|
||||||
*
|
*
|
||||||
* @return UserBackend|null
|
* @return UserBackend|null
|
||||||
* @throws ConfigurationError
|
* @throws ConfigurationError
|
||||||
*/
|
*/
|
||||||
private function getBackendForCredential(Credential $credentials)
|
private function revealBackend(Credential $credentials)
|
||||||
{
|
{
|
||||||
$authErrors = 0;
|
if (count($this->userBackends) === 0) {
|
||||||
|
throw new ConfigurationError(
|
||||||
foreach ($this->userBackends as $userBackend) {
|
'No authentication methods available. It seems that none authentication method has been set up. '
|
||||||
|
. ' Please contact your Icinga Web administrator'
|
||||||
$flag = false;
|
);
|
||||||
|
}
|
||||||
try {
|
$backendsWithError = 0;
|
||||||
|
// 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 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 are available.
|
||||||
|
foreach ($this->userBackends as $backend) {
|
||||||
Logger::debug(
|
Logger::debug(
|
||||||
'AuthManager: Try backend %s for user %s',
|
'Asking authentication backend "%s" for user "%s"',
|
||||||
$userBackend->getName(),
|
$backend->getName(),
|
||||||
$credentials->getUsername()
|
$credentials->getUsername()
|
||||||
);
|
);
|
||||||
$flag = $userBackend->hasUsername($credentials);
|
try {
|
||||||
|
$hasUser = $backend->hasUsername($credentials);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Logger::error(
|
Logger::error(
|
||||||
'AuthManager: Backend "%s" has errors. Exception was thrown: %s',
|
'Cannot ask authentication backend "%s" for user "%s". An exception was thrown: %s',
|
||||||
$userBackend->getName(),
|
$backend->getName(),
|
||||||
|
$credentials->getUsername(),
|
||||||
$e->getMessage()
|
$e->getMessage()
|
||||||
);
|
);
|
||||||
|
++$backendsWithError;
|
||||||
$authErrors++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($hasUser === true) {
|
||||||
if ($flag === true) {
|
|
||||||
Logger::debug(
|
Logger::debug(
|
||||||
'AuthManager: Backend %s has user %s',
|
'Authentication backend "%s" provides user "%s"',
|
||||||
$userBackend->getName(),
|
$backend->getName(),
|
||||||
$credentials->getUsername()
|
$credentials->getUsername()
|
||||||
);
|
);
|
||||||
return $userBackend;
|
return $backend;
|
||||||
}
|
} else {
|
||||||
}
|
Logger::debug(
|
||||||
|
'Authentication backend "%s" does not provide user "%s"',
|
||||||
if ($authErrors >= count($this->userBackends)) {
|
$backend->getName(),
|
||||||
Logger::fatal('AuthManager: No working backend found, unable to authenticate any user');
|
$credentials->getUsername()
|
||||||
throw new ConfigurationError(
|
);
|
||||||
'No working backend found. Unable to authenticate any user.' .
|
}
|
||||||
"\nPlease examine the logs for more information."
|
}
|
||||||
|
if ($backendsWithError === count($this->userBackends)) {
|
||||||
|
throw new ConfigurationError(
|
||||||
|
'No authentication methods available. It seems that all set up authentication methods have errors. '
|
||||||
|
. ' Please contact your Icinga Web administrator'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,26 +263,13 @@ class Manager
|
||||||
*/
|
*/
|
||||||
public function authenticate(Credential $credentials, $persist = true)
|
public function authenticate(Credential $credentials, $persist = true)
|
||||||
{
|
{
|
||||||
if (count($this->userBackends) === 0) {
|
$userBackend = $this->revealBackend($credentials);
|
||||||
Logger::error('AuthManager: No authentication backend provided, your users will never be able to login.');
|
|
||||||
throw new ConfigurationError(
|
|
||||||
'No authentication backend set - login will never succeed as icinga-web'
|
|
||||||
. ' doesn\'t know how to determine your user. ' . "\n"
|
|
||||||
. ' To fix this error, setup your authentication.ini with at least one valid authentication backend.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$userBackend = $this->getBackendForCredential($credentials);
|
|
||||||
|
|
||||||
if ($userBackend === null) {
|
if ($userBackend === null) {
|
||||||
Logger::info('AuthManager: Unknown user %s tried to log in', $credentials->getUsername());
|
Logger::info('Unknown user "%s" tried to log in', $credentials->getUsername());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (($user = $userBackend->authenticate($credentials)) === null) {
|
||||||
$this->user = $userBackend->authenticate($credentials);
|
Logger::info('User "%s" tried to log in with an incorrect password', $credentials->getUsername());
|
||||||
|
|
||||||
if ($this->user === null) {
|
|
||||||
Logger::info('AuthManager: Invalid credentials for user %s provided', $credentials->getUsername());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,15 +278,15 @@ class Manager
|
||||||
$membership = new Membership();
|
$membership = new Membership();
|
||||||
|
|
||||||
$groups = $membership->getGroupsByUsername($username);
|
$groups = $membership->getGroupsByUsername($username);
|
||||||
$this->user->setGroups($groups);
|
$user->setGroups($groups);
|
||||||
|
|
||||||
$admissionLoader = new AdmissionLoader();
|
$admissionLoader = new AdmissionLoader();
|
||||||
|
|
||||||
$this->user->setPermissions(
|
$user->setPermissions(
|
||||||
$admissionLoader->getPermissions($username, $groups)
|
$admissionLoader->getPermissions($username, $groups)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->user->setRestrictions(
|
$user->setRestrictions(
|
||||||
$admissionLoader->getRestrictions($username, $groups)
|
$admissionLoader->getRestrictions($username, $groups)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -339,7 +294,7 @@ class Manager
|
||||||
try {
|
try {
|
||||||
$preferencesStore = PreferencesStore::create(
|
$preferencesStore = PreferencesStore::create(
|
||||||
$preferencesConfig,
|
$preferencesConfig,
|
||||||
$this->user
|
$user
|
||||||
);
|
);
|
||||||
$preferences = new Preferences($preferencesStore->load());
|
$preferences = new Preferences($preferencesStore->load());
|
||||||
} catch (NotReadableError $e) {
|
} catch (NotReadableError $e) {
|
||||||
|
@ -349,13 +304,13 @@ class Manager
|
||||||
} else {
|
} else {
|
||||||
$preferences = new Preferences();
|
$preferences = new Preferences();
|
||||||
}
|
}
|
||||||
$this->user->setPreferences($preferences);
|
$user->setPreferences($preferences);
|
||||||
|
$this->user = $user;
|
||||||
if ($persist == true) {
|
if ($persist == true) {
|
||||||
$this->persistCurrentUser();
|
$this->persistCurrentUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::info('AuthManager: User successfully logged in: %s', $credentials->getUsername());
|
Logger::info('User "%s" logged in successfully', $credentials->getUsername());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue