From d59223bbcb7336ab44b45cbd70b041c7338f97cb Mon Sep 17 00:00:00 2001 From: "J. King" Date: Mon, 18 Mar 2019 22:49:47 -0400 Subject: [PATCH] First authentication test for Fever --- lib/REST/Fever/API.php | 8 +- tests/cases/REST/Fever/PDO/TestAPI.php | 13 ++++ tests/cases/REST/Fever/TestAPI.php | 100 +++++++++++++++++++++++++ tests/phpunit.xml | 4 + 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 tests/cases/REST/Fever/PDO/TestAPI.php create mode 100644 tests/cases/REST/Fever/TestAPI.php diff --git a/lib/REST/Fever/API.php b/lib/REST/Fever/API.php index b9fd5ad..6effe27 100644 --- a/lib/REST/Fever/API.php +++ b/lib/REST/Fever/API.php @@ -20,7 +20,7 @@ use JKingWeb\Arsse\REST\Exception404; use JKingWeb\Arsse\REST\Exception405; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; -use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\JsonResponse; use Zend\Diactoros\Response\EmptyResponse; class API extends \JKingWeb\Arsse\REST\AbstractHandler { @@ -31,7 +31,8 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { public function dispatch(ServerRequestInterface $req): ResponseInterface { $inR = $req->getQueryParams(); - if (!array_key_exists("api")) { + $inW = $req->getParsedBody(); + if (!array_key_exists("api", $inR)) { // the original would have shown the Fever UI in the absence of the "api" parameter, but we'll return 404 return new EmptyResponse(404); } @@ -44,7 +45,6 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { if (strlen($req->getHeaderLine("Content-Type")) && $req->getHeaderLine("Content-Type") !== "application/x-www-form-urlencoded") { return new EmptyResponse(415, ['Accept' => "application/x-www-form-urlencoded"]); } - $inW = $req->getParsedBody(); $out = [ 'api_version' => self::LEVEL, 'auth' => 0, @@ -80,7 +80,7 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { } try { // verify the supplied hash is valid - $s = Arsse::$db->TokenLookup($id, "fever.login"); + $s = Arsse::$db->TokenLookup("fever.login", $hash); } catch (\JKingWeb\Arsse\Db\ExceptionInput $e) { return false; } diff --git a/tests/cases/REST/Fever/PDO/TestAPI.php b/tests/cases/REST/Fever/PDO/TestAPI.php new file mode 100644 index 0000000..02caa3d --- /dev/null +++ b/tests/cases/REST/Fever/PDO/TestAPI.php @@ -0,0 +1,13 @@ + + * @group optional */ +class TestAPI extends \JKingWeb\Arsse\TestCase\REST\Fever\TestAPI { + use \JKingWeb\Arsse\Test\PDOTest; +} diff --git a/tests/cases/REST/Fever/TestAPI.php b/tests/cases/REST/Fever/TestAPI.php new file mode 100644 index 0000000..54b3e07 --- /dev/null +++ b/tests/cases/REST/Fever/TestAPI.php @@ -0,0 +1,100 @@ + */ +class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { + + protected function v($value) { + return $value; + } + + protected function req($dataGet, $dataPost, string $method = "POST", string $type = null, string $url = "", string $user = null): ResponseInterface { + $url = "/fever/".$url; + $server = [ + 'REQUEST_METHOD' => $method, + 'REQUEST_URI' => $url, + 'HTTP_CONTENT_TYPE' => $type ?? "application/x-www-form-urlencoded", + ]; + $req = new ServerRequest($server, [], $url, $method, "php://memory"); + if (is_array($dataGet)) { + $req = $req->withRequestTarget($url)->withQueryParams($dataGet); + } else { + $req = $req->withRequestTarget($url."?".http_build_query((string) $dataGet, "", "&", \PHP_QUERY_RFC3986)); + } + if (is_array($dataPost)) { + $req = $req->withParsedBody($dataPost); + } else { + $body = $req->getBody(); + $body->write($strData); + $req = $req->withBody($body); + } + if (isset($user)) { + if (strlen($user)) { + $req = $req->withAttribute("authenticated", true)->withAttribute("authenticatedUser", $user); + } else { + $req = $req->withAttribute("authenticationFailed", true); + } + } + return $this->h->dispatch($req); + } + + 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); + Arsse::$user->id = "john.doe@example.com"; + // 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->h = new API(); + } + + public function tearDown() { + self::clearData(); + } + + /** @dataProvider provideAuthenticationRequests */ + public function testAuthenticateAUser(bool $httpRequired, bool $tokenEnforced, string $httpUser = null, array $dataPost, array $dataGet, bool $success) { + self::setConf([ + 'userHTTPAuthRequired' => $httpRequired, + 'userSessionEnforced' => $tokenEnforced, + ], true); + \Phake::when(Arsse::$db)->tokenLookup->thenThrow(new ExceptionInput("subjectMissing")); + \Phake::when(Arsse::$db)->tokenLookup("fever.login", "validtoken")->thenReturn(['user' => "jane.doe@example.com"]); + $exp = new JsonResponse($success ? ['api_version' => API::LEVEL, 'auth' => 1] : ['api_version' => API::LEVEL, 'auth' => 0]); + $act = $this->req($dataGet, $dataPost, "POST", null, "", $httpUser); + $this->assertMessage($exp, $act); + } + + public function provideAuthenticationRequests() { + return [ + [false, true, null, ['api_key' => "validToken"], ['api' => null], true], + ]; + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index aac033b..9200dd8 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -104,6 +104,10 @@ cases/REST/TinyTinyRSS/TestIcon.php cases/REST/TinyTinyRSS/PDO/TestAPI.php + + cases/REST/Fever/TestAPI.php + cases/REST/Fever/PDO/TestAPI.php + cases/Service/TestService.php cases/CLI/TestCLI.php