Authentication: Add backend to handle external authentication

Drop external auth configuration from config.ini and move
implementation into a single backend provider named
'autologin'. This provider can strip realm names from
username with a custom regexp.

fixes #6081
This commit is contained in:
Marius Hein 2014-06-03 17:59:22 +02:00
parent a0459d0172
commit 29f593a357
8 changed files with 144 additions and 33 deletions

View File

@ -1,3 +1,9 @@
[autologin]
backend = autologin
;
; If you want to strip the domain
; strip_username_regexp = /\@[^$]+$/
[internal_ldap_authentication] [internal_ldap_authentication]
backend = ldap backend = ldap
resource = internal_ldap resource = internal_ldap

View File

@ -30,6 +30,7 @@
# namespace Icinga\Application\Controllers; # namespace Icinga\Application\Controllers;
use Icinga\Authentication\Backend\AutoLoginBackend;
use Icinga\Web\Controller\ActionController; use Icinga\Web\Controller\ActionController;
use Icinga\Authentication\Manager as AuthManager; use Icinga\Authentication\Manager as AuthManager;
use Icinga\Form\Authentication\LoginForm; use Icinga\Form\Authentication\LoginForm;
@ -62,6 +63,8 @@ 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 = $this->translate('Icingaweb Login'); $this->view->title = $this->translate('Icingaweb Login');
$user = new User('');
$password = '';
try { try {
$redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard')); $redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard'));
@ -71,29 +74,49 @@ class AuthenticationController extends ActionController
} }
$auth = AuthManager::getInstance(); $auth = AuthManager::getInstance();
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$this->redirectNow($redirectUrl); $this->redirectNow($redirectUrl);
} }
if ($this->view->form->isSubmittedAndValid()) { try {
try { $config = Config::app('authentication');
$config = Config::app('authentication'); } catch (NotReadableError $e) {
} catch (NotReadableError $e) { Logger::error(
Logger::error( new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e)
new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e) );
); throw new ConfigurationError(
throw new ConfigurationError( 'No authentication methods available. It seems that none authentication method has been set'
'No authentication methods available. It seems that none authentication method has been set' . ' up. Please check the system log or Icinga Web 2 log for more information'
. ' up. Please check the system log or Icinga Web 2 log for more information' );
); }
$chain = new AuthChain($config);
if ($this->getRequest()->isGet()) {
foreach ($chain as $backend) {
if ($backend instanceof AutoLoginBackend) {
$authenticated = $backend->authenticate($user, $password);
if ($authenticated === true) {
$auth->setAuthenticated($user);
$this->redirectNow($redirectUrl);
}
}
} }
} elseif ($this->view->form->isSubmittedAndValid()) {
$user = new User($this->view->form->getValue('username')); $user = new User($this->view->form->getValue('username'));
$password = $this->view->form->getValue('password'); $password = $this->view->form->getValue('password');
$backendsTried = 0; $backendsTried = 0;
$backendsWithError = 0; $backendsWithError = 0;
$chain = new AuthChain($config);
foreach ($chain as $backend) { foreach ($chain as $backend) {
++$backendsTried; ++$backendsTried;
if ($backend instanceof AutoLoginBackend) {
continue;
}
try { try {
$authenticated = $backend->authenticate($user, $password); $authenticated = $backend->authenticate($user, $password);
} catch (AuthenticationException $e) { } catch (AuthenticationException $e) {

View File

@ -6,6 +6,11 @@
; The backends will be processed from top to bottom using the first backend for authentication which reports that ; The backends will be processed from top to bottom using the first backend for authentication which reports that
; the user trying to log in is available. ; the user trying to log in is available.
[autologin]
backend = autologin
;
; If you want to strip the domain
; strip_username_regexp = /\@[^$]+$/
[internal_ldap_authentication] [internal_ldap_authentication]
@ldap_auth_disabled@ @ldap_auth_disabled@

View File

@ -11,10 +11,6 @@ timeFormat = "g:i A"
; won't show up in the list of disabled modules ; won't show up in the list of disabled modules
; modulePath = "/vagrant/modules:/usr/share/icingaweb/modules" ; modulePath = "/vagrant/modules:/usr/share/icingaweb/modules"
; The used authentication-mode can be either "internal" to handle the authentication in IcingaWeb
; or "external" to delegate the authentication to the used WebServer
authenticationMode = "internal"
[logging] [logging]
enable = true enable = true
; Writing to a Stream ; Writing to a Stream

View File

@ -211,23 +211,6 @@ class Web extends ApplicationBootstrap
if ($authenticationManager->isAuthenticated() === true) { if ($authenticationManager->isAuthenticated() === true) {
$this->user = $authenticationManager->getUser(); $this->user = $authenticationManager->getUser();
return $this;
}
try {
$config = Config::app();
} catch (NotReadableError $e) {
Logger::error(
new Exception('Cannot load global configuration (config.ini). An exception was thrown:', 0, $e)
);
$config = null;
}
if ($config !== null && $config->global !== null &&
$config->global->get('authenticationMode', 'internal') === 'external'
) {
$authenticationManager->authenticateFromRemoteUser();
$this->user = $authenticationManager->getUser();
} }
return $this; return $this;

View File

@ -0,0 +1,89 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Authentication\Backend;
use Icinga\Authentication\UserBackend;
use Icinga\User;
use \Zend_Config;
/**
* Test login with external authentication mechanism, e.g. Apache
*/
class AutoLoginBackend extends UserBackend
{
/**
* Regexp expression to strip values from a username
*
* @var string
*/
private $stripUsernameRegexp;
/**
* Create new autologin backend
*
* @param Zend_Config $config
*/
public function __construct(Zend_Config $config)
{
$this->stripUsernameRegexp = $config->get('strip_username_regexp');
}
/**
* (PHP 5 &gt;= 5.1.0)<br/>
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
*/
public function count()
{
return 1;
}
/**
* Test whether the given user exists
*
* @param User $user
*
* @return bool
*/
public function hasUser(User $user)
{
if (isset($_SERVER['PHP_AUTH_USER'])
&& isset($_SERVER['AUTH_TYPE'])
&& in_array($_SERVER['AUTH_TYPE'], array('Basic', 'Digest')) === true
) {
$username = filter_var(
$_SERVER['PHP_AUTH_USER'],
FILTER_SANITIZE_STRING,
FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW
);
if ($username !== false) {
if ($this->stripUsernameRegexp !== null) {
$username = preg_replace($this->stripUsernameRegexp, '', $username);
}
return true;
}
}
return false;
}
/**
* Authenticate
*
* @param User $user
* @param string $password
*
* @return bool
*/
public function authenticate(User $user, $password)
{
return $this->hasUser($user);
}
}

View File

@ -30,6 +30,7 @@
namespace Icinga\Authentication; namespace Icinga\Authentication;
use Countable; use Countable;
use Icinga\Authentication\Backend\AutoLoginBackend;
use Zend_Config; use Zend_Config;
use Icinga\Authentication\Backend\DbUserBackend; use Icinga\Authentication\Backend\DbUserBackend;
use Icinga\Authentication\Backend\LdapUserBackend; use Icinga\Authentication\Backend\LdapUserBackend;
@ -84,6 +85,11 @@ abstract class UserBackend implements Countable
} }
return new $backendConfig->class($backendConfig); return new $backendConfig->class($backendConfig);
} }
if ($name === 'autologin') {
$backend = new AutoLoginBackend($backendConfig);
$backend->setName($name);
return $backend;
}
if ($backendConfig->resource === null) { if ($backendConfig->resource === null) {
throw new ConfigurationError( throw new ConfigurationError(
'Authentication configuration for backend "' . $name 'Authentication configuration for backend "' . $name

View File

@ -287,6 +287,9 @@ class Monitoring_ListController extends Controller
) )
)->getQuery(); )->getQuery();
$this->view->contacts = $query->paginate(); $this->view->contacts = $query->paginate();
file_put_contents('/tmp/query.txt', (string) $query);
$this->setupSortControl(array( $this->setupSortControl(array(
'contact_name' => 'Name', 'contact_name' => 'Name',
'contact_alias' => 'Alias', 'contact_alias' => 'Alias',