diff --git a/config/authentication.ini b/config/authentication.ini index 381dc9df0..beade9aa5 100644 --- a/config/authentication.ini +++ b/config/authentication.ini @@ -1,7 +1,7 @@ [users] backend=Db -dbtype=mysql -table=user +dbtype=pgsql +table=account host=localhost password=icinga user=icingaweb diff --git a/doc/installation.md b/doc/installation.md index 3170bf4b3..0beb33331 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -43,7 +43,7 @@ your backend, take a look at the various options described in `./configure --hel It is required to set up all used Databases correctly, which basically means to create all needed user accounts and to create all database tables. You will find the installation guides for the different databases in the sections below: -*IMPORTANT*: Select a secure password instead of "icinga" and alter the config/*.ini accordingly. +*IMPORTANT*: Select a secure password instead of "icinga" and alter the config/authentication.ini accordingly. #### MySQL @@ -51,12 +51,12 @@ create all database tables. You will find the installation guides for the differ 1. Create the user and the database - mysql -u root -p - mysql> CREATE USER `icingaweb`@`localhost` IDENTIFIED BY 'icinga'; - mysql> CREATE DATABASE `icingaweb`; - mysql> GRANT ALL PRIVILEGES ON `icingaweb`.* TO `icingaweb`@`localhost`; - mysql> FLUSH PRIVILEGES; - mysql> quit + mysql -u root -p + mysql> CREATE USER `icingaweb`@`localhost` IDENTIFIED BY 'icinga'; + mysql> CREATE DATABASE `icingaweb`; + mysql> GRANT ALL PRIVILEGES ON `icingaweb`.* TO `icingaweb`@`localhost`; + mysql> FLUSH PRIVILEGES; + mysql> quit 2. Create all tables (You need to be in the icinga2-web folder) @@ -80,8 +80,18 @@ create all database tables. You will find the installation guides for the differ 2. Create all tables (You need to be in the icinga2-web folder) - bash$ psql -d icingaweb -a -f etc/schema/users.mysql.sql + bash$ psql -U icingaweb -a -f etc/schema/users.pgsql.sql +3. Enable trust authentication on localhost + +Add the following lines to your pg_hba.conf (etc/postgresql/X.x/main/pg_hba.conf under debian, /var/lib/pgsql/data/pg_hba.conf for Redhat/Fedora) +to enable trust authentication for the icingaweb user when connecting from the localhost. + + local icingaweb icingaweb trust + host icingaweb icingaweb 127.0.0.1/32 trust + host icingaweb icingaweb ::1/128 trust + +And restart your databse ('service postgresql restart' or '/etc/init.d/postgresql-X.x reload') Quick and Dirty ---------------- diff --git a/etc/schema/users.mysql.sql b/etc/schema/users.mysql.sql index e564050e0..48800da75 100644 --- a/etc/schema/users.mysql.sql +++ b/etc/schema/users.mysql.sql @@ -1,4 +1,4 @@ -create table user ( +create table account ( user_name varchar(255) NOT NULL, first_name varchar(255), last_name varchar(255), @@ -15,7 +15,7 @@ create table user ( * user: icingaadmin * password: icinga */ -INSERT INTO user ( +INSERT INTO account ( user_name, salt, password, diff --git a/etc/schema/users.pgsql.sql b/etc/schema/users.pgsql.sql index f0e3d511b..3c69499f2 100644 --- a/etc/schema/users.pgsql.sql +++ b/etc/schema/users.pgsql.sql @@ -1,4 +1,4 @@ -create table "user" ( +create table "account" ( user_name varchar(255) NOT NULL, first_name varchar(255), last_name varchar(255), @@ -15,7 +15,7 @@ create table "user" ( * user: icingaadmin * password: icinga */ -INSERT INTO "user" ( +INSERT INTO "account" ( user_name, salt, password, diff --git a/library/Icinga/Authentication/Backend/DbUserBackend.php b/library/Icinga/Authentication/Backend/DbUserBackend.php index a1e0f785e..a971f2618 100644 --- a/library/Icinga/Authentication/Backend/DbUserBackend.php +++ b/library/Icinga/Authentication/Backend/DbUserBackend.php @@ -33,7 +33,7 @@ use Icinga\Authentication\User as User; use Icinga\Authentication\UserBackend; use Icinga\Authentication\Credentials; use Icinga\Authentication; - +use Icinga\Application\Logger; /** * Authenticates users using a sql db as backend. @@ -41,26 +41,41 @@ use Icinga\Authentication; */ class DbUserBackend implements UserBackend { - private $db; + /** + * The database connection that will be used for fetching users + * + * @var \Zend_Db + */ + private $db = null; + /** + * The name of the user table as provided by the configuration + * + * @var String + */ private $userTable; - private $USER_NAME_COLUMN = "user_name", - $FIRST_NAME_COLUMN = "first_name", - $LAST_NAME_COLUMN = "last_name", - $LAST_LOGIN_COLUMN = "last_login", - $SALT_COLUMN = "salt", - $PASSWORD_COLUMN = "password", - $ACTIVE_COLUMN = "active", - $DOMAIN_COLUMN = "domain", - $EMAIL_COLUMN = "email"; + /** + * Mapping of columns + * + * @var string + */ + private $USER_NAME_COLUMN = 'user_name', + $FIRST_NAME_COLUMN = 'first_name', + $LAST_NAME_COLUMN = 'last_name', + $LAST_LOGIN_COLUMN = 'last_login', + $SALT_COLUMN = 'salt', + $PASSWORD_COLUMN = 'password', + $ACTIVE_COLUMN = 'active', + $DOMAIN_COLUMN = 'domain', + $EMAIL_COLUMN = 'email'; /* * maps the configuration dbtypes to the corresponding Zend-PDOs */ private $dbTypeMap = Array( - "mysql" => "PDO_MYSQL", - "pgsql" => "PDO_PGSQL" + 'mysql' => 'PDO_MYSQL', + 'pgsql' => 'PDO_PGSQL' ); /** @@ -72,21 +87,26 @@ class DbUserBackend implements UserBackend { { $this->dbtype = $config->dbtype; $this->userTable = $config->table; + try { + $this->db = \Zend_Db::factory( + $this->dbTypeMap[$config->dbtype], + array( + 'host' => $config->host, + 'username' => $config->user, + 'password' => $config->password, + 'dbname' => $config->db + )); - $this->db = \Zend_Db::factory( - $this->dbTypeMap[$config->dbtype], - array( - 'host' => $config->host, - 'username' => $config->user, - 'password' => $config->password, - 'dbname' => $config->db - )); + /* + * Test the connection settings + */ + $this->db->getConnection(); + $this->db->select()->from($this->userTable,new \Zend_Db_Expr('TRUE')); + } catch (\Zend_Db_Adapter_Exception $exc) { + Logger::error('Could not authenticate via database : %s ', $exc->getMessage()); + $this->db = null; - /* - * Test the connection settings - */ - $this->db->getConnection(); - $this->db->select()->from($this->userTable,new \Zend_Db_Expr("TRUE")); + } } /** @@ -97,6 +117,10 @@ class DbUserBackend implements UserBackend { */ public function hasUsername(Credentials $credential) { + if ($this->db === null) { + Logger::warn('Ignoring hasUsername in database as no connection is available'); + return false; + } $user = $this->getUserByName($credential->getUsername()); return !empty($user); } @@ -109,12 +133,16 @@ class DbUserBackend implements UserBackend { */ public function authenticate(Credentials $credential) { + if ($this->db === null) { + Logger::warn('Ignoring database authentication as no connection is available'); + return null; + } $this->db->getConnection(); $res = $this->db ->select()->from($this->userTable) ->where($this->USER_NAME_COLUMN.' = ?',$credential->getUsername()) ->where($this->ACTIVE_COLUMN. ' = ?',true) - ->where($this->PASSWORD_COLUMN. ' = ?',hash_hmac("sha256", + ->where($this->PASSWORD_COLUMN. ' = ?',hash_hmac('sha256', $this->getUserSalt($credential->getUsername()), $credential->getPassword()) ) @@ -137,7 +165,7 @@ class DbUserBackend implements UserBackend { $this->db->update( $this->userTable, array( - $this->LAST_LOGIN_COLUMN => new \Zend_Db_Expr("NOW()") + $this->LAST_LOGIN_COLUMN => new \Zend_Db_Expr('NOW()') ), $this->USER_NAME_COLUMN.' = '.$this->db->quoteInto('?',$username)); } @@ -166,16 +194,25 @@ class DbUserBackend implements UserBackend { */ private function getUserByName($username) { - $this->db->getConnection(); - $res = $this->db-> - select()->from($this->userTable) - ->where($this->USER_NAME_COLUMN.' = ?',$username) - ->where($this->ACTIVE_COLUMN.' = ?',true) - ->query()->fetch(); - if (empty($res)) { + if ($this->db === null) { + Logger::warn('Ignoring getUserByName as no database connection is available'); + return null; + } + try { + $this->db->getConnection(); + $res = $this->db-> + select()->from($this->userTable) + ->where($this->USER_NAME_COLUMN.' = ?',$username) + ->where($this->ACTIVE_COLUMN.' = ?',true) + ->query()->fetch(); + if (empty($res)) { + return null; + } + return $this->createUserFromResult($res); + } catch (\Zend_Db_Statement_Exception $exc) { + Logger::error("Could not fetch users from db : %s ", $exc->getMessage()); return null; } - return $this->createUserFromResult($res); } /** diff --git a/test/php/library/Icinga/Authentication/DbUserBackendTest.php b/test/php/library/Icinga/Authentication/DbUserBackendTest.php index c45582d9b..495fe8dbc 100644 --- a/test/php/library/Icinga/Authentication/DbUserBackendTest.php +++ b/test/php/library/Icinga/Authentication/DbUserBackendTest.php @@ -8,14 +8,14 @@ namespace Tests\Icinga\Authentication; //use Icinga\Protocol\Ldap\Exception; //use Zend_Config_Ini; -require_once("Zend/Config/Ini.php"); -require_once("Zend/Db.php"); -require_once("../../library/Icinga/Authentication/UserBackend.php"); -require_once("../../library/Icinga/Protocol/Ldap/Exception.php"); -require_once("../../library/Icinga/Application/Config.php"); -require_once("../../library/Icinga/Authentication/Credentials.php"); -require_once("../../library/Icinga/Authentication/Backend/DbUserBackend.php"); -require_once("../../library/Icinga/Authentication/User.php"); +require_once('Zend/Config/Ini.php'); +require_once('Zend/Db.php'); +require_once('../../library/Icinga/Authentication/UserBackend.php'); +require_once('../../library/Icinga/Protocol/Ldap/Exception.php'); +require_once('../../library/Icinga/Application/Config.php'); +require_once('../../library/Icinga/Authentication/Credentials.php'); +require_once('../../library/Icinga/Authentication/Backend/DbUserBackend.php'); +require_once('../../library/Icinga/Authentication/User.php'); use Icinga\Authentication\Backend\DbUserBackend; use Icinga\Util\Crypto; @@ -33,21 +33,21 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { private $dbUserBackend; private $db; - private $testTable = "icinga_users_test"; - private $testDatabase = "icinga_unittest"; + private $testTable = 'account'; + private $testDatabase = 'icinga_unittest'; /* * Must be identical with the column names defined in DbUserBackend */ - private $USER_NAME_COLUMN = "user_name", - $FIRST_NAME_COLUMN = "first_name", - $LAST_NAME_COLUMN = "last_name", - $LAST_LOGIN_COLUMN = "last_login", - $SALT_COLUMN = "salt", - $PASSWORD_COLUMN = "password", - $ACTIVE_COLUMN = "active", - $DOMAIN_COLUMN = "domain", - $EMAIL_COLUMN = "email"; + private $USER_NAME_COLUMN = 'user_name', + $FIRST_NAME_COLUMN = 'first_name', + $LAST_NAME_COLUMN = 'last_name', + $LAST_LOGIN_COLUMN = 'last_login', + $SALT_COLUMN = 'salt', + $PASSWORD_COLUMN = 'password', + $ACTIVE_COLUMN = 'active', + $DOMAIN_COLUMN = 'domain', + $EMAIL_COLUMN = 'email'; private $users; private $mysql; @@ -67,9 +67,9 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { private function getBackendConfig() { $config = new \stdClass(); - $config->host = "127.0.0.1"; - $config->user = "icinga_unittest"; - $config->password= "icinga_unittest"; + $config->host = '127.0.0.1'; + $config->user = 'icinga_unittest'; + $config->password= 'icinga_unittest'; $config->table = $this->testTable; $config->db = $this->testDatabase; return $config; @@ -78,7 +78,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { /** * Create a backend with the given database type * - * @param $dbType The database type as a string, like "mysql" or "pgsql". + * @param $dbType The database type as a string, like 'mysql' or 'pgsql'. * @return DbUserBackend|null */ private function createBackend($dbType) @@ -90,7 +90,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $this->setUpDb($db); return new DbUserBackend($config); } catch(\Exception $e) { - echo "CREATE_BACKEND_ERROR:".$e->getMessage(); + echo 'CREATE_BACKEND_ERROR:'.$e->getMessage(); return null; } } @@ -120,8 +120,8 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $this->ACTIVE_COLUMN => 0 ) ); - $this->mysql = $this->createBackend("mysql"); - $this->pgsql = $this->createBackend("pgsql"); + $this->mysql = $this->createBackend('mysql'); + $this->pgsql = $this->createBackend('pgsql'); } /** @@ -134,7 +134,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $this->runBackendUsername($this->pgsql); } else{ - echo "\nSKIPPING PGSQL TEST...\n"; + echo '\nSKIPPING PGSQL TEST...\n'; $this->markTestSkipped(); } } @@ -149,7 +149,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $this->runBackendUsername($this->mysql); } else{ - echo "\nSKIPPING MYSQL TEST...\n"; + echo '\nSKIPPING MYSQL TEST...\n'; $this->markTestSkipped(); } } @@ -157,7 +157,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { /** * Create a database with the given config and type * - * @param $dbtype The database type as a string, like "mysql" or "pgsql". + * @param $dbtype The database type as a string, like 'mysql' or 'pgsql'. * @param $config The configuration-object. * @return mixed */ @@ -168,7 +168,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { 'host' => $config->host, 'username' => $config->user, 'password' => $config->password, - "dbname" => "icinga_unittest" + 'dbname' => 'icinga_unittest' )); } @@ -178,11 +178,11 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { public function tearDown() { try{ - $db = $this->createDb("mysql",$this->getBackendConfig()); + $db = $this->createDb('mysql',$this->getBackendConfig()); $this->tearDownDb($db); } catch(\Exception $e) { } try { - $db = $this->createDb("pgsql",$this->getBackendConfig()); + $db = $this->createDb('pgsql',$this->getBackendConfig()); $this->tearDownDb($db); } catch(\Exception $e) { } } @@ -204,6 +204,11 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { */ private function setUpDb($db) { + try { + $this->tearDownDb($db); + } catch (\Exception $e) { + // if no database exists, an exception will be thrown + } $db->exec('CREATE TABLE '.$this->testTable.' ( '.$this->USER_NAME_COLUMN.' varchar(255) NOT NULL, '.$this->FIRST_NAME_COLUMN.' varchar(255), @@ -220,7 +225,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $usr = $this->users[$i]; $data = Array( $this->USER_NAME_COLUMN => $usr[$this->USER_NAME_COLUMN], - $this->PASSWORD_COLUMN => hash_hmac("sha256", + $this->PASSWORD_COLUMN => hash_hmac('sha256', $usr[$this->SALT_COLUMN], $usr[$this->PASSWORD_COLUMN] ),