From b0139664645fa3655c6ef4fa5b54eb367c47c7af Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 25 Jul 2013 10:05:47 +0200 Subject: [PATCH] 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 --- doc/authentication.md | 29 +++ .../Authentication/DbUserBackendTest.php | 199 ++++++++++++------ 2 files changed, 169 insertions(+), 59 deletions(-) create mode 100644 doc/authentication.md diff --git a/doc/authentication.md b/doc/authentication.md new file mode 100644 index 000000000..d05c8babd --- /dev/null +++ b/doc/authentication.md @@ -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. \ No newline at end of file diff --git a/test/php/library/Icinga/Authentication/DbUserBackendTest.php b/test/php/library/Icinga/Authentication/DbUserBackendTest.php index 7c39aa762..201d21c6c 100644 --- a/test/php/library/Icinga/Authentication/DbUserBackendTest.php +++ b/test/php/library/Icinga/Authentication/DbUserBackendTest.php @@ -34,6 +34,7 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { private $dbUserBackend; private $db; private $testTable = "icinga_users_test"; + private $testDatabase = "icinga_unittest"; /* * Must be identical with the column names defined in DbUserBackend @@ -49,13 +50,52 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $EMAIL_COLUMN = "email"; private $users; - private $unknownUsers; + private $mysql; + private $pgsql; private $dbTypeMap = Array( 'mysql' => 'PDO_MYSQL', '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() { $this->users = Array( @@ -63,67 +103,111 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { $this->USER_NAME_COLUMN => 'user1', $this->PASSWORD_COLUMN => 'secret1', $this->SALT_COLUMN => '8a7487a539c5d1d6766639d04d1ed1e6', - $this->ACTIVE_COLUMN => true + $this->ACTIVE_COLUMN => 1 ), 1 => Array( $this->USER_NAME_COLUMN => 'user2', $this->PASSWORD_COLUMN => 'secret2', $this->SALT_COLUMN => '04b5521ddd761b5a5b633be83faa494d', - $this->ACTIVE_COLUMN => true + $this->ACTIVE_COLUMN => 1 ), 2 => Array( $this->USER_NAME_COLUMN => 'user3', $this->PASSWORD_COLUMN => 'secret3', $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 - * will break when the path changes - */ - Config::$configDir = "/vagrant/config"; - $config = Config::app('authentication')->users; - $config->table = $this->testTable; + /** + * Test the PostgreSQL backend. + */ + public function testPgsql(){ + if(!empty($this->pgsql)){ + $this->runBackendAuthentication($this->pgsql); + $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( 'host' => $config->host, 'username' => $config->user, '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() { - $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){ - $db->exec('CREATE TABLE '.$this->testTable.' ( - '.$this->USER_NAME_COLUMN.' varchar(255) NOT NULL, - '.$this->FIRST_NAME_COLUMN.' varchar(255), - '.$this->LAST_NAME_COLUMN.' varchar(255), - '.$this->LAST_LOGIN_COLUMN.' timestamp, - '.$this->SALT_COLUMN.' varchar(255), - '.$this->DOMAIN_COLUMN.' varchar(255), - '.$this->EMAIL_COLUMN.' varchar(255), - '.$this->PASSWORD_COLUMN.' varchar(255) NOT NULL, - '.$this->ACTIVE_COLUMN.' BOOL, - PRIMARY KEY ('.$this->USER_NAME_COLUMN.') - )'); - + '.$this->USER_NAME_COLUMN.' varchar(255) NOT NULL, + '.$this->FIRST_NAME_COLUMN.' varchar(255), + '.$this->LAST_NAME_COLUMN.' varchar(255), + '.$this->LAST_LOGIN_COLUMN.' timestamp, + '.$this->SALT_COLUMN.' varchar(255), + '.$this->DOMAIN_COLUMN.' varchar(255), + '.$this->EMAIL_COLUMN.' varchar(255), + '.$this->PASSWORD_COLUMN.' varchar(255) NOT NULL, + '.$this->ACTIVE_COLUMN.' BOOL, + PRIMARY KEY ('.$this->USER_NAME_COLUMN.') + )'); for($i = 0; $i < count($this->users); $i++){ $usr = $this->users[$i]; $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() - **/ - public function testHasUsername(){ - + * Run the hasUsername test against an instance of DbUserBackend. + * @param $backend The backend that will be tested. + */ + private function runBackendUsername($backend){ // Known user - $this->assertTrue($this->dbUserBackend->hasUsername( + $this->assertTrue($backend->hasUsername( new Credentials( $this->users[0][$this->USER_NAME_COLUMN], $this->users[0][$this->PASSWORD_COLUMN]) )); // Unknown user - $this->assertFalse($this->dbUserBackend->hasUsername( + $this->assertFalse($backend->hasUsername( new Credentials( 'unkown user', 'secret') )); // Inactive user - $this->assertFalse($this->dbUserBackend->hasUsername( + $this->assertFalse($backend->hasUsername( new Credentials( $this->users[2][$this->USER_NAME_COLUMN], $this->users[2][$this->PASSWORD_COLUMN]) )); - } /** - * Test for DbUserBackend::Authenticate() - * - **/ - public function testAuthenticate(){ + * Run the authentication test against an instance of DbUserBackend. + * @param $backend The backend that will be tested. + */ + private function runBackendAuthentication($backend){ // Known user - $this->assertNotNull($this->dbUserBackend->authenticate( + $this->assertNotNull($backend->authenticate( new Credentials( $this->users[0][$this->USER_NAME_COLUMN], $this->users[0][$this->PASSWORD_COLUMN]) @@ -185,27 +265,28 @@ class DbUserBackendTest extends \PHPUnit_Framework_TestCase { // Wrong password $this->assertNull( - $this->dbUserBackend->authenticate( + $backend->authenticate( new Credentials( $this->users[1][$this->USER_NAME_COLUMN], 'wrongpassword') ) ); - // Nonexistend user + // Nonexisting user $this->assertNull( - $this->dbUserBackend->authenticate( + $backend->authenticate( new Credentials( - 'nonexistend user', + 'nonexisting user', $this->users[1][$this->PASSWORD_COLUMN]) ) ); // Inactive user - $this->assertNull($this->dbUserBackend->authenticate( - new Credentials( - $this->users[2][$this->USER_NAME_COLUMN], - $this->users[2][$this->PASSWORD_COLUMN]) + $this->assertNull( + $backend->authenticate( + new Credentials( + $this->users[2][$this->USER_NAME_COLUMN], + $this->users[2][$this->PASSWORD_COLUMN]) )); } } \ No newline at end of file