Fix unit tests and add documentation

Add functionality to check if a certain database type like psql or mysql is
available and skip the tests accordingly.

Add documentation for backend authentication.

refs #3769
This commit is contained in:
Matthias Jentsch 2013-07-25 10:05:47 +02:00
parent 2807982f72
commit b013966464
2 changed files with 169 additions and 59 deletions

29
doc/authentication.md Normal file
View File

@ -0,0 +1,29 @@
# Authentication via internal DB
The class DbUserBackend allows
## Configuration
The internal authentication is configured in *config/authentication.ini*. The value
of the configuration key "backend" will determine which UserBackend class to
load. To use the internal backend you will need to specifiy the value "Db"
which will cause the class "DbUserBackend" to be loaded.
There are various configuration keys in "Authentication.ini" and some are only
used by specific backends. The internal DB uses the values
*dbtype*,*table*,*host*,*password*,*user* and *db*, which define the used
connection parameters, the database and the table.
## Database support
The module currently supports these databases:
- mysql (dbtype=mysql)
- PostgreSQL (dbtype=pgsql)
## Authentication
The backend will store the salted hash of the password in the column "password" and the salt in the column "salt".
When a password is checked, the hash is calculated with the function hash_hmac("sha256",salt,password) and compared
to the stored value.

View File

@ -34,6 +34,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
private $dbUserBackend; private $dbUserBackend;
private $db; private $db;
private $testTable = "icinga_users_test"; private $testTable = "icinga_users_test";
private $testDatabase = "icinga_unittest";
/* /*
* Must be identical with the column names defined in DbUserBackend * Must be identical with the column names defined in DbUserBackend
@ -49,13 +50,52 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$EMAIL_COLUMN = "email"; $EMAIL_COLUMN = "email";
private $users; private $users;
private $unknownUsers; private $mysql;
private $pgsql;
private $dbTypeMap = Array( private $dbTypeMap = Array(
'mysql' => 'PDO_MYSQL', 'mysql' => 'PDO_MYSQL',
'pgsql' => 'PDO_PGSQL' 'pgsql' => 'PDO_PGSQL'
); );
/**
* Create a preset-configuration that can be used to access the database
* with the icinga_unittest account.
* @return \stdClass
*/
private function getBackendConfig()
{
$config = new \stdClass();
$config->host = "127.0.0.1";
$config->user = "icinga_unittest";
$config->password= "icinga_unittest";
$config->table = $this->testTable;
$config->db = $this->testDatabase;
return $config;
}
/**
* Create a backend with the given database type.
* @param $dbType The database type as a string, like "mysql" or "pgsql".
* @return DbUserBackend|null
*/
private function createBackend($dbType){
try{
$config = $this->getBackendConfig();
$config->dbtype = $dbType;
$db = $this->createDb($dbType,$config);
$this->setUpDb($db);
return new DbUserBackend($config);
}
catch(\Exception $e){
echo "CREATE_BACKEND_ERROR:".$e->getMessage();
return null;
}
}
/**
* Create the backends and fill it with sample-data.
*/
protected function setUp() protected function setUp()
{ {
$this->users = Array( $this->users = Array(
@ -63,67 +103,111 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
$this->USER_NAME_COLUMN => 'user1', $this->USER_NAME_COLUMN => 'user1',
$this->PASSWORD_COLUMN => 'secret1', $this->PASSWORD_COLUMN => 'secret1',
$this->SALT_COLUMN => '8a7487a539c5d1d6766639d04d1ed1e6', $this->SALT_COLUMN => '8a7487a539c5d1d6766639d04d1ed1e6',
$this->ACTIVE_COLUMN => true $this->ACTIVE_COLUMN => 1
), ),
1 => Array( 1 => Array(
$this->USER_NAME_COLUMN => 'user2', $this->USER_NAME_COLUMN => 'user2',
$this->PASSWORD_COLUMN => 'secret2', $this->PASSWORD_COLUMN => 'secret2',
$this->SALT_COLUMN => '04b5521ddd761b5a5b633be83faa494d', $this->SALT_COLUMN => '04b5521ddd761b5a5b633be83faa494d',
$this->ACTIVE_COLUMN => true $this->ACTIVE_COLUMN => 1
), ),
2 => Array( 2 => Array(
$this->USER_NAME_COLUMN => 'user3', $this->USER_NAME_COLUMN => 'user3',
$this->PASSWORD_COLUMN => 'secret3', $this->PASSWORD_COLUMN => 'secret3',
$this->SALT_COLUMN => '08bb94ba3120338ae56db80ef551d324', $this->SALT_COLUMN => '08bb94ba3120338ae56db80ef551d324',
$this->ACTIVE_COLUMN => false $this->ACTIVE_COLUMN => 0
) )
); );
$this->mysql = $this->createBackend("mysql");
$this->pgsql = $this->createBackend("pgsql");
}
/* /**
* TODO: Fetch config folder from somewhere instead of defining it statically, or this test * Test the PostgreSQL backend.
* will break when the path changes */
*/ public function testPgsql(){
Config::$configDir = "/vagrant/config"; if(!empty($this->pgsql)){
$config = Config::app('authentication')->users; $this->runBackendAuthentication($this->pgsql);
$config->table = $this->testTable; $this->runBackendUsername($this->pgsql);
}
else{
echo "\nSKIPPING PGSQL TEST...\n";
$this->markTestSkipped();
}
}
$this->db = \Zend_Db::factory($this->dbTypeMap[$config->dbtype], /**
* Test the MySQL-Backend.
*/
public function testMySQL(){
if(!empty($this->mysql)){
$this->runBackendAuthentication($this->mysql);
$this->runBackendUsername($this->mysql);
}
else{
echo "\nSKIPPING MYSQL TEST...\n";
$this->markTestSkipped();
}
}
/**
* Create a database with the given config and type.
* @param $dbtype The database type as a string, like "mysql" or "pgsql".
* @param $config The configuration-object.
* @return mixed
*/
private function createDb($dbtype,$config){
return \Zend_Db::factory($this->dbTypeMap[$dbtype],
array( array(
'host' => $config->host, 'host' => $config->host,
'username' => $config->user, 'username' => $config->user,
'password' => $config->password, 'password' => $config->password,
'dbname' => $config->db "dbname" => "icinga_unittest"
)); ));
if($config->dbtype == 'pgsql'){
$this->users[0][$this->ACTIVE_COLUMN] = "TRUE";
$this->users[1][$this->ACTIVE_COLUMN] = "TRUE";
$this->users[2][$this->ACTIVE_COLUMN] = "FALSE";
}
$this->setUpDb($this->db);
$this->dbUserBackend = new DbUserBackend($config);
} }
/**
* Try to drop all databases that may eventually be present.
*/
public function tearDown() public function tearDown()
{ {
$this->tearDownDb($this->db); try{
$db = $this->createDb("mysql",$this->getBackendConfig());
$this->tearDownDb($db);
}
catch(\Exception $e){}
try{
$db = $this->createDb("pgsql",$this->getBackendConfig());
$this->tearDownDb($db);
}
catch(\Exception $e){}
} }
/**
* Drop the test database in the given db.
* @param $db
*/
private function tearDownDb($db){
$db->exec('DROP TABLE '.$this->testTable);
}
/**
* Fill the given database with the sample-data provided in users.
* @param $db
*/
private function setUpDb($db){ private function setUpDb($db){
$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),
'.$this->LAST_NAME_COLUMN.' varchar(255), '.$this->LAST_NAME_COLUMN.' varchar(255),
'.$this->LAST_LOGIN_COLUMN.' timestamp, '.$this->LAST_LOGIN_COLUMN.' timestamp,
'.$this->SALT_COLUMN.' varchar(255), '.$this->SALT_COLUMN.' varchar(255),
'.$this->DOMAIN_COLUMN.' varchar(255), '.$this->DOMAIN_COLUMN.' varchar(255),
'.$this->EMAIL_COLUMN.' varchar(255), '.$this->EMAIL_COLUMN.' varchar(255),
'.$this->PASSWORD_COLUMN.' varchar(255) NOT NULL, '.$this->PASSWORD_COLUMN.' varchar(255) NOT NULL,
'.$this->ACTIVE_COLUMN.' BOOL, '.$this->ACTIVE_COLUMN.' BOOL,
PRIMARY KEY ('.$this->USER_NAME_COLUMN.') PRIMARY KEY ('.$this->USER_NAME_COLUMN.')
)'); )');
for($i = 0; $i < count($this->users); $i++){ for($i = 0; $i < count($this->users); $i++){
$usr = $this->users[$i]; $usr = $this->users[$i];
$data = Array( $data = Array(
@ -139,45 +223,41 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
} }
} }
private function tearDownDb($db){
$db->exec('DROP TABLE '.$this->testTable);
}
/** /**
* Test for DbUserBackend::HasUsername() * Run the hasUsername test against an instance of DbUserBackend.
**/ * @param $backend The backend that will be tested.
public function testHasUsername(){ */
private function runBackendUsername($backend){
// Known user // Known user
$this->assertTrue($this->dbUserBackend->hasUsername( $this->assertTrue($backend->hasUsername(
new Credentials( new Credentials(
$this->users[0][$this->USER_NAME_COLUMN], $this->users[0][$this->USER_NAME_COLUMN],
$this->users[0][$this->PASSWORD_COLUMN]) $this->users[0][$this->PASSWORD_COLUMN])
)); ));
// Unknown user // Unknown user
$this->assertFalse($this->dbUserBackend->hasUsername( $this->assertFalse($backend->hasUsername(
new Credentials( new Credentials(
'unkown user', 'unkown user',
'secret') 'secret')
)); ));
// Inactive user // Inactive user
$this->assertFalse($this->dbUserBackend->hasUsername( $this->assertFalse($backend->hasUsername(
new Credentials( new Credentials(
$this->users[2][$this->USER_NAME_COLUMN], $this->users[2][$this->USER_NAME_COLUMN],
$this->users[2][$this->PASSWORD_COLUMN]) $this->users[2][$this->PASSWORD_COLUMN])
)); ));
} }
/** /**
* Test for DbUserBackend::Authenticate() * Run the authentication test against an instance of DbUserBackend.
* * @param $backend The backend that will be tested.
**/ */
public function testAuthenticate(){ private function runBackendAuthentication($backend){
// Known user // Known user
$this->assertNotNull($this->dbUserBackend->authenticate( $this->assertNotNull($backend->authenticate(
new Credentials( new Credentials(
$this->users[0][$this->USER_NAME_COLUMN], $this->users[0][$this->USER_NAME_COLUMN],
$this->users[0][$this->PASSWORD_COLUMN]) $this->users[0][$this->PASSWORD_COLUMN])
@ -185,27 +265,28 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase {
// Wrong password // Wrong password
$this->assertNull( $this->assertNull(
$this->dbUserBackend->authenticate( $backend->authenticate(
new Credentials( new Credentials(
$this->users[1][$this->USER_NAME_COLUMN], $this->users[1][$this->USER_NAME_COLUMN],
'wrongpassword') 'wrongpassword')
) )
); );
// Nonexistend user // Nonexisting user
$this->assertNull( $this->assertNull(
$this->dbUserBackend->authenticate( $backend->authenticate(
new Credentials( new Credentials(
'nonexistend user', 'nonexisting user',
$this->users[1][$this->PASSWORD_COLUMN]) $this->users[1][$this->PASSWORD_COLUMN])
) )
); );
// Inactive user // Inactive user
$this->assertNull($this->dbUserBackend->authenticate( $this->assertNull(
new Credentials( $backend->authenticate(
$this->users[2][$this->USER_NAME_COLUMN], new Credentials(
$this->users[2][$this->PASSWORD_COLUMN]) $this->users[2][$this->USER_NAME_COLUMN],
$this->users[2][$this->PASSWORD_COLUMN])
)); ));
} }
} }