diff --git a/.horde.yml b/.horde.yml index 365a9bc..03231d1 100644 --- a/.horde.yml +++ b/.horde.yml @@ -9,23 +9,23 @@ list: dev type: library homepage: https://www.horde.org/libraries/Horde_Auth authors: + - + name: Chuck Hagenbuch + user: chuck + email: chuck@horde.org + active: true + role: lead - name: Jan Schneider user: jan email: jan@horde.org active: true role: lead - - - name: Chuck Hagenbuch - user: chuck - email: chuck@horde.org - active: false - role: lead - name: Michael Slusarz user: slusarz email: slusarz@horde.org - active: false + active: true role: lead version: release: 2.2.3 @@ -56,12 +56,8 @@ dependencies: pear.horde.org/Horde_Imsp: ^2 pear.horde.org/Horde_Http: ^2 pear.horde.org/Horde_Test: ^2.1 - pecl.php.net/pam: - version: '*' - providesextension: pam - pecl.php.net/sasl: - version: '*' - providesextension: sasl + pecl.php.net/pam: '*' + pecl.php.net/sasl: '*' ext: ctype: '*' ftp: '*' diff --git a/composer.json b/composer.json index 14e6047..8c49403 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "version": "2.2.3", - "time": "2017-11-11", + "time": "2018-02-15", "repositories": [ { "type": "pear", @@ -60,4 +60,4 @@ "Horde_Auth": "lib/" } } -} +} \ No newline at end of file diff --git a/doc/Horde/Auth/CHANGES b/doc/Horde/Auth/CHANGES index 95c0ee0..e6c7459 100644 --- a/doc/Horde/Auth/CHANGES +++ b/doc/Horde/Auth/CHANGES @@ -1,6 +1,9 @@ ----------- -v2.2.3-git ----------- +------ +v2.2.3 +------ + +[rla] Add Cascading Authentication driver. +[rla] Add Mock Authentication driver. ------ diff --git a/doc/Horde/Auth/changelog.yml b/doc/Horde/Auth/changelog.yml index 14a9b4b..fb9460b 100644 --- a/doc/Horde/Auth/changelog.yml +++ b/doc/Horde/Auth/changelog.yml @@ -8,7 +8,9 @@ license: identifier: LGPL-2.1 uri: http://www.horde.org/licenses/lgpl21 - notes: + notes: | + [rla] Add Cascading Authentication driver. + [rla] Add Mock Authentication driver. 2.2.2: api: 2.2.0 state: diff --git a/lib/Horde/Auth/Cascading.php b/lib/Horde/Auth/Cascading.php new file mode 100644 index 0000000..33ea4ff --- /dev/null +++ b/lib/Horde/Auth/Cascading.php @@ -0,0 +1,337 @@ + (implementation) + * @author Ralf Lang (concept) + * @category Horde + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Auth + */ + +/** + * The Horde_Auth_Cascading class provides a way to combine multiple + * authentication drivers for local overrides and integration scenarios + * + * @author Florian Frank (implementation) + * @author Ralf Lang (concept) + * @category Horde + * @copyright 2017-2018 Horde LLC + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Auth + */ +class Horde_Auth_Cascading extends Horde_Auth_Base +{ + /** + * Constructor. + * + * @param array $params Required parameters: + *
+     * 'drivers' - array hash of (Horde_Auth_Base) The list of backend drivers.
+     * 'order' - a list of drivers indexes to define a default order.
+     * 
+ * + * 'capabilities' - defines capabilities this driver + * exposes and how to map them to the backends. + * Defaults to "order" for all drivers which support it. + * + * @throws InvalidArgumentException + */ + public function __construct(array $params = array()) + { + $this->_test = $params; + foreach (array('drivers', 'order') as $val) { + if (!isset($params[$val])) { + throw new InvalidArgumentException('Missing ' . $val . ' parameter.'); + } + } + $capabilities = array(); + // Autodetect capabilities and build a default execution order + foreach ($this->_capabilities as $capabilityKey => $capability) { + foreach ($params['order'] as $driverKey) { + if ($params['drivers'][$driverKey]->hasCapability($capabilityKey)) { + if (empty($capabilities[$capabilityKey])) { + /* TODO: resetpassword capability is debatable - + We actually call update on the backend drivers to + get the same password in all backends + Thus, automatically assign resetpassword capability + to drivers which have 'update'. + The user may override this manually. + Drivers which actually only provide resetpassword are + supported, but more than one brings unpredictable + results + */ + $capabilities[$capabilityKey] = array(); + } + array_push($capabilities[$capabilityKey], $driverKey); + } + } + } + + if (!empty($params['capabilities'])) { + // override default capabilities with provided capabilities + $capabilities = array_merge($capabilities, $params['capabilities']); + + } + $params['capabilities'] = $capabilities; + // TODO: unset order, we don't use it after initialization + // Do base initialisation + unset($params['order']); + parent::__construct($params); + } + + protected $_test = array(); //test array + + public function getParams() //test function to see all arrays + { + return($this->_test); + } + + /** + * Find out if a set of login credentials are valid. + * Valid means valid in any backend + * + * @param string $userId The userId to check. + * @param array $credentials The credentials to use. + * + * @throws Horde_Auth_Exception + */ + protected function _authenticate($userId, $credentials) + { + // Return if any driver accepts this userId and credentials as valid + foreach ($this->_params['capabilities']['authenticate'] as $driverKey) { + if ($this->_params['drivers'][$driverKey]->authenticate($userId, $credentials)) { + return; + } + } + // Otherwise throw an exception + throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN); + } + + /** + * Advertise capability if a capability has backend driver + * + * @param string $capability The capability to test for. + * + * @return boolean Whether or not the capability is supported. + */ + public function hasCapability($capability) + { + if (empty($this->_params['capabilities'][$capability])) { + return false; + } + return true; + } + + /** + * Automatic authentication. + * + * @return boolean Whether or not the client is allowed. + */ + public function transparent() + { + // TODO: Check each configured driver in $this->_params['capabilities']['transparent']. Stop and return true if successful. Throw Exception if no driver available + + if (!$this->hasCapability('transparent')) + { + throw new Horde_Auth_Exception('Unsupported.'); + } + foreach ($this->_params['capabilities']['transparent'] as $driverKey) { + try{ + if ($this->_params['drivers']['driverKey']->transparent()) { + return true; + } + }catch (Horde_Auth_Exception $e){ + } + } + return false; + } + + /** + * Add a set of authentication credentials. + * + * @param string $userId The userId to add. + * @param array $credentials The credentials to use. + * + * @throws Horde_Auth_Exception + */ + public function addUser($userId, $credentials) + { + // TODO try to add the user to all backends in $this->_params['capabilities']['add'] - throw exception if no driver available + if (!$this->hasCapability('add')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + foreach ($this->_params['capabilities']['add'] as $driverKey) { + try{ + $this->_params['drivers'][$driverKey]->addUser($userId, $credentials); + }catch (Horde_Auth_Exception $e) { + } + } + } +/** + * Update a set of authentication credentials. + * + * @param string $oldID The old userId. + * @param string $newID The new userId. + * @param array $credentials The new credentials + * + * @throws Horde_Auth_Exception + */ + public function updateUser($oldID, $newID, $credentials) + { + // TODO try to add the user to all backends in $this->_params['capabilities']['update'] - throw exception if no driver available + if (!$this->hasCapability('update')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + foreach ($this->_params['capabilities']['update'] as $driverKey) { + try{ + $this->_params['drivers'][$driverKey]->updateUser($oldID, $newID, $credentials); + } catch (Horde_Auth_Exception $e) { + } + } + } + + /** + * Reset a user's password. Used for example when the user does not + * remember the existing password. + * + * @param string $userId The user id for which to reset the password. + * + * @return string The new password on success. + * @throws Horde_Auth_Exception + */ + public function resetPassword($userId) + { + // Implement this later: + // Check the list of auth drivers for drivers which has resetpassword but not update capability (configured). + // If exists, remove these drivers from list and run resetPassword on these driver. Use the first returned password for all drivers which have update (see below) + // Else, do as below + + if (!$this->hasCapability('resetpassword')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + $newPassword = ''; + $resetButUpdate = array(); + foreach ($this->_params['capabilities']['resetpassword'] as $resetKey) { + if (in_array($resetKey, $this->_params['capabilities']['update'])) { + array_push($resetButUpdate, $resetKey); + } + else { + try { + $newPassword = $this->_params['drivers'][$resetKey]->resetPassword($userId); + } catch (Horde_Auth_Exception $e) { + } + } + } + // Implement this first: + // Generate a random password ONCE + // Try to update the user password to all backends - throw exception if no driver available + // Return the new random password + + // inspired by https://stackoverflow.com/questions/4356289/php-random-string-generator + + if (!empty($resetButUpdate)) { + if (newPassword == '') { + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + for ($i = 0; $i < 8; $i++) { + $newPassword .= $characters[rand(0, $charactersLength - 1)]; + } + } + $credentials = array('password' => $newPassword); + foreach ($resetButUpdate as $driverKey) { + try{ + $this->_params['drivers'][$driverKey]->updateUser($userId, $userId, $credentials); + } catch (Horde_Auth_Exception $e) { + } + } + } + return $newPassword; + } + + /** + * Delete a set of authentication credentials. + * + * @param string $userId The userId to delete. + * + * @throws Horde_Auth_Exception + */ + public function removeUser($userId) + { + // TODO try to remove the user from all backends in $this->_params['capabilities']['remove'] - throw exception if no driver available + if (!$this->hasCapability('remove')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + $users = array(); + foreach ($this->_params['capabilities']['remove'] as $driverKey) { + try{ + $this->_params['drivers'][$driverKey]->removeUser($userId); + } catch (Horde_Auth_Exception $e) { + } + } + } + + /** + * Lists all users in the system. + * + * @param boolean $sort Sort the users? + * + * @return array The array of userIds. + * @throws Horde_Auth_Exception + */ + public function listUsers($sort = false) + { + //Todo list all users from all backends and merge them with array_unique + // Merge the results - don't list any user twice + + if (!$this->hasCapability('list')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + $users = array(); + foreach ($this->_params['capabilities']['list'] as $driverKey) { + try { + $users = array_merge($users , $this->_params['drivers'][$driverKey]->listUsers($sort)); + } catch (Horde_Auth_Exception $e) { + } + } + $uniqueUsers = array_values(array_unique($users)); + return($uniqueUsers); + } + + /** + * Checks if a userId exists in the system. + * + * @param string $userId User ID to check + * + * @return boolean Whether or not the userId already exists. + */ + public function exists($userId) + { + // rotate through all backends which have list capabddlity or exists capability - return true if any backend has this user, otherwise return false. + if (!$this->hasCapability('list') and !$this->hasCapability('exists')) { + throw new Horde_Auth_Exception('Unsupported.'); + } + if ($this->hasCapability('exists')) { + foreach ($this->_params['capabilities']['exists'] as $existsKey) { + try { + if ($this->_params['drivers'][$existsKey]->exists($userId)) { + return true; + } + } catch (Horde_Auth_Exception $e) { + } + } + } + foreach ($this->_params['capabilities']['list'] as $listKey) { + try { + if (in_array($userId, $this->_params['drivers'][$listKey]->listUsers())) { + return true; + } + } catch (Horde_Auth_Exception $e) { + } + } + return false; + } +} diff --git a/lib/Horde/Auth/Mock.php b/lib/Horde/Auth/Mock.php new file mode 100644 index 0000000..7ff5262 --- /dev/null +++ b/lib/Horde/Auth/Mock.php @@ -0,0 +1,169 @@ + + * @category Horde + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Auth + */ + +/** + * The Horde_Auth_Mock class provides an in-memory user list. + * + * It is meant to be used for throwaway setups, satellite systems or for + * providing a source of administrative accounts in Composite or Cascading driver + * The driver can also be used as a mock-like backend for integration tests + * + * @author Ralf Lang + * @category Horde + * @copyright 2017 Horde LLC + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @package Auth + */ +class Horde_Auth_Mock extends Horde_Auth_Base +{ + /** + * An array of capabilities, so that the driver can report which + * operations it supports and which it doesn't. + * + * @var array + */ + protected $_capabilities = array( + 'authenticate' => true, + 'list' => true, + 'update' => true, + 'remove' => true, + 'add' => true, + ); + + /** + * Constructor. + * + * @param array $params Optional parameters: + *
+     * 'users' - (array) Usernames are hash keys, passwords are values
+     * 'encryption' - (string) Optionally supply an encryption or hashing
+     * 'show_encryption' - (boolean) prepend encryption info to password string
+     * 
+ */ + public function __construct(array $params = array()) + { + $params = array_merge( + array( + 'encryption' => 'plain', + 'users' => array(), + 'show_encryption' => false + ), + $params + ); + parent::__construct($params); + } + + /** + * Adds a set of authentication credentials. + * + * @param string $userId The userId to add. + * @param array $credentials The credentials to use. + * + * @throws Horde_Auth_Exception + */ + public function addUser($userId, $credentials) + { + // TODO: use persistence callback if given + if ($this->exists($userId)) { + throw new Horde_Auth_Exception('User already exists'); + } + $this->_params['users'][$userId] = Horde_Auth::getCryptedPassword( + $credentials['password'], + '', + $this->_params['encryption'], + $this->_params['show_encryption']); + } + + /** + * Lists all users in the system. + * + * @param boolean $sort Sort the users? + * + * @return mixed The array of userIds. + */ + public function listUsers($sort = false) + { + return $this->_sort(array_keys($this->_params['users']), $sort); + } + + /** + * Updates a set of authentication credentials for the life time of the driver. + * + * @param string $oldID The old userId. + * @param string $newID The new userId. + * @param array $credentials The new credentials + * + * @throws Horde_Auth_Exception + */ + public function updateUser($oldID, $newID, $credentials) + { + if (!$this->exists($oldID)) { + throw new Horde_Auth_Exception('User does not exist'); + } + if ($this->exists($newID) && $newID != $oldID) { + throw new Horde_Auth_Exception('Cannot rename to existing user name'); + } + $this->removeUser($oldID); + $this->addUser($newID, $credentials); + } + + /** + * Deletes a set of authentication credentials for the life of the driver. + * + * @param string $userId The userId to delete. + */ + public function removeUser($userId) + { + // TODO: use persistence callback if given + unset($this->_params['users'][$userId]); + } + + /** + * Authenticate. + * + * @param string $userId The userID to check. + * @param array $credentials An array of login credentials. + * + * @throws Horde_Auth_Exception + */ + protected function _authenticate($userId, $credentials) + { + if (!$this->exists($userId) || + !$this->_comparePasswords( + $this->_params['users'][$userId], + $credentials['password'] + )) { + throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN); + } + return; + } + + /** + * Compare an encrypted password to a plaintext string to see if + * they match. + * + * @param string $encrypted The crypted password to compare against. + * @param string $plaintext The plaintext password to verify. + * + * @return boolean True if matched, false otherwise. + */ + protected function _comparePasswords($encrypted, $plaintext) + { + return $encrypted == Horde_Auth::getCryptedPassword( + $plaintext, + $encrypted, + $this->_params['encryption'], + $this->_params['show_encryption']); + } + +} diff --git a/package.xml b/package.xml index 026a7fd..80ab555 100644 --- a/package.xml +++ b/package.xml @@ -23,7 +23,7 @@ slusarz@horde.org yes - 2017-11-11 + 2018-02-15 2.2.3 2.2.0 @@ -34,7 +34,8 @@ LGPL-2.1 -* +* [rla] Add Cascading Authentication driver. +* [rla] Add Mock Authentication driver. @@ -53,6 +54,7 @@ + @@ -64,6 +66,7 @@ + @@ -179,7 +182,11 @@ + + + + @@ -294,12 +301,10 @@ pam pecl.php.net - pam sasl pecl.php.net - sasl ctype @@ -315,6 +320,7 @@ + @@ -327,6 +333,7 @@ + @@ -371,7 +378,11 @@ + + + + @@ -381,7 +392,6 @@ - 2008-09-16 0.1.0 0.1.0 @@ -390,6 +400,7 @@ beta beta + 2008-09-16 LGPL-2.1 * Fixed error handling when removing user data. @@ -420,7 +431,6 @@ - 2008-10-29 0.1.1 0.1.0 @@ -429,6 +439,7 @@ beta beta + 2008-10-29 LGPL-2.1 * Imap driver now uses Horde_Imap_Client library. @@ -543,10 +554,12 @@ 1.0.2 - 1.0.0 + 1.0.0 + stable - stable + stable + 2011-05-03 LGPL-2.1 @@ -556,10 +569,12 @@ 1.0.3 - 1.0.0 + 1.0.0 + stable - stable + stable + 2011-05-18 LGPL-2.1 @@ -569,10 +584,12 @@ 1.0.4 - 1.0.0 + 1.0.0 + stable - stable + stable + 2011-06-08 LGPL-2.1 @@ -582,10 +599,12 @@ 1.1.0 - 1.1.0 + 1.1.0 + stable - stable + stable + 2011-07-05 LGPL-2.1 @@ -596,10 +615,12 @@ 1.2.0 - 1.2.0 + 1.2.0 + stable - stable + stable + 2011-08-31 LGPL-2.1 @@ -611,10 +632,12 @@ 1.3.0 - 1.3.0 + 1.3.0 + stable - stable + stable + 2011-09-20 LGPL-2.1 @@ -626,10 +649,12 @@ 1.4.0 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-09-28 LGPL-2.1 @@ -639,10 +664,12 @@ 1.4.1 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-10-03 LGPL-2.1 @@ -652,10 +679,12 @@ 1.4.2 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-10-04 LGPL-2.1 @@ -665,10 +694,12 @@ 1.4.3 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-10-18 LGPL-2.1 @@ -678,10 +709,12 @@ 1.4.4 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-11-08 LGPL-2.1 @@ -691,10 +724,12 @@ 1.4.5 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-11-22 LGPL-2.1 @@ -704,10 +739,12 @@ 1.4.6 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-12-06 LGPL-2.1 @@ -717,10 +754,12 @@ 1.4.7 - 1.4.0 + 1.4.0 + stable - stable + stable + 2011-12-13 LGPL-2.1 @@ -730,10 +769,12 @@ 1.4.8 - 1.4.0 + 1.4.0 + stable - stable + stable + 2012-01-31 LGPL-2.1 @@ -744,10 +785,12 @@ 1.4.9 - 1.4.0 + 1.4.0 + stable - stable + stable + 2012-03-20 LGPL-2.1 @@ -757,10 +800,12 @@ 1.4.10 - 1.4.0 + 1.4.0 + stable - stable + stable + 2012-03-20 LGPL-2.1 @@ -768,8 +813,6 @@ - 2012-07-05 - 2.0.0alpha1 2.0.0alpha1 @@ -778,6 +821,7 @@ alpha alpha + 2012-07-05 LGPL-2.1 * First alpha release for Horde 5. @@ -787,10 +831,12 @@ 2.0.0beta1 - 2.0.0beta1 + 2.0.0beta1 + beta - beta + beta + 2012-07-19 LGPL-2.1 @@ -801,10 +847,12 @@ 2.0.0beta2 - 2.0.0beta1 + 2.0.0beta1 + beta - beta + beta + 2012-10-12 LGPL-2.1 @@ -816,10 +864,12 @@ 2.0.0 - 2.0.0 + 2.0.0 + stable - stable + stable + 2012-10-30 LGPL-2.1 @@ -829,10 +879,12 @@ 2.0.1 - 2.0.0 + 2.0.0 + stable - stable + stable + 2012-11-19 LGPL-2.1 @@ -842,10 +894,12 @@ 2.0.2 - 2.0.0 + 2.0.0 + stable - stable + stable + 2013-01-09 LGPL-2.1 @@ -856,10 +910,12 @@ 2.0.3 - 2.0.0 + 2.0.0 + stable - stable + stable + 2013-01-29 LGPL-2.1 @@ -871,10 +927,12 @@ 2.0.4 - 2.0.0 + 2.0.0 + stable - stable + stable + 2013-03-05 LGPL-2.1 @@ -884,10 +942,12 @@ 2.0.5 - 2.0.0 + 2.0.0 + stable - stable + stable + 2013-07-16 LGPL-2.1 @@ -899,10 +959,12 @@ 2.0.6 - 2.0.0 + 2.0.0 + stable - stable + stable + 2013-07-16 LGPL-2.1 @@ -912,10 +974,12 @@ 2.1.0 - 2.1.0 + 2.1.0 + stable - stable + stable + 2013-09-02 LGPL-2.1 @@ -925,10 +989,12 @@ 2.1.1 - 2.1.0 + 2.1.0 + stable - stable + stable + 2013-10-15 LGPL-2.1 @@ -938,10 +1004,12 @@ 2.1.2 - 2.1.0 + 2.1.0 + stable - stable + stable + 2014-03-03 LGPL-2.1 @@ -953,10 +1021,12 @@ 2.1.3 - 2.1.0 + 2.1.0 + stable - stable + stable + 2014-04-03 LGPL-2.1 @@ -966,10 +1036,12 @@ 2.1.4 - 2.1.0 + 2.1.0 + stable - stable + stable + 2014-05-21 LGPL-2.1 @@ -979,10 +1051,12 @@ 2.1.5 - 2.1.0 + 2.1.0 + stable - stable + stable + 2014-06-17 LGPL-2.1 @@ -992,10 +1066,12 @@ 2.1.6 - 2.1.0 + 2.1.0 + stable - stable + stable + 2015-01-08 LGPL-2.1 @@ -1006,10 +1082,12 @@ 2.1.7 - 2.1.0 + 2.1.0 + stable - stable + stable + 2015-04-13 LGPL-2.1 @@ -1019,10 +1097,12 @@ 2.1.8 - 2.1.0 + 2.1.0 + stable - stable + stable + 2015-04-28 LGPL-2.1 @@ -1032,10 +1112,12 @@ 2.1.9 - 2.1.0 + 2.1.0 + stable - stable + stable + 2015-06-29 LGPL-2.1 @@ -1045,10 +1127,12 @@ 2.1.10 - 2.1.0 + 2.1.0 + stable - stable + stable + 2015-07-06 LGPL-2.1 @@ -1058,10 +1142,12 @@ 2.1.11 - 2.1.0 + 2.1.0 + stable - stable + stable + 2016-02-01 LGPL-2.1 @@ -1071,10 +1157,12 @@ 2.1.12 - 2.1.0 + 2.1.0 + stable - stable + stable + 2016-04-05 LGPL-2.1 @@ -1084,10 +1172,12 @@ 2.2.0 - 2.2.0 + 2.2.0 + stable - stable + stable + 2016-07-28 LGPL-2.1 @@ -1097,10 +1187,12 @@ 2.2.1 - 2.2.0 + 2.2.0 + stable - stable + stable + 2016-12-03 LGPL-2.1 @@ -1110,10 +1202,12 @@ 2.2.2 - 2.2.0 + 2.2.0 + stable - stable + stable + 2017-06-22 LGPL-2.1 @@ -1123,14 +1217,17 @@ 2.2.3 - 2.2.0 + 2.2.0 + stable - stable - 2017-11-11 + stable + + 2018-02-15 LGPL-2.1 -* +* [rla] Add Cascading Authentication driver. +* [rla] Add Mock Authentication driver. diff --git a/test/Horde/Auth/Unit/CascadingMockTest.php b/test/Horde/Auth/Unit/CascadingMockTest.php new file mode 100644 index 0000000..b2df34d --- /dev/null +++ b/test/Horde/Auth/Unit/CascadingMockTest.php @@ -0,0 +1,169 @@ + + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @link http://pear.horde.org/index.php?package=Auth + */ + +class Horde_Auth_Unit_CascadingTest extends Horde_Auth_TestCase +{ + + function setUp() { + $d1 = new Horde_Auth_Mock(array('users' => array('user1' => 'pw1','user2' => 'pw2'))); + $d2 = new Horde_Auth_Mock(array('users' => array('user1' => 'pw3443243', 'tester2' => 'pw3323242'))); + $d3 = new Horde_Auth_Mock( + array( + 'users' => array( + 'tester1337' => '$1$W1n1f.uf$4pL90iQvvfS0PqshMaMLi.', //testpassword1 + 'tester1338' => '$1$W1n1f.uf$M3F2kPykfT3Z1ywuAqvO6.', //testpassword2 + 'tester2' => '$1$W1n1f.uf$ng08WnPr8wQuVGg2pvw2f1' //hi$M+i3Rd + ), + 'encryption' => 'crypt-md5', + 'show_encryption' => false + ) + ); + $this->cascading = new Horde_Auth_Cascading( + array( + 'drivers' => array('admins' => $d1, 'db' => $d2, 'cryptdb' => $d3), + 'order' => array('admins', 'db', 'cryptdb'), + ) + ); + } + + public function testHasCapability() + { + $this->assertFalse($this->cascading->hasCapability('transparent')); + $this->assertTrue($this->cascading->hasCapability('authenticate')); + $this->assertTrue($this->cascading->hasCapability('remove')); + $this->assertTrue($this->cascading->hasCapability('update')); + } + + public function testTransparent() + { + //throw exception if no backend provides transparent + $this->setExpectedException(Horde_Auth_Exception::class); + $this->cascading->transparent(); + } + + public function testListUsers() + { + $this->assertEquals(array('user1', 'user2', 'tester2', 'tester1337', 'tester1338'), $this->cascading->listUsers(true)); + $this->assertEquals(array('user1', 'user2', 'tester2', 'tester1337', 'tester1338'), $this->cascading->listUsers(false)); + $this->assertEquals(array('user1', 'user2', 'tester2', 'tester1337', 'tester1338'), $this->cascading->listUsers()); + $this->assertCount(5, $this->cascading->listUsers()); + $this->assertCount(5, $this->cascading->listUsers(true)); + $this->assertCount(5, $this->cascading->listUsers(false)); + } + + public function testRemoveUser() + { + $this->cascading->removeUser('user1'); + $this->assertCount(4, $this->cascading->listUsers()); + $this->assertFalse($this->cascading->exists('user1')); + $this->assertFalse($this->cascading->authenticate('user1', array('password' => 'pw1'))); + $this->assertFalse($this->cascading->authenticate('user1', array('password' => 'pw3443243'))); + } + + public function testUpdateUser() + { + //change password + $this->cascading->updateUser('user1', 'user1', array('password' => 'foo')); + $this->assertCount(5, $this->cascading->listUsers()); + $this->assertTrue($this->cascading->exists('user1')); + // check if new password works + $this->assertFalse($this->cascading->authenticate('user1', array('password' => 'pw1'))); + $this->assertTrue($this->cascading->authenticate('user1', array('password' => 'foo'))); + //change userID + $this->cascading->updateUser('user2', 'user42', array('password' => 'pw2')); + $this->assertCount(5, $this->cascading->listUsers()); + $this->assertTrue($this->cascading->exists('user42')); + // check if new user works + $this->assertTrue($this->cascading->authenticate('user42', array('password' => 'pw2'))); + //check if old users still can log in + $this->assertFalse($this->cascading->authenticate('user2', array('password' => 'pw2'))); + //change userID and password + $this->cascading->updateUser('tester2', 'tester1339', array('password' => 'pw1337')); + $this->assertCount(5, $this->cascading->listUsers()); + $this->assertTrue($this->cascading->exists('tester1339')); + // check if new user can log in + $this->assertTrue($this->cascading->authenticate('tester1339', array('password' => 'pw1337'))); + //check if old users still exists + $this->assertFalse($this->cascading->authenticate('tester2', array('password' => 'pw3323242'))); + //update user with encrypted password + $this->cascading->updateUser('tester1337', 'tester1337', array('password' => 'testpassword1337')); + $this->assertTrue($this->cascading->authenticate('tester1337', array('password' => 'testpassword1337'))); + } + + public function testUpdateUserFailDoesNotExist() + { + // Try renaming unknown user + // $this->setExpectedException(Horde_Auth_Exception::class); + $this->cascading->updateUser('unknownuser', 'newname', array('password' => 'foo')); + $this->setUp(); + } + + public function testResetPassword() + { + $this->setExpectedException(Horde_Auth_Exception::class); + $newPassword = $this->cascading->resetPassword('user1'); + //old Password should not work + $this->assertFalse($this->cascading->authenticate('user1' , array('password' => 'pw1'))); + //new password should work + $this->assertTrue($this->cascading->authenticate('user1' , array('password' => $newPassword))); + } + + public function testAddUser() + { + //new user who does not exist in any backend + $this->cascading->addUser('user42', array('password' => 'foo')); + $this->assertCount(6, $this->cascading->listUsers()); + //new user who already exists in one backend with the same password + $this->cascading->addUser('user2', array('password' => 'pw2')); + $this->assertCount(6, $this->cascading->listUsers()); + //new user who already exists in one backend with a wrong password + $this->cascading->addUser('user2', array('password' => 'pw42')); + $this->assertCount(6, $this->cascading->listUsers()); + $this->assertFalse($this->cascading->authenticate('user2' , array('password' => 'pw42'))); + $this->assertTrue($this->cascading->authenticate('user2' , array('password' => 'pw2'))); + //new user who already exists in all backends + $this->cascading->addUser('user1', array('password' => 'pw1')); + $this->assertEquals(array('user1', 'user2', 'user42', 'tester2', 'tester1337', 'tester1338'), $this->cascading->listUsers(true)); + $this->assertTrue($this->cascading->authenticate('user1' , array('password' => 'pw3443243'))); + //check authenticate + $this->assertTrue($this->cascading->authenticate('user42', array('password' => 'foo'))); + $this->assertFalse($this->cascading->authenticate('user42', array('password' => 'bar'))); + } + + public function testAuthenticate() + { + // user does not exist in any backend + $this->assertFalse($this->cascading->authenticate('user42', array('password' => 'pw1'))); + // user exists in two backends with different passwords + $this->assertTrue($this->cascading->authenticate('user1', array('password' => 'pw3443243'))); + $this->assertTrue($this->cascading->authenticate('user1', array('password' => 'pw1'))); + // user exists in one backend + $this->assertTrue($this->cascading->authenticate('user2', array('password' => 'pw2'))); + //user has encrypeted password + $this->assertTrue($this->cascading->authenticate('tester1337', array('password' => 'testpassword1'))); + $this->assertTrue($this->cascading->authenticate('tester2', array('password' => 'hi$M+i3Rd'))); + // user has wrong password + $this->assertFalse($this->cascading->authenticate('user1', array('password' => 'pw42'))); + } + + public function testExists() + { + $this->assertTrue($this->cascading->exists('user1')); + $this->assertFalse($this->cascading->exists('user42')); + } +} diff --git a/test/Horde/Auth/Unit/MockCryptShowTest.php b/test/Horde/Auth/Unit/MockCryptShowTest.php new file mode 100644 index 0000000..7b2a164 --- /dev/null +++ b/test/Horde/Auth/Unit/MockCryptShowTest.php @@ -0,0 +1,142 @@ + + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @link http://pear.horde.org/index.php?package=Auth + */ +class Horde_Auth_Unit_MockCryptShowTest extends Horde_Auth_TestCase +{ + public function setUp() + { + $this->driver = new Horde_Auth_Mock( + array( + 'users' => array( + 'user1' => '{crypt}$1$S/EKq8Dg$OJvaV8Lu1HgCXKNqAo.wG/', + 'user2' => '{crypt}$1$aCWZiBAW$dK0DCmTGYR1gEX11pMxbi0', + 'tester' => '{crypt}$1$sjSx+Q9x$3WIEdh1Ei16QouYx1Xkct1' + ), + 'encryption' => 'crypt-md5', + 'show_encryption' => true + ) + ); + } + + public function testAuthenticate() + { + $this->assertTrue($this->driver->authenticate('user1', array('password' => 'user1pw'))); + $this->assertTrue($this->driver->authenticate('user2', array('password' => 'user2pw'))); + $this->assertTrue($this->driver->authenticate('tester', array('password' => 'WeirdPW92401#1'))); + // correct password extended by garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'user1pwfalse'))); + // Existing user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => null))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => '0'))); + // Unknown user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => null))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => '0'))); + } + + public function testRemoveUser() + { + // Delete somebody who doesn't exist + $this->driver->removeUser('user'); + $this->assertCount(3, $this->driver->listUsers()); + $this->driver->removeUser('user1'); + $this->assertCount(2, $this->driver->listUsers()); + $this->driver->removeUser('user2'); + $this->assertCount(1, $this->driver->listUsers()); + $this->driver->removeUser('tester'); + $this->assertCount(0, $this->driver->listUsers()); + // Restore setup + $this->setUp(); + } + + public function testAddUser() + { + // Add somebody who already exist + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + // Add somebody who already exist + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + + // Restore setup + $this->setUp(); + } + + public function testUpdateUser() + { + // Try a password change + $this->driver->updateUser('tester', 'tester', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('tester')); + // Try renaming + $this->driver->updateUser('tester', 'newname', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('newname')); + $this->assertFalse($this->driver->exists('tester')); + $this->setUp(); + } + + public function testUpdateUserFailDoesNotExist() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('unknownuser', 'newname', array('password' => 'foo')); + $this->setUp(); + } + + public function testUpdateUserFailNewNameExists() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('tester', 'user1', array('password' => 'foo')); + $this->setUp(); + } + + + + public function testExists() + { + $this->assertTrue($this->driver->exists('user1')); + $this->assertTrue($this->driver->exists('user2')); + $this->assertTrue($this->driver->exists('tester')); + $this->assertFalse($this->driver->exists('somebody')); + $this->assertFalse($this->driver->exists('')); + $this->assertFalse($this->driver->exists(null)); + } + + /** + * This is actually a test against Horde_Auth_Base + * TODO: Copy or move to a test with a phpunit mock + */ + + public function testSearchUsers() + { + $this->assertCount(2, $this->driver->searchUsers('user')); + $this->assertCount(1, $this->driver->searchUsers('test')); + $this->assertEquals(array('tester'), $this->driver->searchUsers('test')); + } + + public function testListUsers() + { + $this->assertEquals(array('tester', 'user1', 'user2'), $this->driver->listUsers(true)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers(false)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers()); + } +} diff --git a/test/Horde/Auth/Unit/MockCryptTest.php b/test/Horde/Auth/Unit/MockCryptTest.php new file mode 100644 index 0000000..a0245d6 --- /dev/null +++ b/test/Horde/Auth/Unit/MockCryptTest.php @@ -0,0 +1,142 @@ + + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @link http://pear.horde.org/index.php?package=Auth + */ +class Horde_Auth_Unit_MockCryptTest extends Horde_Auth_TestCase +{ + public function setUp() + { + $this->driver = new Horde_Auth_Mock( + array( + 'users' => array( + 'user1' => '$1$S/EKq8Dg$OJvaV8Lu1HgCXKNqAo.wG/', + 'user2' => '$1$aCWZiBAW$dK0DCmTGYR1gEX11pMxbi0', + 'tester' => '$1$sjSx+Q9x$3WIEdh1Ei16QouYx1Xkct1' + ), + 'encryption' => 'crypt-md5', + 'show_encryption' => false + ) + ); + } + + public function testAuthenticate() + { + $this->assertTrue($this->driver->authenticate('user1', array('password' => 'user1pw'))); + $this->assertTrue($this->driver->authenticate('user2', array('password' => 'user2pw'))); + $this->assertTrue($this->driver->authenticate('tester', array('password' => 'WeirdPW92401#1'))); + // correct password extended by garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'user1pwfalse'))); + // Existing user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => null))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => '0'))); + // Unknown user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => null))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => '0'))); + } + + public function testRemoveUser() + { + // Delete somebody who doesn't exist + $this->driver->removeUser('user'); + $this->assertCount(3, $this->driver->listUsers()); + $this->driver->removeUser('user1'); + $this->assertCount(2, $this->driver->listUsers()); + $this->driver->removeUser('user2'); + $this->assertCount(1, $this->driver->listUsers()); + $this->driver->removeUser('tester'); + $this->assertCount(0, $this->driver->listUsers()); + // Restore setup + $this->setUp(); + } + + public function testAddUser() + { + // Add somebody who already exist + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + // Add somebody who already exist + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + + // Restore setup + $this->setUp(); + } + + public function testUpdateUser() + { + // Try a password change + $this->driver->updateUser('tester', 'tester', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('tester')); + // Try renaming + $this->driver->updateUser('tester', 'newname', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('newname')); + $this->assertFalse($this->driver->exists('tester')); + $this->setUp(); + } + + public function testUpdateUserFailDoesNotExist() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('unknownuser', 'newname', array('password' => 'foo')); + $this->setUp(); + } + + public function testUpdateUserFailNewNameExists() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('tester', 'user1', array('password' => 'foo')); + $this->setUp(); + } + + + + public function testExists() + { + $this->assertTrue($this->driver->exists('user1')); + $this->assertTrue($this->driver->exists('user2')); + $this->assertTrue($this->driver->exists('tester')); + $this->assertFalse($this->driver->exists('somebody')); + $this->assertFalse($this->driver->exists('')); + $this->assertFalse($this->driver->exists(null)); + } + + /** + * This is actually a test against Horde_Auth_Base + * TODO: Copy or move to a test with a phpunit mock + */ + + public function testSearchUsers() + { + $this->assertCount(2, $this->driver->searchUsers('user')); + $this->assertCount(1, $this->driver->searchUsers('test')); + $this->assertEquals(array('tester'), $this->driver->searchUsers('test')); + } + + public function testListUsers() + { + $this->assertEquals(array('tester', 'user1', 'user2'), $this->driver->listUsers(true)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers(false)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers()); + } +} diff --git a/test/Horde/Auth/Unit/MockPlainTest.php b/test/Horde/Auth/Unit/MockPlainTest.php new file mode 100644 index 0000000..51bea2b --- /dev/null +++ b/test/Horde/Auth/Unit/MockPlainTest.php @@ -0,0 +1,140 @@ + + * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 + * @link http://pear.horde.org/index.php?package=Auth + */ +class Horde_Auth_Unit_MockPlainTest extends Horde_Auth_TestCase +{ + public function setUp() + { + $this->driver = new Horde_Auth_Mock( + array( + 'users' => array( + 'user1' => 'user1pw', + 'user2' => 'user2pw', + 'tester' => 'WeirdPW92401#1' + ) + ) + ); + } + + public function testAuthenticate() + { + $this->assertTrue($this->driver->authenticate('user1', array('password' => 'user1pw'))); + $this->assertTrue($this->driver->authenticate('user2', array('password' => 'user2pw'))); + $this->assertTrue($this->driver->authenticate('tester', array('password' => 'WeirdPW92401#1'))); + // correct password extended by garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'user1pwfalse'))); + // Existing user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('user1', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => null))); + $this->assertFalse($this->driver->authenticate('user1', array('password' => '0'))); + // Unknown user with all kinds of garbage + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => 'any'))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => ''))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => null))); + $this->assertFalse($this->driver->authenticate('unknownuser', array('password' => '0'))); + } + + public function testRemoveUser() + { + // Delete somebody who doesn't exist + $this->driver->removeUser('user'); + $this->assertCount(3, $this->driver->listUsers()); + $this->driver->removeUser('user1'); + $this->assertCount(2, $this->driver->listUsers()); + $this->driver->removeUser('user2'); + $this->assertCount(1, $this->driver->listUsers()); + $this->driver->removeUser('tester'); + $this->assertCount(0, $this->driver->listUsers()); + // Restore setup + $this->setUp(); + } + + public function testAddUser() + { + // Add somebody who already exist + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + // Add somebody who already exist + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->addUser('user4', array('password' => 'foo')); + $this->assertCount(4, $this->driver->listUsers()); + + // Restore setup + $this->setUp(); + } + + public function testUpdateUser() + { + // Try a password change + $this->driver->updateUser('tester', 'tester', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('tester')); + // Try renaming + $this->driver->updateUser('tester', 'newname', array('password' => 'foo')); + $this->assertCount(3, $this->driver->listUsers()); + $this->assertTrue($this->driver->exists('newname')); + $this->assertFalse($this->driver->exists('tester')); + $this->setUp(); + } + + public function testUpdateUserFailDoesNotExist() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('unknownuser', 'newname', array('password' => 'foo')); + $this->setUp(); + } + + public function testUpdateUserFailNewNameExists() + { + // Try renaming unknown user + $this->setExpectedException('Horde_Auth_Exception'); + $this->driver->updateUser('tester', 'user1', array('password' => 'foo')); + $this->setUp(); + } + + + + public function testExists() + { + $this->assertTrue($this->driver->exists('user1')); + $this->assertTrue($this->driver->exists('user2')); + $this->assertTrue($this->driver->exists('tester')); + $this->assertFalse($this->driver->exists('somebody')); + $this->assertFalse($this->driver->exists('')); + $this->assertFalse($this->driver->exists(null)); + } + + /** + * This is actually a test against Horde_Auth_Base + * TODO: Copy or move to a test with a phpunit mock + */ + + public function testSearchUsers() + { + $this->assertCount(2, $this->driver->searchUsers('user')); + $this->assertCount(1, $this->driver->searchUsers('test')); + $this->assertEquals(array('tester'), $this->driver->searchUsers('test')); + } + + public function testListUsers() + { + $this->assertEquals(array('tester', 'user1', 'user2'), $this->driver->listUsers(true)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers(false)); + $this->assertEquals(array('user1', 'user2', 'tester'), $this->driver->listUsers()); + } +}