From 7d95e8fc0956a27993f801970d082fa026c0c334 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Mon, 25 Mar 2019 08:31:49 -0400 Subject: [PATCH] Split Fever user management from protocol handler --- lib/REST/Fever/API.php | 22 ------- lib/REST/Fever/User.php | 34 +++++++++++ tests/cases/REST/Fever/TestAPI.php | 57 ----------------- tests/cases/REST/Fever/TestUser.php | 94 +++++++++++++++++++++++++++++ tests/phpunit.xml | 1 + 5 files changed, 129 insertions(+), 79 deletions(-) create mode 100644 lib/REST/Fever/User.php create mode 100644 tests/cases/REST/Fever/TestUser.php diff --git a/lib/REST/Fever/API.php b/lib/REST/Fever/API.php index b80fe8d4..c4ad36b4 100644 --- a/lib/REST/Fever/API.php +++ b/lib/REST/Fever/API.php @@ -97,26 +97,4 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$user->id = $s['user']; return true; } - - public static function userRegister(string $user, string $password = null): string { - $password = $password ?? Arsse::$user->generatePassword(); - $hash = md5("$user:$password"); - $tr = Arsse::$db->begin(); - Arsse::$db->tokenRevoke($user, "fever.login"); - Arsse::$db->tokenCreate($user, "fever.login", $hash); - $tr->commit(); - return $password; - } - - public static function userUnregister(string $user): bool { - return (bool) Arsse::$db->tokenRevoke($user, "fever.login"); - } - - public static function userAuthenticate(string $user, string $password): bool { - try { - return (bool) Arsse::$db->tokenLookup("fever.login", md5("$user:$password")); - } catch (ExceptionInput $e) { - return false; - } - } } diff --git a/lib/REST/Fever/User.php b/lib/REST/Fever/User.php new file mode 100644 index 00000000..ac3cf696 --- /dev/null +++ b/lib/REST/Fever/User.php @@ -0,0 +1,34 @@ +generatePassword(); + $hash = md5("$user:$password"); + $tr = Arsse::$db->begin(); + Arsse::$db->tokenRevoke($user, "fever.login"); + Arsse::$db->tokenCreate($user, "fever.login", $hash); + $tr->commit(); + return $password; + } + + public static function unregister(string $user): bool { + return (bool) Arsse::$db->tokenRevoke($user, "fever.login"); + } + + public static function authenticate(string $user, string $password): bool { + try { + return (bool) Arsse::$db->tokenLookup("fever.login", md5("$user:$password")); + } catch (ExceptionInput $e) { + return false; + } + } +} diff --git a/tests/cases/REST/Fever/TestAPI.php b/tests/cases/REST/Fever/TestAPI.php index b1c6901b..c76d567f 100644 --- a/tests/cases/REST/Fever/TestAPI.php +++ b/tests/cases/REST/Fever/TestAPI.php @@ -144,61 +144,4 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { [true, false, "validUser", ['api_key' => "invalidToken"], ['api' => null], $success], ]; } - - /** @dataProvider providePasswordCreations */ - public function testRegisterAUserPassword(string $user, string $password = null, $exp) { - \Phake::when(Arsse::$user)->generatePassword->thenReturn("RANDOM_PASSWORD"); - \Phake::when(Arsse::$db)->tokenCreate->thenReturnCallback(function($user, $class, $id = null) { - return $id ?? "RANDOM_TOKEN"; - }); - \Phake::when(Arsse::$db)->tokenCreate("john.doe@example.org", $this->anything(), $this->anything())->thenThrow(new UserException("doesNotExist")); - try { - if ($exp instanceof \JKingWeb\Arsse\AbstractException) { - $this->assertException($exp); - API::userRegister($user, $password); - } else { - $this->assertSame($exp, API::userRegister($user, $password)); - } - } finally { - \Phake::verify(Arsse::$db)->tokenRevoke($user, "fever.login"); - \Phake::verify(Arsse::$db)->tokenCreate($user, "fever.login", md5($user.":".($password ?? "RANDOM_PASSWORD"))); - } - } - - public function providePasswordCreations() { - return [ - ["jane.doe@example.com", "secret", "secret"], - ["jane.doe@example.com", "superman", "superman"], - ["jane.doe@example.com", null, "RANDOM_PASSWORD"], - ["john.doe@example.org", null, new UserException("doesNotExist")], - ["john.doe@example.net", null, "RANDOM_PASSWORD"], - ["john.doe@example.net", "secret", "secret"], - ]; - } - - public function testUnregisterAUser() { - \Phake::when(Arsse::$db)->tokenRevoke->thenReturn(3); - $this->assertTrue(API::userUnregister("jane.doe@example.com")); - \Phake::verify(Arsse::$db)->tokenRevoke("jane.doe@example.com", "fever.login"); - \Phake::when(Arsse::$db)->tokenRevoke->thenReturn(0); - $this->assertFalse(API::userUnregister("john.doe@example.com")); - \Phake::verify(Arsse::$db)->tokenRevoke("john.doe@example.com", "fever.login"); - } - - /** @dataProvider provideUserAuthenticationRequests */ - public function testAuthenticateAUserName(string $user, string $password, bool $exp) { - \Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("constraintViolation")); - \Phake::when(Arsse::$db)->tokenLookup("fever.login", md5("jane.doe@example.com:secret"))->thenReturn(['user' => "jane.doe@example.com"]); - \Phake::when(Arsse::$db)->tokenLookup("fever.login", md5("john.doe@example.com:superman"))->thenReturn(['user' => "john.doe@example.com"]); - $this->assertSame($exp, API::userAuthenticate($user, $password)); - } - - public function provideUserAuthenticationRequests() { - return [ - ["jane.doe@example.com", "secret", true], - ["jane.doe@example.com", "superman", false], - ["john.doe@example.com", "secret", false], - ["john.doe@example.com", "superman", true], - ]; - } } diff --git a/tests/cases/REST/Fever/TestUser.php b/tests/cases/REST/Fever/TestUser.php new file mode 100644 index 00000000..c1856472 --- /dev/null +++ b/tests/cases/REST/Fever/TestUser.php @@ -0,0 +1,94 @@ + */ +class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { + protected $u; + + public function setUp() { + self::clearData(); + self::setConf(); + // create a mock user manager + Arsse::$user = \Phake::mock(User::class); + \Phake::when(Arsse::$user)->auth->thenReturn(true); + // create a mock database interface + Arsse::$db = \Phake::mock(Database::class); + \Phake::when(Arsse::$db)->begin->thenReturn(\Phake::mock(Transaction::class)); + // instantiate the handler + $this->u = new FeverUser(); + } + + public function tearDown() { + self::clearData(); + } + + /** @dataProvider providePasswordCreations */ + public function testRegisterAUserPassword(string $user, string $password = null, $exp) { + \Phake::when(Arsse::$user)->generatePassword->thenReturn("RANDOM_PASSWORD"); + \Phake::when(Arsse::$db)->tokenCreate->thenReturnCallback(function($user, $class, $id = null) { + return $id ?? "RANDOM_TOKEN"; + }); + \Phake::when(Arsse::$db)->tokenCreate("john.doe@example.org", $this->anything(), $this->anything())->thenThrow(new UserException("doesNotExist")); + try { + if ($exp instanceof \JKingWeb\Arsse\AbstractException) { + $this->assertException($exp); + $this->u->register($user, $password); + } else { + $this->assertSame($exp, $this->u->register($user, $password)); + } + } finally { + \Phake::verify(Arsse::$db)->tokenRevoke($user, "fever.login"); + \Phake::verify(Arsse::$db)->tokenCreate($user, "fever.login", md5($user.":".($password ?? "RANDOM_PASSWORD"))); + } + } + + public function providePasswordCreations() { + return [ + ["jane.doe@example.com", "secret", "secret"], + ["jane.doe@example.com", "superman", "superman"], + ["jane.doe@example.com", null, "RANDOM_PASSWORD"], + ["john.doe@example.org", null, new UserException("doesNotExist")], + ["john.doe@example.net", null, "RANDOM_PASSWORD"], + ["john.doe@example.net", "secret", "secret"], + ]; + } + + public function testUnregisterAUser() { + \Phake::when(Arsse::$db)->tokenRevoke->thenReturn(3); + $this->assertTrue($this->u->unregister("jane.doe@example.com")); + \Phake::verify(Arsse::$db)->tokenRevoke("jane.doe@example.com", "fever.login"); + \Phake::when(Arsse::$db)->tokenRevoke->thenReturn(0); + $this->assertFalse($this->u->unregister("john.doe@example.com")); + \Phake::verify(Arsse::$db)->tokenRevoke("john.doe@example.com", "fever.login"); + } + + /** @dataProvider provideUserAuthenticationRequests */ + public function testAuthenticateAUserName(string $user, string $password, bool $exp) { + \Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("constraintViolation")); + \Phake::when(Arsse::$db)->tokenLookup("fever.login", md5("jane.doe@example.com:secret"))->thenReturn(['user' => "jane.doe@example.com"]); + \Phake::when(Arsse::$db)->tokenLookup("fever.login", md5("john.doe@example.com:superman"))->thenReturn(['user' => "john.doe@example.com"]); + $this->assertSame($exp, $this->u->authenticate($user, $password)); + } + + public function provideUserAuthenticationRequests() { + return [ + ["jane.doe@example.com", "secret", true], + ["jane.doe@example.com", "superman", false], + ["john.doe@example.com", "secret", false], + ["john.doe@example.com", "superman", true], + ]; + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 9200dd8f..7c698ab7 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -105,6 +105,7 @@ cases/REST/TinyTinyRSS/PDO/TestAPI.php + cases/REST/Fever/TestUser.php cases/REST/Fever/TestAPI.php cases/REST/Fever/PDO/TestAPI.php