11 class ApiUtilsTest
extends \PHPUnit_Framework_TestCase
14 * Force the timezone for ISO datetimes.
16 public static function setUpBeforeClass()
18 date_default_timezone_set('UTC');
22 * Generate a valid JWT token.
24 * @param string $secret API secret used to generate the signature.
26 * @return string Generated token.
28 public static function generateValidJwtToken($secret)
30 $header = Base64Url
::encode('{
34 $payload = Base64Url
::encode('{
37 $signature = Base64Url
::encode(hash_hmac('sha512', $header .'.'. $payload , $secret, true));
38 return $header .'.'. $payload .'.'. $signature;
42 * Generate a JWT token from given header and payload.
44 * @param string $header Header in JSON format.
45 * @param string $payload Payload in JSON format.
46 * @param string $secret API secret used to hash the signature.
48 * @return string JWT token.
50 public static function generateCustomJwtToken($header, $payload, $secret)
52 $header = Base64Url
::encode($header);
53 $payload = Base64Url
::encode($payload);
54 $signature = Base64Url
::encode(hash_hmac('sha512', $header . '.' . $payload, $secret, true));
55 return $header . '.' . $payload . '.' . $signature;
59 * Test validateJwtToken() with a valid JWT token.
61 public function testValidateJwtTokenValid()
63 $secret = 'WarIsPeace';
64 ApiUtils
::validateJwtToken(self
::generateValidJwtToken($secret), $secret);
68 * Test validateJwtToken() with a malformed JWT token.
70 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
71 * @expectedExceptionMessage Malformed JWT token
73 public function testValidateJwtTokenMalformed()
76 ApiUtils
::validateJwtToken($token, 'foo');
80 * Test validateJwtToken() with an empty JWT token.
82 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
83 * @expectedExceptionMessage Malformed JWT token
85 public function testValidateJwtTokenMalformedEmpty()
88 ApiUtils
::validateJwtToken($token, 'foo');
92 * Test validateJwtToken() with a JWT token without header.
94 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
95 * @expectedExceptionMessage Malformed JWT token
97 public function testValidateJwtTokenMalformedEmptyHeader()
99 $token = '.payload.signature';
100 ApiUtils
::validateJwtToken($token, 'foo');
104 * Test validateJwtToken() with a JWT token without payload
106 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
107 * @expectedExceptionMessage Malformed JWT token
109 public function testValidateJwtTokenMalformedEmptyPayload()
111 $token = 'header..signature';
112 ApiUtils
::validateJwtToken($token, 'foo');
116 * Test validateJwtToken() with a JWT token with an empty signature.
118 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
119 * @expectedExceptionMessage Invalid JWT signature
121 public function testValidateJwtTokenInvalidSignatureEmpty()
123 $token = 'header.payload.';
124 ApiUtils
::validateJwtToken($token, 'foo');
128 * Test validateJwtToken() with a JWT token with an invalid signature.
130 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
131 * @expectedExceptionMessage Invalid JWT signature
133 public function testValidateJwtTokenInvalidSignature()
135 $token = 'header.payload.nope';
136 ApiUtils
::validateJwtToken($token, 'foo');
140 * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret.
142 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
143 * @expectedExceptionMessage Invalid JWT signature
145 public function testValidateJwtTokenInvalidSignatureSecret()
147 ApiUtils
::validateJwtToken(self
::generateValidJwtToken('foo'), 'bar');
151 * Test validateJwtToken() with a JWT token with a an invalid header (not JSON).
153 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
154 * @expectedExceptionMessage Invalid JWT header
156 public function testValidateJwtTokenInvalidHeader()
158 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret');
159 ApiUtils
::validateJwtToken($token, 'secret');
163 * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON).
165 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
166 * @expectedExceptionMessage Invalid JWT payload
168 public function testValidateJwtTokenInvalidPayload()
170 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret');
171 ApiUtils
::validateJwtToken($token, 'secret');
175 * Test validateJwtToken() with a JWT token without issued time.
177 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
178 * @expectedExceptionMessage Invalid JWT issued time
180 public function testValidateJwtTokenInvalidTimeEmpty()
182 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret');
183 ApiUtils
::validateJwtToken($token, 'secret');
187 * Test validateJwtToken() with an expired JWT token.
189 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
190 * @expectedExceptionMessage Invalid JWT issued time
192 public function testValidateJwtTokenInvalidTimeExpired()
194 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret');
195 ApiUtils
::validateJwtToken($token, 'secret');
199 * Test validateJwtToken() with a JWT token issued in the future.
201 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
202 * @expectedExceptionMessage Invalid JWT issued time
204 public function testValidateJwtTokenInvalidTimeFuture()
206 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret');
207 ApiUtils
::validateJwtToken($token, 'secret');
211 * Test formatLink() with a link using all useful fields.
213 public function testFormatLinkComplete()
215 $indexUrl = 'https://domain.tld/sub/';
218 'url' => 'http://lol.lol',
220 'title' => 'Important Title',
221 'description' => 'It is very lol<tag>' . PHP_EOL
. 'new line',
222 'tags' => 'blip .blop ',
224 'created' => \DateTime
::createFromFormat('Ymd_His', '20170107_160102'),
225 'updated' => \DateTime
::createFromFormat('Ymd_His', '20170107_160612'),
230 'url' => 'http://lol.lol',
232 'title' => 'Important Title',
233 'description' => 'It is very lol<tag>' . PHP_EOL
. 'new line',
234 'tags' => ['blip', '.blop'],
236 'created' => '2017-01-07T16:01:02+00:00',
237 'updated' => '2017-01-07T16:06:12+00:00',
240 $this->assertEquals($expected, ApiUtils
::formatLink($link, $indexUrl));
244 * Test formatLink() with only minimal fields filled, and internal link.
246 public function testFormatLinkMinimalNote()
248 $indexUrl = 'https://domain.tld/sub/';
257 'created' => \DateTime
::createFromFormat('Ymd_His', '20170107_160102'),
262 'url' => 'https://domain.tld/sub/?abc',
268 'created' => '2017-01-07T16:01:02+00:00',
272 $this->assertEquals($expected, ApiUtils
::formatLink($link, $indexUrl));