assertTrue(ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret)); } /** * Test validateJwtToken() with a malformed JWT token. */ public function testValidateJwtTokenMalformed() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Malformed JWT token'); $token = 'ABC.DEF'; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with an empty JWT token. */ public function testValidateJwtTokenMalformedEmpty() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Malformed JWT token'); $token = false; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with a JWT token without header. */ public function testValidateJwtTokenMalformedEmptyHeader() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Malformed JWT token'); $token = '.payload.signature'; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with a JWT token without payload */ public function testValidateJwtTokenMalformedEmptyPayload() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Malformed JWT token'); $token = 'header..signature'; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with a JWT token with an empty signature. */ public function testValidateJwtTokenInvalidSignatureEmpty() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT signature'); $token = 'header.payload.'; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with a JWT token with an invalid signature. */ public function testValidateJwtTokenInvalidSignature() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT signature'); $token = 'header.payload.nope'; ApiUtils::validateJwtToken($token, 'foo'); } /** * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret. */ public function testValidateJwtTokenInvalidSignatureSecret() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT signature'); ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar'); } /** * Test validateJwtToken() with a JWT token with a an invalid header (not JSON). */ public function testValidateJwtTokenInvalidHeader() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT header'); $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret'); ApiUtils::validateJwtToken($token, 'secret'); } /** * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON). */ public function testValidateJwtTokenInvalidPayload() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT payload'); $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret'); ApiUtils::validateJwtToken($token, 'secret'); } /** * Test validateJwtToken() with a JWT token without issued time. */ public function testValidateJwtTokenInvalidTimeEmpty() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT issued time'); $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret'); ApiUtils::validateJwtToken($token, 'secret'); } /** * Test validateJwtToken() with an expired JWT token. */ public function testValidateJwtTokenInvalidTimeExpired() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT issued time'); $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret'); ApiUtils::validateJwtToken($token, 'secret'); } /** * Test validateJwtToken() with a JWT token issued in the future. */ public function testValidateJwtTokenInvalidTimeFuture() { $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class); $this->expectExceptionMessage('Invalid JWT issued time'); $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/'; $data = [ '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'), ]; $bookmark = new Bookmark(); $bookmark->fromArray($data); $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($bookmark, $indexUrl)); } /** * Test formatLink() with only minimal fields filled, and internal link. */ public function testFormatLinkMinimalNote() { $indexUrl = 'https://domain.tld/sub/'; $data = [ 'id' => 12, 'url' => '?abc', 'shorturl' => 'abc', 'title' => 'Note', 'description' => '', 'tags' => '', 'private' => '', 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), ]; $bookmark = new Bookmark(); $bookmark->fromArray($data); $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($bookmark, $indexUrl)); } /** * Test updateLink with valid data, and also unnecessary fields. */ public function testUpdateLink() { $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); $data = [ 'id' => 12, 'url' => '?abc', 'shorturl' => 'abc', 'title' => 'Note', 'description' => '', 'tags' => '', 'private' => '', 'created' => $created, ]; $old = new Bookmark(); $old->fromArray($data); $data = [ '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', ]; $new = new Bookmark(); $new->fromArray($data); $result = ApiUtils::updateLink($old, $new); $this->assertEquals(12, $result->getId()); $this->assertEquals('http://somewhere.else', $result->getUrl()); $this->assertEquals('abc', $result->getShortUrl()); $this->assertEquals('Le Cid', $result->getTitle()); $this->assertEquals('Percé jusques au fond du cœur [...]', $result->getDescription()); $this->assertEquals('corneille rodrigue', $result->getTagsString()); $this->assertEquals(true, $result->isPrivate()); $this->assertEquals($created, $result->getCreated()); } /** * Test updateLink with minimal data. */ public function testUpdateLinkMinimal() { $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); $data = [ 'id' => 12, 'url' => '?abc', 'shorturl' => 'abc', 'title' => 'Note', 'description' => 'Interesting description!', 'tags' => 'doggo', 'private' => true, 'created' => $created, ]; $old = new Bookmark(); $old->fromArray($data); $new = new Bookmark(); $result = ApiUtils::updateLink($old, $new); $this->assertEquals(12, $result->getId()); $this->assertEquals('', $result->getUrl()); $this->assertEquals('abc', $result->getShortUrl()); $this->assertEquals('', $result->getTitle()); $this->assertEquals('', $result->getDescription()); $this->assertEquals('', $result->getTagsString()); $this->assertEquals(false, $result->isPrivate()); $this->assertEquals($created, $result->getCreated()); } }