From 03b65c196c7e794abfda1581278d5e37df541525 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sun, 31 Aug 2014 17:42:48 +0200 Subject: [PATCH] Worked on user registration --- src/Controllers/UserController.php | 19 +++++----- src/Entities/User.php | 3 ++ src/FormData/RegistrationFormData.php | 9 +++++ src/Services/AuthService.php | 31 ++++++++++++---- src/Services/UserService.php | 51 ++++++++++++++++----------- src/ViewProxies/User.php | 2 ++ tests/Dao/TokenDaoTest.php | 28 +++++++++++++++ tests/Services/AuthServiceTest.php | 35 +++++++++++++++--- 8 files changed, 139 insertions(+), 39 deletions(-) create mode 100644 src/FormData/RegistrationFormData.php create mode 100644 tests/Dao/TokenDaoTest.php diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index 4b0f59cd..333bf0e6 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -3,35 +3,36 @@ namespace Szurubooru\Controllers; final class UserController extends AbstractController { - private $userService; - private $passwordService; private $inputReader; + private $userService; public function __construct( \Szurubooru\Services\UserService $userService, - \Szurubooru\Services\PasswordService $passwordService, \Szurubooru\Helpers\InputReader $inputReader) { $this->inputReader = $inputReader; $this->userService = $userService; - $this->passwordService = $passwordService; } public function registerRoutes(\Szurubooru\Router $router) { - $router->post('/api/users', [$this, 'create']); + $router->post('/api/users', [$this, 'register']); $router->get('/api/users', [$this, 'getAll']); $router->get('/api/users/:id', [$this, 'getById']); $router->put('/api/users/:id', [$this, 'update']); $router->delete('/api/users/:id', [$this, 'delete']); } - public function create() + public function register() { - $this->userService->validateUserName($this->inputReader->userName); - $this->passwordService->validatePassword($this->inputReader->password); + $input = new \Szurubooru\FormData\RegistrationFormData; + $input->name = $this->inputReader->userName; + $input->password = $this->inputReader->password; + $input->email = $this->inputReader->email; - throw new \BadMethodCallException('Not implemented'); + $user = $this->userService->register($input); + + return new \Szurubooru\ViewProxies\User($user); } public function update($id) diff --git a/src/Entities/User.php b/src/Entities/User.php index 35174187..dcbbaac9 100644 --- a/src/Entities/User.php +++ b/src/Entities/User.php @@ -12,4 +12,7 @@ final class User extends Entity public $name; public $passwordHash; + public $email; + public $registrationDate; + public $lastLoginTime; } diff --git a/src/FormData/RegistrationFormData.php b/src/FormData/RegistrationFormData.php new file mode 100644 index 00000000..99f2a9b2 --- /dev/null +++ b/src/FormData/RegistrationFormData.php @@ -0,0 +1,9 @@ +loggedInUser = new \Szurubooru\Entities\User(); - $this->loggedInUser->name = 'Anonymous'; - $this->userDao = $userDao; - $this->tokenDao = $tokenDao; + $this->loggedInUser = $this->getAnonymousUser(); $this->passwordService = $passwordService; + $this->timeService = $timeService; + $this->tokenDao = $tokenDao; + $this->userDao = $userDao; } public function isLoggedIn() @@ -47,8 +49,9 @@ final class AuthService if ($user->passwordHash != $passwordHash) throw new \InvalidArgumentException('Specified password is invalid.'); - $this->loggedInUser = $user; $this->loginToken = $this->createAndSaveLoginToken($user); + $this->loggedInUser = $user; + $this->updateLoginTime($user); } public function loginFromToken($loginTokenName) @@ -59,6 +62,8 @@ final class AuthService $this->loginToken = $loginToken; $this->loggedInUser = $this->userDao->getById($loginToken->additionalData); + $this->updateLoginTime($this->loggedInUser); + if (!$this->loggedInUser) { $this->logout(); @@ -66,10 +71,18 @@ final class AuthService } } + public function getAnonymousUser() + { + $user = new \Szurubooru\Entities\User(); + $user->name = 'Anonymous user'; + $user->accessRank = \Szurubooru\Entities\User::ACCESS_RANK_ANONYMOUS; + return $user; + } + public function loginAnonymous() { $this->loginToken = null; - $this->loggedInUser = $this->userService->getAnonymousUser(); + $this->loggedInUser = $this->getAnonymousUser(); } public function logout() @@ -90,4 +103,10 @@ final class AuthService $this->tokenDao->save($loginToken); return $loginToken; } + + private function updateLoginTime($user) + { + $user->lastLoginTime = $this->timeService->getCurrentTime(); + $this->userDao->save($user); + } } diff --git a/src/Services/UserService.php b/src/Services/UserService.php index 9e4951d7..0433de4e 100644 --- a/src/Services/UserService.php +++ b/src/Services/UserService.php @@ -3,34 +3,52 @@ namespace Szurubooru\Services; class UserService { - private $userDao; private $config; + private $userDao; + private $passwordService; + private $emailService; + private $timeService; public function __construct( + \Szurubooru\Config $config, \Szurubooru\Dao\UserDao $userDao, - \Szurubooru\Config $config) + \Szurubooru\Services\PasswordService $passwordService, + \Szurubooru\Services\EmailService $emailService, + \Szurubooru\Services\TimeService $timeService) { - $this->userDao = $userDao; $this->config = $config; + $this->userDao = $userDao; + $this->passwordService = $passwordService; + $this->emailService = $emailService; + $this->timeService = $timeService; } - public function getById($userId) + public function register(\Szurubooru\FormData\RegistrationFormData $formData) { - return $this->userDao->getById($userId); - } + $this->validateUserName($formData->name); + $this->passwordService->validatePassword($formData->password); + $this->emailService->validateEmail($formData->email); - public function getByName($userName) - { - return $this->userDao->getByName($userName); - } + if ($this->userDao->getByName($formData->name)) + throw new \DomainException('User with this name already exists.'); + + //todo: privilege checking + + $user = new \Szurubooru\Entities\User(); + $user->name = $formData->name; + $user->email = $formData->email; + $user->passwordHash = $this->passwordService->getHash($formData->password); + $user->registrationTime = $this->timeService->getCurrentTime(); + + //todo: send activation mail if necessary - public function save($user) - { return $this->userDao->save($user); } - public function validateUserName($userName) + public function validateUserName(&$userName) { + $userName = trim($userName); + if (!$userName) throw new \DomainException('User name cannot be empty.'); @@ -41,11 +59,4 @@ class UserService if (strlen($userName) < $maxUserNameLength) throw new \DomainException('User name must have at most ' . $minUserNameLength . ' character(s).'); } - - public function getAnonymousUser() - { - $user = new \Szurubooru\Entities\User(); - $user->name = 'Anonymous user'; - $user->accessRank = \Szurubooru\Entities\User::ACCESS_RANK_ANONYMOUS; - } } diff --git a/src/ViewProxies/User.php b/src/ViewProxies/User.php index 6a27b21f..9e7bc49d 100644 --- a/src/ViewProxies/User.php +++ b/src/ViewProxies/User.php @@ -3,6 +3,7 @@ namespace Szurubooru\ViewProxies; class User { + public $id; public $name; public function __construct($user) @@ -10,6 +11,7 @@ class User if (!$user) return; + $this->id = $user->id; $this->name = $user->name; } } diff --git a/tests/Dao/TokenDaoTest.php b/tests/Dao/TokenDaoTest.php new file mode 100644 index 00000000..7763cf91 --- /dev/null +++ b/tests/Dao/TokenDaoTest.php @@ -0,0 +1,28 @@ +databaseConnection); + + $token = new \Szurubooru\Entities\Token(); + $token->name = 'test'; + + $tokenDao->save($token); + $expected = $token; + $actual = $tokenDao->getByName($token->name); + + $this->assertEquals($actual, $expected); + } + + public function testRetrievingByInvalidName() + { + $tokenDao = new \Szurubooru\Dao\TokenDao($this->databaseConnection); + + $actual = $tokenDao->getByName('rubbish'); + + $this->assertNull($actual); + } +} diff --git a/tests/Services/AuthServiceTest.php b/tests/Services/AuthServiceTest.php index 966f34b9..a1bfc4f6 100644 --- a/tests/Services/AuthServiceTest.php +++ b/tests/Services/AuthServiceTest.php @@ -6,18 +6,20 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase public function testInvalidUser() { $passwordServiceMock = $this->getPasswordServiceMock(); + $timeServiceMock = $this->getTimeServiceMock(); $tokenDaoMock = $this->getTokenDaoMock(); $userDaoMock = $this->getUserDaoMock(); $this->setExpectedException(\InvalidArgumentException::class, 'User not found'); - $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $tokenDaoMock, $userDaoMock); + $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $timeServiceMock, $tokenDaoMock, $userDaoMock); $authService->loginFromCredentials('dummy', 'godzilla'); } public function testInvalidPassword() { $passwordServiceMock = $this->getPasswordServiceMock(); + $timeServiceMock = $this->getTimeServiceMock(); $tokenDaoMock = $this->getTokenDaoMock(); $userDaoMock = $this->getUserDaoMock(); @@ -28,7 +30,7 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase $testUser->passwordHash = 'hash'; $userDaoMock->expects($this->once())->method('getByName')->willReturn($testUser); - $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $tokenDaoMock, $userDaoMock); + $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $timeServiceMock, $tokenDaoMock, $userDaoMock); $this->setExpectedException(\InvalidArgumentException::class, 'Specified password is invalid'); $authService->loginFromCredentials('dummy', 'godzilla'); } @@ -36,6 +38,7 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase public function testValidCredentials() { $passwordServiceMock = $this->getPasswordServiceMock(); + $timeServiceMock = $this->getTimeServiceMock(); $tokenDaoMock = $this->getTokenDaoMock(); $userDaoMock = $this->getUserDaoMock(); @@ -47,16 +50,33 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase $testUser->passwordHash = 'hash'; $userDaoMock->expects($this->once())->method('getByName')->willReturn($testUser); - $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $tokenDaoMock, $userDaoMock); + $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $timeServiceMock, $tokenDaoMock, $userDaoMock); $authService->loginFromCredentials('dummy', 'godzilla'); $this->assertTrue($authService->isLoggedIn()); $this->assertEquals($testUser, $authService->getLoggedInUser()); + $this->assertNotNull($authService->getLoginToken()); + $this->assertNotNull($authService->getLoginToken()->name); + } + + public function testInvalidToken() + { + $passwordServiceMock = $this->getPasswordServiceMock(); + $timeServiceMock = $this->getTimeServiceMock(); + $tokenDaoMock = $this->getTokenDaoMock(); + $userDaoMock = $this->getUserDaoMock(); + + $tokenDaoMock->expects($this->once())->method('getByName')->willReturn(null); + + $this->setExpectedException(\Exception::class); + $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $timeServiceMock, $tokenDaoMock, $userDaoMock); + $authService->loginFromToken(''); } public function testValidToken() { $passwordServiceMock = $this->getPasswordServiceMock(); + $timeServiceMock = $this->getTimeServiceMock(); $tokenDaoMock = $this->getTokenDaoMock(); $userDaoMock = $this->getUserDaoMock(); @@ -71,11 +91,13 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase $testToken->additionalData = $testUser->id; $tokenDaoMock->expects($this->once())->method('getByName')->willReturn($testToken); - $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $tokenDaoMock, $userDaoMock); + $authService = new \Szurubooru\Services\AuthService($passwordServiceMock, $timeServiceMock, $tokenDaoMock, $userDaoMock); $authService->loginFromToken($testToken->name); $this->assertTrue($authService->isLoggedIn()); $this->assertEquals($testUser, $authService->getLoggedInUser()); + $this->assertNotNull($authService->getLoginToken()); + $this->assertNotNull($authService->getLoginToken()->name); } private function getTokenDaoMock() @@ -92,4 +114,9 @@ class AuthServiceTest extends \PHPUnit_Framework_TestCase { return $this->getMockBuilder(\Szurubooru\Services\PasswordService::class)->disableOriginalConstructor()->getMock(); } + + private function getTimeServiceMock() + { + return $this->getMockBuilder(\Szurubooru\Services\TimeService::class)->disableOriginalConstructor()->getMock(); + } }