Change 'user' table to 'account', error handling and docs

Fix installation instructions for postgresql, the user table is now
'account', as user is a keyword in some rdbms, now catching exceptions
and returning auth failed while logging errors

refs #3769
This commit is contained in:
Jannis Moßhammer 2013-07-26 15:01:52 +02:00
parent ce95511890
commit 306a51271b
6 changed files with 135 additions and 83 deletions

View File

@ -1,7 +1,7 @@
[users] [users]
backend=Db backend=Db
dbtype=mysql dbtype=pgsql
table=user table=account
host=localhost host=localhost
password=icinga password=icinga
user=icingaweb user=icingaweb

View File

@ -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 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: 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 #### MySQL
@ -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) 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 Quick and Dirty
---------------- ----------------

View File

@ -1,4 +1,4 @@
create table user ( create table account (
user_name varchar(255) NOT NULL, user_name varchar(255) NOT NULL,
first_name varchar(255), first_name varchar(255),
last_name varchar(255), last_name varchar(255),
@ -15,7 +15,7 @@ create table user (
* user: icingaadmin * user: icingaadmin
* password: icinga * password: icinga
*/ */
INSERT INTO user ( INSERT INTO account (
user_name, user_name,
salt, salt,
password, password,

View File

@ -1,4 +1,4 @@
create table "user" ( create table "account" (
user_name varchar(255) NOT NULL, user_name varchar(255) NOT NULL,
first_name varchar(255), first_name varchar(255),
last_name varchar(255), last_name varchar(255),
@ -15,7 +15,7 @@ create table "user" (
* user: icingaadmin * user: icingaadmin
* password: icinga * password: icinga
*/ */
INSERT INTO "user" ( INSERT INTO "account" (
user_name, user_name,
salt, salt,
password, password,

View File

@ -33,7 +33,7 @@ use Icinga\Authentication\User as User;
use Icinga\Authentication\UserBackend; use Icinga\Authentication\UserBackend;
use Icinga\Authentication\Credentials; use Icinga\Authentication\Credentials;
use Icinga\Authentication; use Icinga\Authentication;
use Icinga\Application\Logger;
/** /**
* Authenticates users using a sql db as backend. * Authenticates users using a sql db as backend.
@ -41,26 +41,41 @@ use Icinga\Authentication;
*/ */
class DbUserBackend implements UserBackend { 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 $userTable;
private $USER_NAME_COLUMN = "user_name", /**
$FIRST_NAME_COLUMN = "first_name", * Mapping of columns
$LAST_NAME_COLUMN = "last_name", *
$LAST_LOGIN_COLUMN = "last_login", * @var string
$SALT_COLUMN = "salt", */
$PASSWORD_COLUMN = "password", private $USER_NAME_COLUMN = 'user_name',
$ACTIVE_COLUMN = "active", $FIRST_NAME_COLUMN = 'first_name',
$DOMAIN_COLUMN = "domain", $LAST_NAME_COLUMN = 'last_name',
$EMAIL_COLUMN = "email"; $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 * maps the configuration dbtypes to the corresponding Zend-PDOs
*/ */
private $dbTypeMap = Array( private $dbTypeMap = Array(
"mysql" => "PDO_MYSQL", 'mysql' => 'PDO_MYSQL',
"pgsql" => "PDO_PGSQL" 'pgsql' => 'PDO_PGSQL'
); );
/** /**
@ -72,7 +87,7 @@ class DbUserBackend implements UserBackend {
{ {
$this->dbtype = $config->dbtype; $this->dbtype = $config->dbtype;
$this->userTable = $config->table; $this->userTable = $config->table;
try {
$this->db = \Zend_Db::factory( $this->db = \Zend_Db::factory(
$this->dbTypeMap[$config->dbtype], $this->dbTypeMap[$config->dbtype],
array( array(
@ -86,7 +101,12 @@ class DbUserBackend implements UserBackend {
* Test the connection settings * Test the connection settings
*/ */
$this->db->getConnection(); $this->db->getConnection();
$this->db->select()->from($this->userTable,new \Zend_Db_Expr("TRUE")); $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;
}
} }
/** /**
@ -97,6 +117,10 @@ class DbUserBackend implements UserBackend {
*/ */
public function hasUsername(Credentials $credential) 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()); $user = $this->getUserByName($credential->getUsername());
return !empty($user); return !empty($user);
} }
@ -109,12 +133,16 @@ class DbUserBackend implements UserBackend {
*/ */
public function authenticate(Credentials $credential) public function authenticate(Credentials $credential)
{ {
if ($this->db === null) {
Logger::warn('Ignoring database authentication as no connection is available');
return null;
}
$this->db->getConnection(); $this->db->getConnection();
$res = $this->db $res = $this->db
->select()->from($this->userTable) ->select()->from($this->userTable)
->where($this->USER_NAME_COLUMN.' = ?',$credential->getUsername()) ->where($this->USER_NAME_COLUMN.' = ?',$credential->getUsername())
->where($this->ACTIVE_COLUMN. ' = ?',true) ->where($this->ACTIVE_COLUMN. ' = ?',true)
->where($this->PASSWORD_COLUMN. ' = ?',hash_hmac("sha256", ->where($this->PASSWORD_COLUMN. ' = ?',hash_hmac('sha256',
$this->getUserSalt($credential->getUsername()), $this->getUserSalt($credential->getUsername()),
$credential->getPassword()) $credential->getPassword())
) )
@ -137,7 +165,7 @@ class DbUserBackend implements UserBackend {
$this->db->update( $this->db->update(
$this->userTable, $this->userTable,
array( 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)); $this->USER_NAME_COLUMN.' = '.$this->db->quoteInto('?',$username));
} }
@ -166,6 +194,11 @@ class DbUserBackend implements UserBackend {
*/ */
private function getUserByName($username) private function getUserByName($username)
{ {
if ($this->db === null) {
Logger::warn('Ignoring getUserByName as no database connection is available');
return null;
}
try {
$this->db->getConnection(); $this->db->getConnection();
$res = $this->db-> $res = $this->db->
select()->from($this->userTable) select()->from($this->userTable)
@ -176,6 +209,10 @@ class DbUserBackend implements UserBackend {
return null; return null;
} }
return $this->createUserFromResult($res); return $this->createUserFromResult($res);
} catch (\Zend_Db_Statement_Exception $exc) {
Logger::error("Could not fetch users from db : %s ", $exc->getMessage());
return null;
}
} }
/** /**

View File

@ -8,14 +8,14 @@ namespace Tests\Icinga\Authentication;
//use Icinga\Protocol\Ldap\Exception; //use Icinga\Protocol\Ldap\Exception;
//use Zend_Config_Ini; //use Zend_Config_Ini;
require_once("Zend/Config/Ini.php"); require_once('Zend/Config/Ini.php');
require_once("Zend/Db.php"); require_once('Zend/Db.php');
require_once("../../library/Icinga/Authentication/UserBackend.php"); require_once('../../library/Icinga/Authentication/UserBackend.php');
require_once("../../library/Icinga/Protocol/Ldap/Exception.php"); require_once('../../library/Icinga/Protocol/Ldap/Exception.php');
require_once("../../library/Icinga/Application/Config.php"); require_once('../../library/Icinga/Application/Config.php');
require_once("../../library/Icinga/Authentication/Credentials.php"); require_once('../../library/Icinga/Authentication/Credentials.php');
require_once("../../library/Icinga/Authentication/Backend/DbUserBackend.php"); require_once('../../library/Icinga/Authentication/Backend/DbUserBackend.php');
require_once("../../library/Icinga/Authentication/User.php"); require_once('../../library/Icinga/Authentication/User.php');
use Icinga\Authentication\Backend\DbUserBackend; use Icinga\Authentication\Backend\DbUserBackend;
use Icinga\Util\Crypto; use Icinga\Util\Crypto;
@ -33,21 +33,21 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
private $dbUserBackend; private $dbUserBackend;
private $db; private $db;
private $testTable = "icinga_users_test"; private $testTable = 'account';
private $testDatabase = "icinga_unittest"; private $testDatabase = 'icinga_unittest';
/* /*
* Must be identical with the column names defined in DbUserBackend * Must be identical with the column names defined in DbUserBackend
*/ */
private $USER_NAME_COLUMN = "user_name", private $USER_NAME_COLUMN = 'user_name',
$FIRST_NAME_COLUMN = "first_name", $FIRST_NAME_COLUMN = 'first_name',
$LAST_NAME_COLUMN = "last_name", $LAST_NAME_COLUMN = 'last_name',
$LAST_LOGIN_COLUMN = "last_login", $LAST_LOGIN_COLUMN = 'last_login',
$SALT_COLUMN = "salt", $SALT_COLUMN = 'salt',
$PASSWORD_COLUMN = "password", $PASSWORD_COLUMN = 'password',
$ACTIVE_COLUMN = "active", $ACTIVE_COLUMN = 'active',
$DOMAIN_COLUMN = "domain", $DOMAIN_COLUMN = 'domain',
$EMAIL_COLUMN = "email"; $EMAIL_COLUMN = 'email';
private $users; private $users;
private $mysql; private $mysql;
@ -67,9 +67,9 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
private function getBackendConfig() private function getBackendConfig()
{ {
$config = new \stdClass(); $config = new \stdClass();
$config->host = "127.0.0.1"; $config->host = '127.0.0.1';
$config->user = "icinga_unittest"; $config->user = 'icinga_unittest';
$config->password= "icinga_unittest"; $config->password= 'icinga_unittest';
$config->table = $this->testTable; $config->table = $this->testTable;
$config->db = $this->testDatabase; $config->db = $this->testDatabase;
return $config; return $config;
@ -78,7 +78,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
/** /**
* Create a backend with the given database type * 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 * @return DbUserBackend|null
*/ */
private function createBackend($dbType) private function createBackend($dbType)
@ -90,7 +90,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$this->setUpDb($db); $this->setUpDb($db);
return new DbUserBackend($config); return new DbUserBackend($config);
} catch(\Exception $e) { } catch(\Exception $e) {
echo "CREATE_BACKEND_ERROR:".$e->getMessage(); echo 'CREATE_BACKEND_ERROR:'.$e->getMessage();
return null; return null;
} }
} }
@ -120,8 +120,8 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$this->ACTIVE_COLUMN => 0 $this->ACTIVE_COLUMN => 0
) )
); );
$this->mysql = $this->createBackend("mysql"); $this->mysql = $this->createBackend('mysql');
$this->pgsql = $this->createBackend("pgsql"); $this->pgsql = $this->createBackend('pgsql');
} }
/** /**
@ -134,7 +134,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$this->runBackendUsername($this->pgsql); $this->runBackendUsername($this->pgsql);
} }
else{ else{
echo "\nSKIPPING PGSQL TEST...\n"; echo '\nSKIPPING PGSQL TEST...\n';
$this->markTestSkipped(); $this->markTestSkipped();
} }
} }
@ -149,7 +149,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$this->runBackendUsername($this->mysql); $this->runBackendUsername($this->mysql);
} }
else{ else{
echo "\nSKIPPING MYSQL TEST...\n"; echo '\nSKIPPING MYSQL TEST...\n';
$this->markTestSkipped(); $this->markTestSkipped();
} }
} }
@ -157,7 +157,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
/** /**
* Create a database with the given config and type * 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. * @param $config The configuration-object.
* @return mixed * @return mixed
*/ */
@ -168,7 +168,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
'host' => $config->host, 'host' => $config->host,
'username' => $config->user, 'username' => $config->user,
'password' => $config->password, 'password' => $config->password,
"dbname" => "icinga_unittest" 'dbname' => 'icinga_unittest'
)); ));
} }
@ -178,11 +178,11 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
public function tearDown() public function tearDown()
{ {
try{ try{
$db = $this->createDb("mysql",$this->getBackendConfig()); $db = $this->createDb('mysql',$this->getBackendConfig());
$this->tearDownDb($db); $this->tearDownDb($db);
} catch(\Exception $e) { } } catch(\Exception $e) { }
try { try {
$db = $this->createDb("pgsql",$this->getBackendConfig()); $db = $this->createDb('pgsql',$this->getBackendConfig());
$this->tearDownDb($db); $this->tearDownDb($db);
} catch(\Exception $e) { } } catch(\Exception $e) { }
} }
@ -204,6 +204,11 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
*/ */
private function setUpDb($db) 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.' ( $db->exec('CREATE TABLE '.$this->testTable.' (
'.$this->USER_NAME_COLUMN.' varchar(255) NOT NULL, '.$this->USER_NAME_COLUMN.' varchar(255) NOT NULL,
'.$this->FIRST_NAME_COLUMN.' varchar(255), '.$this->FIRST_NAME_COLUMN.' varchar(255),
@ -220,7 +225,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$usr = $this->users[$i]; $usr = $this->users[$i];
$data = Array( $data = Array(
$this->USER_NAME_COLUMN => $usr[$this->USER_NAME_COLUMN], $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->SALT_COLUMN],
$usr[$this->PASSWORD_COLUMN] $usr[$this->PASSWORD_COLUMN]
), ),