From 18e6796726d73d7dc90ecdd16c181493941f5487 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 15 Dec 2016 10:13:00 +0100 Subject: REST API structure using Slim framework * REST API routes are handle by Slim. * Every API controller go through ApiMiddleware which handles security. * First service implemented `/info`, for tests purpose. --- tests/api/ApiUtilsTest.php | 206 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 tests/api/ApiUtilsTest.php (limited to 'tests/api/ApiUtilsTest.php') diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php new file mode 100644 index 00000000..10da1459 --- /dev/null +++ b/tests/api/ApiUtilsTest.php @@ -0,0 +1,206 @@ +generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret'); + ApiUtils::validateJwtToken($token, 'secret'); + } + + /** + * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON). + * + * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException + * @expectedExceptionMessage Invalid JWT payload + */ + public function testValidateJwtTokenInvalidPayload() + { + $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret'); + ApiUtils::validateJwtToken($token, 'secret'); + } + + /** + * Test validateJwtToken() with a JWT token without issued time. + * + * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException + * @expectedExceptionMessage Invalid JWT issued time + */ + public function testValidateJwtTokenInvalidTimeEmpty() + { + $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret'); + ApiUtils::validateJwtToken($token, 'secret'); + } + + /** + * Test validateJwtToken() with an expired JWT token. + * + * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException + * @expectedExceptionMessage Invalid JWT issued time + */ + public function testValidateJwtTokenInvalidTimeExpired() + { + $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret'); + ApiUtils::validateJwtToken($token, 'secret'); + } + + /** + * Test validateJwtToken() with a JWT token issued in the future. + * + * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException + * @expectedExceptionMessage Invalid JWT issued time + */ + public function testValidateJwtTokenInvalidTimeFuture() + { + $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret'); + ApiUtils::validateJwtToken($token, 'secret'); + } +} -- cgit v1.2.3 From 7a9daac56dc64ec1ddb12adece3e1a8f71778cc7 Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Wed, 4 Jan 2017 11:41:05 +0100 Subject: API: fix JWT signature verification Fixes https://github.com/shaarli/Shaarli/issues/737 Added: - Base64Url utilities Fixed: - use URL-safe Base64 encoding/decoding functions - use byte representations for HMAC digests - all JWT parts are Base64Url-encoded See: - https://en.wikipedia.org/wiki/JSON_Web_Token - https://tools.ietf.org/html/rfc7519 - https://scotch.io/tutorials/the-anatomy-of-a-json-web-token - https://jwt.io/introduction/ - https://en.wikipedia.org/wiki/Base64#URL_applications - https://secure.php.net/manual/en/function.base64-encode.php#103849 Signed-off-by: VirtualTam --- tests/api/ApiUtilsTest.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'tests/api/ApiUtilsTest.php') diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php index 10da1459..4b2fa3b2 100644 --- a/tests/api/ApiUtilsTest.php +++ b/tests/api/ApiUtilsTest.php @@ -2,6 +2,9 @@ namespace Shaarli\Api; +use Shaarli\Base64Url; + + /** * Class ApiUtilsTest */ @@ -24,14 +27,14 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase */ public static function generateValidJwtToken($secret) { - $header = base64_encode('{ + $header = Base64Url::encode('{ "typ": "JWT", "alg": "HS512" }'); - $payload = base64_encode('{ + $payload = Base64Url::encode('{ "iat": '. time() .' }'); - $signature = hash_hmac('sha512', $header .'.'. $payload , $secret); + $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload , $secret, true)); return $header .'.'. $payload .'.'. $signature; } @@ -46,9 +49,9 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase */ public static function generateCustomJwtToken($header, $payload, $secret) { - $header = base64_encode($header); - $payload = base64_encode($payload); - $signature = hash_hmac('sha512', $header . '.' . $payload, $secret); + $header = Base64Url::encode($header); + $payload = Base64Url::encode($payload); + $signature = Base64Url::encode(hash_hmac('sha512', $header . '.' . $payload, $secret, true)); return $header . '.' . $payload . '.' . $signature; } -- cgit v1.2.3 From c3b00963fe22479e87998c82bc83827a54c8d972 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 22 Dec 2016 14:36:45 +0100 Subject: REST API: implement getLinks service See http://shaarli.github.io/api-documentation/#links-links-collection-get --- tests/api/ApiUtilsTest.php | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'tests/api/ApiUtilsTest.php') diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php index 10da1459..516ee686 100644 --- a/tests/api/ApiUtilsTest.php +++ b/tests/api/ApiUtilsTest.php @@ -203,4 +203,69 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret'); ApiUtils::validateJwtToken($token, 'secret'); } + + /** + * Test formatLink() with a link using all useful fields. + */ + public function testFormatLinkComplete() + { + $indexUrl = 'https://domain.tld/sub/'; + $link = [ + 'id' => 12, + 'url' => 'http://lol.lol', + 'shorturl' => 'abc', + 'title' => 'Important Title', + 'description' => 'It is very lol' . PHP_EOL . 'new line', + 'tags' => 'blip .blop ', + 'private' => '1', + 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), + 'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'), + ]; + + $expected = [ + 'id' => 12, + 'url' => 'http://lol.lol', + 'shorturl' => 'abc', + 'title' => 'Important Title', + 'description' => 'It is very lol' . PHP_EOL . 'new line', + 'tags' => ['blip', '.blop'], + 'private' => true, + 'created' => '2017-01-07T16:01:02+00:00', + 'updated' => '2017-01-07T16:06:12+00:00', + ]; + + $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl)); + } + + /** + * Test formatLink() with only minimal fields filled, and internal link. + */ + public function testFormatLinkMinimalNote() + { + $indexUrl = 'https://domain.tld/sub/'; + $link = [ + 'id' => 12, + 'url' => '?abc', + 'shorturl' => 'abc', + 'title' => 'Note', + 'description' => '', + 'tags' => '', + 'private' => '', + 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), + ]; + + $expected = [ + 'id' => 12, + 'url' => 'https://domain.tld/sub/?abc', + 'shorturl' => 'abc', + 'title' => 'Note', + 'description' => '', + 'tags' => [], + 'private' => false, + 'created' => '2017-01-07T16:01:02+00:00', + 'updated' => '', + ]; + + $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl)); + } } -- cgit v1.2.3 From cf9181dddf8b6113b1b017e4bcb21fac0a0b1c83 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 1 Apr 2017 11:11:25 +0200 Subject: REST API: implement PUT method * Related to #609 * Documentation: http://shaarli.github.io/api-documentation/#links-link-put --- tests/api/ApiUtilsTest.php | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'tests/api/ApiUtilsTest.php') diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php index b4431d1b..62baf4c5 100644 --- a/tests/api/ApiUtilsTest.php +++ b/tests/api/ApiUtilsTest.php @@ -271,4 +271,82 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl)); } + + /** + * Test updateLink with valid data, and also unnecessary fields. + */ + public function testUpdateLink() + { + $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); + $old = [ + 'id' => 12, + 'url' => '?abc', + 'shorturl' => 'abc', + 'title' => 'Note', + 'description' => '', + 'tags' => '', + 'private' => '', + 'created' => $created, + ]; + + $new = [ + 'id' => 13, + 'shorturl' => 'nope', + 'url' => 'http://somewhere.else', + 'title' => 'Le Cid', + 'description' => 'Percé jusques au fond du cœur [...]', + 'tags' => 'corneille rodrigue', + 'private' => true, + 'created' => 'creation', + 'updated' => 'updation', + ]; + + $result = ApiUtils::updateLink($old, $new); + $this->assertEquals(12, $result['id']); + $this->assertEquals('http://somewhere.else', $result['url']); + $this->assertEquals('abc', $result['shorturl']); + $this->assertEquals('Le Cid', $result['title']); + $this->assertEquals('Percé jusques au fond du cœur [...]', $result['description']); + $this->assertEquals('corneille rodrigue', $result['tags']); + $this->assertEquals(true, $result['private']); + $this->assertEquals($created, $result['created']); + $this->assertTrue(new \DateTime('5 seconds ago') < $result['updated']); + } + + /** + * Test updateLink with minimal data. + */ + public function testUpdateLinkMinimal() + { + $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); + $old = [ + 'id' => 12, + 'url' => '?abc', + 'shorturl' => 'abc', + 'title' => 'Note', + 'description' => 'Interesting description!', + 'tags' => 'doggo', + 'private' => true, + 'created' => $created, + ]; + + $new = [ + 'url' => '', + 'title' => '', + 'description' => '', + 'tags' => '', + 'private' => false, + ]; + + $result = ApiUtils::updateLink($old, $new); + $this->assertEquals(12, $result['id']); + $this->assertEquals('?abc', $result['url']); + $this->assertEquals('abc', $result['shorturl']); + $this->assertEquals('?abc', $result['title']); + $this->assertEquals('', $result['description']); + $this->assertEquals('', $result['tags']); + $this->assertEquals(false, $result['private']); + $this->assertEquals($created, $result['created']); + $this->assertTrue(new \DateTime('5 seconds ago') < $result['updated']); + } } -- cgit v1.2.3