From 955bda1842986a6737c8d3b575b0cd3bb5a0e17d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 28 May 2013 23:46:57 +0200 Subject: [PATCH] New user management classes --- lib/user/backend.php | 22 +-- lib/user/dummy.php | 144 +++++++------- lib/user/manager.php | 189 +++++++++++++++++++ lib/user/session.php | 161 ++++++++++++++++ lib/user/user.php | 177 ++++++++++++++++++ tests/lib/user/backend.php | 99 ++++++++++ tests/lib/user/database.php | 44 +++++ tests/lib/user/dummy.php | 27 +++ tests/lib/user/manager.php | 304 ++++++++++++++++++++++++++++++ tests/lib/user/session.php | 155 +++++++++++++++ tests/lib/user/user.php | 364 ++++++++++++++++++++++++++++++++++++ 11 files changed, 1605 insertions(+), 81 deletions(-) create mode 100644 lib/user/manager.php create mode 100644 lib/user/session.php create mode 100644 lib/user/user.php create mode 100644 tests/lib/user/backend.php create mode 100644 tests/lib/user/database.php create mode 100644 tests/lib/user/dummy.php create mode 100644 tests/lib/user/manager.php create mode 100644 tests/lib/user/session.php create mode 100644 tests/lib/user/user.php diff --git a/lib/user/backend.php b/lib/user/backend.php index 93e8f17ca9..e9be08e429 100644 --- a/lib/user/backend.php +++ b/lib/user/backend.php @@ -58,7 +58,7 @@ abstract class OC_User_Backend implements OC_User_Interface { /** * @brief Get all supported actions - * @returns bitwise-or'ed actions + * @return int bitwise-or'ed actions * * Returns the supported actions as int to be * compared with OC_USER_BACKEND_CREATE_USER etc. @@ -76,8 +76,8 @@ abstract class OC_User_Backend implements OC_User_Interface { /** * @brief Check if backend implements actions - * @param $actions bitwise-or'ed actions - * @returns boolean + * @param int $actions bitwise-or'ed actions + * @return boolean * * Returns the supported actions as int to be * compared with OC_USER_BACKEND_CREATE_USER etc. @@ -87,12 +87,12 @@ abstract class OC_User_Backend implements OC_User_Interface { } /** - * @brief delete a user - * @param $uid The username of the user to delete - * @returns true/false - * - * Deletes a user - */ + * @brief delete a user + * @param string $uid The username of the user to delete + * @return bool + * + * Deletes a user + */ public function deleteUser( $uid ) { return false; } @@ -127,8 +127,8 @@ abstract class OC_User_Backend implements OC_User_Interface { /** * @brief get display name of the user - * @param $uid user ID of the user - * @return display name + * @param string $uid user ID of the user + * @return string display name */ public function getDisplayName($uid) { return $uid; diff --git a/lib/user/dummy.php b/lib/user/dummy.php index d63f60efbe..b5b7a6c3c7 100644 --- a/lib/user/dummy.php +++ b/lib/user/dummy.php @@ -1,114 +1,118 @@ . -* -*/ + * ownCloud + * + * @author Frank Karlitschek + * @copyright 2012 Frank Karlitschek frank@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ /** * dummy user backend, does not keep state, only for testing use */ class OC_User_Dummy extends OC_User_Backend { - private $users=array(); + private $users = array(); + /** - * @brief Create a new user - * @param $uid The username of the user to create - * @param $password The password of the new user - * @returns true/false - * - * Creates a new user. Basic checking of username is done in OC_User - * itself, not in its subclasses. - */ + * @brief Create a new user + * @param string $uid The username of the user to create + * @param string $password The password of the new user + * @return bool + * + * Creates a new user. Basic checking of username is done in OC_User + * itself, not in its subclasses. + */ public function createUser($uid, $password) { - if(isset($this->users[$uid])) { + if (isset($this->users[$uid])) { return false; - }else{ - $this->users[$uid]=$password; + } else { + $this->users[$uid] = $password; return true; } } /** - * @brief delete a user - * @param $uid The username of the user to delete - * @returns true/false - * - * Deletes a user - */ - public function deleteUser( $uid ) { - if(isset($this->users[$uid])) { + * @brief delete a user + * @param string $uid The username of the user to delete + * @return bool + * + * Deletes a user + */ + public function deleteUser($uid) { + if (isset($this->users[$uid])) { unset($this->users[$uid]); return true; - }else{ + } else { return false; } } /** - * @brief Set password - * @param $uid The username - * @param $password The new password - * @returns true/false - * - * Change the password of a user - */ + * @brief Set password + * @param string $uid The username + * @param string $password The new password + * @return bool + * + * Change the password of a user + */ public function setPassword($uid, $password) { - if(isset($this->users[$uid])) { - $this->users[$uid]=$password; + if (isset($this->users[$uid])) { + $this->users[$uid] = $password; return true; - }else{ + } else { return false; } } /** - * @brief Check if the password is correct - * @param $uid The username - * @param $password The password - * @returns string - * - * Check if the password is correct without logging in the user - * returns the user id or false - */ + * @brief Check if the password is correct + * @param string $uid The username + * @param string $password The password + * @return string + * + * Check if the password is correct without logging in the user + * returns the user id or false + */ public function checkPassword($uid, $password) { - if(isset($this->users[$uid])) { - return ($this->users[$uid]==$password); - }else{ + if (isset($this->users[$uid])) { + return ($this->users[$uid] == $password); + } else { return false; } } /** - * @brief Get a list of all users - * @returns array with all uids - * - * Get a list of all users. - */ + * @brief Get a list of all users + * @param string $search + * @param int $limit + * @param int $offset + * @return array with all uids + * + * Get a list of all users. + */ public function getUsers($search = '', $limit = null, $offset = null) { return array_keys($this->users); } /** - * @brief check if a user exists - * @param string $uid the username - * @return boolean - */ + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ public function userExists($uid) { return isset($this->users[$uid]); } diff --git a/lib/user/manager.php b/lib/user/manager.php new file mode 100644 index 0000000000..25e67ca36b --- /dev/null +++ b/lib/user/manager.php @@ -0,0 +1,189 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\User; + +use OC\Hooks\PublicEmitter; + +/** + * Class Manager + * + * Hooks available in scope \OC\User: + * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) + * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) + * - preDelete(\OC\User\User $user) + * - postDelete(\OC\User\User $user) + * - preCreateUser(string $uid, string $password) + * - postCreateUser(\OC\User\User $user) + * + * @package OC\User + */ +class Manager extends PublicEmitter { + /** + * @var \OC_User_Backend[] $backends + */ + private $backends = array(); + + /** + * @param \OC_User_Backend $backend + */ + public function registerBackend($backend) { + $this->backends[] = $backend; + } + + /** + * @param \OC_User_Backend $backend + */ + public function removeBackend($backend) { + if (($i = array_search($backend, $this->backends)) !== false) { + unset($this->backends[$i]); + } + } + + public function clearBackends() { + $this->backends = array(); + } + + /** + * @param string $uid + * @return \OC\User\User + */ + public function get($uid) { + foreach ($this->backends as $backend) { + if ($backend->userExists($uid)) { + return new User($uid, $backend); + } + } + return null; + } + + /** + * @param string $uid + * @return bool + */ + public function userExists($uid) { + foreach ($this->backends as $backend) { + if ($backend->userExists($uid)) { + return true; + } + } + return false; + } + + /** + * search by user id + * + * @param string $pattern + * @param int $limit + * @param int $offset + * @return \OC\User\User[] + */ + public function search($pattern, $limit = null, $offset = null) { + $users = array(); + foreach ($this->backends as $backend) { + $backendUsers = $backend->getUsers($pattern, $limit, $offset); + if (is_array($backendUsers)) { + foreach ($backendUsers as $uid) { + $users[] = new User($uid, $backend); + if (!is_null($limit)) { + $limit--; + } + if (!is_null($offset) and $offset > 0) { + $offset--; + } + + } + } + } + + usort($users, function ($a, $b) { + /** + * @var \OC\User\User $a + * @var \OC\User\User $b + */ + return strcmp($a->getUID(), $b->getUID()); + }); + return $users; + } + + /** + * search by displayName + * + * @param string $pattern + * @param int $limit + * @param int $offset + * @return \OC\User\User[] + */ + public function searchDisplayName($pattern, $limit = null, $offset = null) { + $users = array(); + foreach ($this->backends as $backend) { + $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); + if (is_array($backendUsers)) { + foreach ($backendUsers as $uid => $displayName) { + $users[] = new User($uid, $backend); + if (!is_null($limit)) { + $limit--; + } + if (!is_null($offset) and $offset > 0) { + $offset--; + } + + } + } + } + + usort($users, function ($a, $b) { + /** + * @var \OC\User\User $a + * @var \OC\User\User $b + */ + return strcmp($a->getDisplayName(), $b->getDisplayName()); + }); + return $users; + } + + /** + * @param string $uid + * @param string $password + * @throws \Exception + * @return bool | \OC\User\User the created user of false + */ + public function createUser($uid, $password) { + // Check the name for bad characters + // Allowed are: "a-z", "A-Z", "0-9" and "_.@-" + if (preg_match('/[^a-zA-Z0-9 _\.@\-]/', $uid)) { + throw new \Exception('Only the following characters are allowed in a username:' + . ' "a-z", "A-Z", "0-9", and "_.@-"'); + } + // No empty username + if (trim($uid) == '') { + throw new \Exception('A valid username must be provided'); + } + // No empty password + if (trim($password) == '') { + throw new \Exception('A valid password must be provided'); + } + + // Check if user already exists + if ($this->userExists($uid)) { + throw new \Exception('The username is already being used'); + } + + $this->emit('\OC\User', 'preCreateUser', array($uid, $password)); + foreach ($this->backends as $backend) { + if ($backend->implementsActions(\OC_USER_BACKEND_CREATE_USER)) { + $backend->createUser($uid, $password); + $user = new User($uid, $backend); + $this->emit('\OC\User', 'postCreateUser', array($user)); + return $user; + } + } + return false; + } +} diff --git a/lib/user/session.php b/lib/user/session.php new file mode 100644 index 0000000000..b0fdcd3e85 --- /dev/null +++ b/lib/user/session.php @@ -0,0 +1,161 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\User; + +use OC\Hooks\Emitter; + +/** + * Class Session + * + * Hooks available in scope \OC\User: + * - preSetPassword(\OC\User\User $user, string $password, string $recoverPassword) + * - postSetPassword(\OC\User\User $user, string $password, string $recoverPassword) + * - preDelete(\OC\User\User $user) + * - postDelete(\OC\User\User $user) + * - preCreateUser(string $uid, string $password) + * - postCreateUser(\OC\User\User $user) + * - preLogin(string $user, string $password) + * - postLogin(\OC\User\User $user) + * - logout() + * + * @package OC\User + */ +class Session implements Emitter { + /** + * @var \OC\User\Manager $manager + */ + private $manager; + + /** + * @var \OC\Session\Session $session + */ + private $session; + + /** + * @var \OC\User\User $activeUser + */ + protected $activeUser; + + /** + * @param \OC\User\Manager $manager + * @param \OC\Session\Session $session + */ + public function __construct($manager, $session) { + $this->manager = $manager; + $this->session = $session; + } + + /** + * @param string $scope + * @param string $method + * @param callable $callback + */ + public function listen($scope, $method, $callback) { + $this->manager->listen($scope, $method, $callback); + } + + /** + * @param string $scope optional + * @param string $method optional + * @param callable $callback optional + */ + public function removeListener($scope = null, $method = null, $callback = null) { + $this->manager->removeListener($scope, $method, $callback); + } + + /** + * @return \OC\User\Manager + */ + public function getManager() { + return $this->manager; + } + + /** + * set the currently active user + * + * @param \OC\User\User $user + */ + public function setUser($user) { + if (is_null($user)) { + $this->session->remove('user_id'); + } else { + $this->session->set('user_id', $user->getUID()); + } + $this->activeUser = $user; + } + + /** + * get the current active user + * + * @return \OC\User\User + */ + public function getUser() { + if ($this->activeUser) { + return $this->activeUser; + } else { + $uid = $this->session->get('user_id'); + if ($uid) { + $this->activeUser = $this->manager->get($uid); + return $this->activeUser; + } else { + return null; + } + } + } + + public function login($uid, $password) { + $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); + $user = $this->manager->get($uid); + if ($user) { + $result = $user->checkPassword($password); + if ($result and $user->isEnabled()) { + $this->manager->emit('\OC\User', 'postLogin', array($user)); + $this->setUser($user); + return true; + } else { + return false; + } + } else { + return false; + } + } + + public function logout() { + $this->manager->emit('\OC\User', 'logout'); + $this->setUser(null); + $this->unsetMagicInCookie(); + } + + /** + * Set cookie value to use in next page load + * + * @param string $username username to be set + * @param string $token + */ + public function setMagicInCookie($username, $token) { + $secure_cookie = \OC_Config::getValue("forcessl", false); //TODO: DI for cookies and OC_Config + $expires = time() + \OC_Config::getValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); + setcookie("oc_username", $username, $expires, OC::$WEBROOT, '', $secure_cookie); + setcookie("oc_token", $token, $expires, OC::$WEBROOT, '', $secure_cookie, true); + setcookie("oc_remember_login", true, $expires, OC::$WEBROOT, '', $secure_cookie); + } + + /** + * @brief Remove cookie for "remember username" + */ + public function unsetMagicInCookie() { + unset($_COOKIE["oc_username"]); //TODO: DI + unset($_COOKIE["oc_token"]); + unset($_COOKIE["oc_remember_login"]); + setcookie("oc_username", null, -1); + setcookie("oc_token", null, -1); + setcookie("oc_remember_login", null, -1); + } +} diff --git a/lib/user/user.php b/lib/user/user.php new file mode 100644 index 0000000000..095c37939a --- /dev/null +++ b/lib/user/user.php @@ -0,0 +1,177 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\User; + +use OC\Hooks\Emitter; + +class User { + /** + * @var string $uid + */ + private $uid; + + /** + * @var string $displayName + */ + private $displayName; + + /** + * @var \OC_User_Backend $backend + */ + private $backend; + + /** + * @var bool $enabled + */ + private $enabled; + + /** + * @var Emitter | Manager $emitter + */ + private $emitter; + + /** + * @param string $uid + * @param \OC_User_Backend $backend + * @param Emitter $emitter + */ + public function __construct($uid, $backend, $emitter = null) { + $this->uid = $uid; + if ($backend->implementsActions(OC_USER_BACKEND_GET_DISPLAYNAME)) { + $this->displayName = $backend->getDisplayName($uid); + } else { + $this->displayName = $uid; + } + $this->backend = $backend; + $this->emitter = $emitter; + $enabled = \OC_Preferences::getValue($uid, 'core', 'enabled', 'true'); //TODO: DI for OC_Preferences + $this->enabled = ($enabled === 'true'); + } + + /** + * @return string + */ + public function getUID() { + return $this->uid; + } + + /** + * @return string + */ + public function getDisplayName() { + return $this->displayName; + } + + /** + * @param string $displayName + * @return bool + */ + public function setDisplayName($displayName) { + if ($this->canChangeDisplayName()) { + $this->displayName = $displayName; + $this->backend->setDisplayName($this->uid, $displayName); + return true; + } else { + return false; + } + } + + /** + * @return bool + */ + public function delete() { + if ($this->emitter) { + $this->emitter->emit('\OC\User', 'preDelete', array($this)); + } + $result = $this->backend->deleteUser($this->uid); + if ($this->emitter) { + $this->emitter->emit('\OC\User', 'postDelete', array($this)); + } + return !($result === false); + } + + /** + * @param $password + * @return bool + */ + public function checkPassword($password) { + if ($this->backend->implementsActions(\OC_USER_BACKEND_CHECK_PASSWORD)) { + $result = $this->backend->checkPassword($this->uid, $password); + if ($result !== false) { + $this->uid = $result; + } + return !($result === false); + } else { + return false; + } + } + + /** + * @param string $password + * @param string $recoveryPassword for the encryption app to reset encryption keys + * @return bool + */ + public function setPassword($password, $recoveryPassword) { + if ($this->backend->implementsActions(\OC_USER_BACKEND_SET_PASSWORD)) { + if ($this->emitter) { + $this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword)); + } + $result = $this->backend->setPassword($this->uid, $password); + if ($this->emitter) { + $this->emitter->emit('\OC\User', 'postSetPassword', array($this, $password, $recoveryPassword)); + } + return !($result === false); + } else { + return false; + } + } + + /** + * get the users home folder to mount + * + * @return string + */ + public function getHome() { + if ($this->backend->implementsActions(\OC_USER_BACKEND_GET_HOME)) { + return $this->backend->getHome($this->uid); + } + return \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data") . '/' . $this->uid; //TODO switch to Config object once implemented + } + + /** + * @return bool + */ + public function canChangePassword() { + return $this->backend->implementsActions(\OC_USER_BACKEND_SET_PASSWORD); + } + + /** + * @return bool + */ + public function canChangeDisplayName() { + return $this->backend->implementsActions(\OC_USER_BACKEND_SET_DISPLAYNAME); + } + + /** + * @return bool + */ + public function isEnabled() { + return $this->enabled; + } + + /** + * @param bool $enabled + */ + public function setEnabled($enabled) { + $this->enabled = $enabled; + $enabled = ($enabled) ? 'true' : 'false'; + \OC_Preferences::setValue($this->uid, 'core', 'enabled', $enabled); + } +} diff --git a/tests/lib/user/backend.php b/tests/lib/user/backend.php new file mode 100644 index 0000000000..40674424c9 --- /dev/null +++ b/tests/lib/user/backend.php @@ -0,0 +1,99 @@ +. +* +*/ + +/** + * Abstract class to provide the basis of backend-specific unit test classes. + * + * All subclasses MUST assign a backend property in setUp() which implements + * user operations (add, remove, etc.). Test methods in this class will then be + * run on each separate subclass and backend therein. + * + * For an example see /tests/lib/user/dummy.php + */ + +abstract class Test_User_Backend extends PHPUnit_Framework_TestCase { + /** + * @var OC_User_Backend $backend + */ + protected $backend; + + /** + * get a new unique user name + * test cases can override this in order to clean up created user + * @return array + */ + public function getUser() { + return uniqid('test_'); + } + + public function testAddRemove() { + //get the number of groups we start with, in case there are exising groups + $startCount=count($this->backend->getUsers()); + + $name1=$this->getUser(); + $name2=$this->getUser(); + $this->backend->createUser($name1, ''); + $count=count($this->backend->getUsers())-$startCount; + $this->assertEquals(1, $count); + $this->assertTrue((array_search($name1, $this->backend->getUsers())!==false)); + $this->assertFalse((array_search($name2, $this->backend->getUsers())!==false)); + $this->backend->createUser($name2, ''); + $count=count($this->backend->getUsers())-$startCount; + $this->assertEquals(2, $count); + $this->assertTrue((array_search($name1, $this->backend->getUsers())!==false)); + $this->assertTrue((array_search($name2, $this->backend->getUsers())!==false)); + + $this->backend->deleteUser($name2); + $count=count($this->backend->getUsers())-$startCount; + $this->assertEquals(1, $count); + $this->assertTrue((array_search($name1, $this->backend->getUsers())!==false)); + $this->assertFalse((array_search($name2, $this->backend->getUsers())!==false)); + } + + public function testLogin() { + $name1=$this->getUser(); + $name2=$this->getUser(); + + $this->assertFalse($this->backend->userExists($name1)); + $this->assertFalse($this->backend->userExists($name2)); + + $this->backend->createUser($name1, 'pass1'); + $this->backend->createUser($name2, 'pass2'); + + $this->assertTrue($this->backend->userExists($name1)); + $this->assertTrue($this->backend->userExists($name2)); + + $this->assertTrue($this->backend->checkPassword($name1, 'pass1')); + $this->assertTrue($this->backend->checkPassword($name2, 'pass2')); + + $this->assertFalse($this->backend->checkPassword($name1, 'pass2')); + $this->assertFalse($this->backend->checkPassword($name2, 'pass1')); + + $this->assertFalse($this->backend->checkPassword($name1, 'dummy')); + $this->assertFalse($this->backend->checkPassword($name2, 'foobar')); + + $this->backend->setPassword($name1, 'newpass1'); + $this->assertFalse($this->backend->checkPassword($name1, 'pass1')); + $this->assertTrue($this->backend->checkPassword($name1, 'newpass1')); + $this->assertFalse($this->backend->checkPassword($name2, 'newpass1')); + } +} diff --git a/tests/lib/user/database.php b/tests/lib/user/database.php new file mode 100644 index 0000000000..fe7d87c44d --- /dev/null +++ b/tests/lib/user/database.php @@ -0,0 +1,44 @@ +. +* +*/ + +class Test_User_Database extends Test_User_Backend { + /** + * get a new unique user name + * test cases can override this in order to clean up created user + * @return array + */ + public function getUser() { + $user=uniqid('test_'); + $this->users[]=$user; + return $user; + } + + public function setUp() { + $this->backend=new OC_User_Dummy(); + } + + public function tearDown() { + foreach($this->users as $user) { + $this->backend->deleteUser($user); + } + } +} diff --git a/tests/lib/user/dummy.php b/tests/lib/user/dummy.php new file mode 100644 index 0000000000..e417fd9760 --- /dev/null +++ b/tests/lib/user/dummy.php @@ -0,0 +1,27 @@ +. +* +*/ + +class Test_User_Dummy extends Test_User_Backend { + public function setUp() { + $this->backend=new OC_User_Dummy(); + } +} diff --git a/tests/lib/user/manager.php b/tests/lib/user/manager.php new file mode 100644 index 0000000000..bc49f6db4b --- /dev/null +++ b/tests/lib/user/manager.php @@ -0,0 +1,304 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\User; + +class Manager extends \PHPUnit_Framework_TestCase { + public function testUserExistsSingleBackendExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertTrue($manager->userExists('foo')); + } + + public function testUserExistsSingleBackendNotExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertFalse($manager->userExists('foo')); + } + + public function testUserExistsNoBackends() { + $manager = new \OC\User\Manager(); + + $this->assertFalse($manager->userExists('foo')); + } + + public function testUserExistsTwoBackendsSecondExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend1 + */ + $backend1 = $this->getMock('\OC_User_Dummy'); + $backend1->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend2 + */ + $backend2 = $this->getMock('\OC_User_Dummy'); + $backend2->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend1); + $manager->registerBackend($backend2); + + $this->assertTrue($manager->userExists('foo')); + } + + public function testUserExistsTwoBackendsFirstExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend1 + */ + $backend1 = $this->getMock('\OC_User_Dummy'); + $backend1->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend2 + */ + $backend2 = $this->getMock('\OC_User_Dummy'); + $backend2->expects($this->never()) + ->method('userExists'); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend1); + $manager->registerBackend($backend2); + + $this->assertTrue($manager->userExists('foo')); + } + + public function testGetOneBackendExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertEquals('foo', $manager->get('foo')->getUID()); + } + + public function testGetOneBackendNotExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertEquals(null, $manager->get('foo')); + } + + public function testSearchOneBackend() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('getUsers') + ->with($this->equalTo('fo')) + ->will($this->returnValue(array('foo', 'afoo'))); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $result = $manager->search('fo'); + $this->assertEquals(2, count($result)); + $this->assertEquals('afoo', $result[0]->getUID()); + $this->assertEquals('foo', $result[1]->getUID()); + } + + public function testSearchTwoBackendLimitOffset() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend1 + */ + $backend1 = $this->getMock('\OC_User_Dummy'); + $backend1->expects($this->once()) + ->method('getUsers') + ->with($this->equalTo('fo'), $this->equalTo(3), $this->equalTo(1)) + ->will($this->returnValue(array('foo1', 'foo2'))); + + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend2 + */ + $backend2 = $this->getMock('\OC_User_Dummy'); + $backend2->expects($this->once()) + ->method('getUsers') + ->with($this->equalTo('fo'), $this->equalTo(1), $this->equalTo(0)) + ->will($this->returnValue(array('foo3'))); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend1); + $manager->registerBackend($backend2); + + $result = $manager->search('fo', 3, 1); + $this->assertEquals(3, count($result)); + $this->assertEquals('foo1', $result[0]->getUID()); + $this->assertEquals('foo2', $result[1]->getUID()); + $this->assertEquals('foo3', $result[2]->getUID()); + } + + public function testCreateUserSingleBackendNotExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(true)); + + $backend->expects($this->once()) + ->method('createUser') + ->with($this->equalTo('foo'), $this->equalTo('bar')); + + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $user = $manager->createUser('foo', 'bar'); + $this->assertEquals('foo', $user->getUID()); + } + + /** + * @expectedException \Exception + */ + public function testCreateUserSingleBackendExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(true)); + + $backend->expects($this->never()) + ->method('createUser'); + + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $manager->createUser('foo', 'bar'); + } + + public function testCreateUserSingleBackendNotSupported() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $backend->expects($this->never()) + ->method('createUser'); + + $backend->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertFalse($manager->createUser('foo', 'bar')); + } + + public function testCreateUserNoBackends() { + $manager = new \OC\User\Manager(); + + $this->assertFalse($manager->createUser('foo', 'bar')); + } + + /** + * @expectedException \Exception + */ + public function testCreateUserTwoBackendExists() { + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend1 + */ + $backend1 = $this->getMock('\OC_User_Dummy'); + $backend1->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(true)); + + $backend1->expects($this->never()) + ->method('createUser'); + + $backend1->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + + /** + * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend2 + */ + $backend2 = $this->getMock('\OC_User_Dummy'); + $backend2->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(true)); + + $backend2->expects($this->never()) + ->method('createUser'); + + $backend2->expects($this->once()) + ->method('userExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend1); + $manager->registerBackend($backend2); + + $manager->createUser('foo', 'bar'); + } +} diff --git a/tests/lib/user/session.php b/tests/lib/user/session.php new file mode 100644 index 0000000000..274e9e2831 --- /dev/null +++ b/tests/lib/user/session.php @@ -0,0 +1,155 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\User; + +class Session extends \PHPUnit_Framework_TestCase { + public function testGetUser() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->once()) + ->method('get') + ->with('user_id') + ->will($this->returnValue('foo')); + + $backend = $this->getMock('OC_User_Dummy'); + $backend->expects($this->once()) + ->method('userExists') + ->with('foo') + ->will($this->returnValue(true)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $userSession = new \OC\User\Session($manager, $session); + $user = $userSession->getUser(); + $this->assertEquals('foo', $user->getUID()); + } + + public function testSetUser() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->once()) + ->method('set') + ->with('user_id', 'foo'); + + $manager = $this->getMock('\OC\User\Manager'); + + $backend = $this->getMock('OC_User_Dummy'); + + $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('foo')); + + $userSession = new \OC\User\Session($manager, $session); + $userSession->setUser($user); + } + + public function testLoginValidPasswordEnabled() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->once()) + ->method('set') + ->with('user_id', 'foo'); + + $manager = $this->getMock('\OC\User\Manager'); + + $backend = $this->getMock('OC_User_Dummy'); + + $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); + $user->expects($this->once()) + ->method('checkPassword') + ->with('bar') + ->will($this->returnValue(true)); + $user->expects($this->once()) + ->method('isEnabled') + ->will($this->returnValue(true)); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('foo')); + + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + + $userSession = new \OC\User\Session($manager, $session); + $userSession->login('foo', 'bar'); + $this->assertEquals($user, $userSession->getUser()); + } + + public function testLoginValidPasswordDisabled() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->never()) + ->method('set'); + + $manager = $this->getMock('\OC\User\Manager'); + + $backend = $this->getMock('OC_User_Dummy'); + + $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); + $user->expects($this->once()) + ->method('checkPassword') + ->with('bar') + ->will($this->returnValue(true)); + $user->expects($this->once()) + ->method('isEnabled') + ->will($this->returnValue(false)); + + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + + $userSession = new \OC\User\Session($manager, $session); + $userSession->login('foo', 'bar'); + } + + public function testLoginInValidPassword() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->never()) + ->method('set'); + + $manager = $this->getMock('\OC\User\Manager'); + + $backend = $this->getMock('OC_User_Dummy'); + + $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); + $user->expects($this->once()) + ->method('checkPassword') + ->with('bar') + ->will($this->returnValue(false)); + $user->expects($this->never()) + ->method('isEnabled'); + + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($user)); + + $userSession = new \OC\User\Session($manager, $session); + $userSession->login('foo', 'bar'); + } + + public function testLoginNonExisting() { + $session = $this->getMock('\OC\Session\Memory', array(), array('')); + $session->expects($this->never()) + ->method('set'); + + $manager = $this->getMock('\OC\User\Manager'); + + $backend = $this->getMock('OC_User_Dummy'); + + $manager->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue(null)); + + $userSession = new \OC\User\Session($manager, $session); + $userSession->login('foo', 'bar'); + } +} diff --git a/tests/lib/user/user.php b/tests/lib/user/user.php new file mode 100644 index 0000000000..b0d170cbfc --- /dev/null +++ b/tests/lib/user/user.php @@ -0,0 +1,364 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\User; + +use OC\Hooks\PublicEmitter; + +class User extends \PHPUnit_Framework_TestCase { + public function testDisplayName() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Backend'); + $backend->expects($this->once()) + ->method('getDisplayName') + ->with($this->equalTo('foo')) + ->will($this->returnValue('Foo')); + + $backend->expects($this->any()) + ->method('implementsActions') + ->with($this->equalTo(\OC_USER_BACKEND_GET_DISPLAYNAME)) + ->will($this->returnValue(true)); + + $user = new \OC\User\User('foo', $backend); + $this->assertEquals('Foo', $user->getDisplayName()); + } + + public function testDisplayNameNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Backend'); + $backend->expects($this->never()) + ->method('getDisplayName'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->with($this->equalTo(\OC_USER_BACKEND_GET_DISPLAYNAME)) + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertEquals('foo', $user->getDisplayName()); + } + + public function testSetPassword() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('setPassword') + ->with($this->equalTo('foo'), $this->equalTo('bar')); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_SET_PASSWORD) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->setPassword('bar','')); + } + + public function testSetPasswordNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->never()) + ->method('setPassword'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertFalse($user->setPassword('bar','')); + } + + public function testDelete() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('deleteUser') + ->with($this->equalTo('foo')); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->delete()); + } + + public function testCheckPassword() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('checkPassword') + ->with($this->equalTo('foo'), $this->equalTo('bar')) + ->will($this->returnValue(true)); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_CHECK_PASSWORD) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->checkPassword('bar')); + } + + public function testCheckPasswordNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->never()) + ->method('checkPassword'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertFalse($user->checkPassword('bar')); + } + + public function testGetHome() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('getHome') + ->with($this->equalTo('foo')) + ->will($this->returnValue('/home/foo')); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_GET_HOME) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend); + $this->assertEquals('/home/foo', $user->getHome()); + } + + public function testGetHomeNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->never()) + ->method('getHome'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertEquals(\OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data") . '/foo', $user->getHome()); + } + + public function testCanChangePassword() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_SET_PASSWORD) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->canChangePassword()); + } + + public function testCanChangePasswordNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertFalse($user->canChangePassword()); + } + + public function testCanChangeDisplayName() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_SET_DISPLAYNAME) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->canChangeDisplayName()); + } + + public function testCanChangeDisplayNameNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $user = new \OC\User\User('foo', $backend); + $this->assertFalse($user->canChangeDisplayName()); + } + + public function testSetDisplayNameSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Database'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_SET_DISPLAYNAME) { + return true; + } else { + return false; + } + })); + + $backend->expects($this->once()) + ->method('setDisplayName') + ->with('foo','Foo'); + + $user = new \OC\User\User('foo', $backend); + $this->assertTrue($user->setDisplayName('Foo')); + $this->assertEquals('Foo',$user->getDisplayName()); + } + + public function testSetDisplayNameNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Database'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + return false; + })); + + $backend->expects($this->never()) + ->method('setDisplayName'); + + $user = new \OC\User\User('foo', $backend); + $this->assertFalse($user->setDisplayName('Foo')); + $this->assertEquals('foo',$user->getDisplayName()); + } + + public function testSetPasswordHooks() { + $hooksCalled = 0; + $test = $this; + + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('setPassword'); + + /** + * @param \OC\User\User $user + * @param string $password + */ + $hook = function ($user, $password) use ($test, &$hooksCalled) { + $hooksCalled++; + $test->assertEquals('foo', $user->getUID()); + $test->assertEquals('bar', $password); + }; + + $emitter = new PublicEmitter(); + $emitter->listen('\OC\User', 'preSetPassword', $hook); + $emitter->listen('\OC\User', 'postSetPassword', $hook); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_SET_PASSWORD) { + return true; + } else { + return false; + } + })); + + $user = new \OC\User\User('foo', $backend, $emitter); + + $user->setPassword('bar',''); + $this->assertEquals(2, $hooksCalled); + } + + public function testDeleteHooks() { + $hooksCalled = 0; + $test = $this; + + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('deleteUser'); + + /** + * @param \OC\User\User $user + */ + $hook = function ($user) use ($test, &$hooksCalled) { + $hooksCalled++; + $test->assertEquals('foo', $user->getUID()); + }; + + $emitter = new PublicEmitter(); + $emitter->listen('\OC\User', 'preDelete', $hook); + $emitter->listen('\OC\User', 'postDelete', $hook); + + $user = new \OC\User\User('foo', $backend, $emitter); + $this->assertTrue($user->delete()); + $this->assertEquals(2, $hooksCalled); + } +}