]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/api/ApiUtilsTest.php
REST API: implement getLinks service
[github/shaarli/Shaarli.git] / tests / api / ApiUtilsTest.php
1 <?php
2
3 namespace Shaarli\Api;
4
5 /**
6 * Class ApiUtilsTest
7 */
8 class ApiUtilsTest extends \PHPUnit_Framework_TestCase
9 {
10 /**
11 * Force the timezone for ISO datetimes.
12 */
13 public static function setUpBeforeClass()
14 {
15 date_default_timezone_set('UTC');
16 }
17
18 /**
19 * Generate a valid JWT token.
20 *
21 * @param string $secret API secret used to generate the signature.
22 *
23 * @return string Generated token.
24 */
25 public static function generateValidJwtToken($secret)
26 {
27 $header = base64_encode('{
28 "typ": "JWT",
29 "alg": "HS512"
30 }');
31 $payload = base64_encode('{
32 "iat": '. time() .'
33 }');
34 $signature = hash_hmac('sha512', $header .'.'. $payload , $secret);
35 return $header .'.'. $payload .'.'. $signature;
36 }
37
38 /**
39 * Generate a JWT token from given header and payload.
40 *
41 * @param string $header Header in JSON format.
42 * @param string $payload Payload in JSON format.
43 * @param string $secret API secret used to hash the signature.
44 *
45 * @return string JWT token.
46 */
47 public static function generateCustomJwtToken($header, $payload, $secret)
48 {
49 $header = base64_encode($header);
50 $payload = base64_encode($payload);
51 $signature = hash_hmac('sha512', $header . '.' . $payload, $secret);
52 return $header . '.' . $payload . '.' . $signature;
53 }
54
55 /**
56 * Test validateJwtToken() with a valid JWT token.
57 */
58 public function testValidateJwtTokenValid()
59 {
60 $secret = 'WarIsPeace';
61 ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret);
62 }
63
64 /**
65 * Test validateJwtToken() with a malformed JWT token.
66 *
67 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
68 * @expectedExceptionMessage Malformed JWT token
69 */
70 public function testValidateJwtTokenMalformed()
71 {
72 $token = 'ABC.DEF';
73 ApiUtils::validateJwtToken($token, 'foo');
74 }
75
76 /**
77 * Test validateJwtToken() with an empty JWT token.
78 *
79 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
80 * @expectedExceptionMessage Malformed JWT token
81 */
82 public function testValidateJwtTokenMalformedEmpty()
83 {
84 $token = false;
85 ApiUtils::validateJwtToken($token, 'foo');
86 }
87
88 /**
89 * Test validateJwtToken() with a JWT token without header.
90 *
91 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
92 * @expectedExceptionMessage Malformed JWT token
93 */
94 public function testValidateJwtTokenMalformedEmptyHeader()
95 {
96 $token = '.payload.signature';
97 ApiUtils::validateJwtToken($token, 'foo');
98 }
99
100 /**
101 * Test validateJwtToken() with a JWT token without payload
102 *
103 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
104 * @expectedExceptionMessage Malformed JWT token
105 */
106 public function testValidateJwtTokenMalformedEmptyPayload()
107 {
108 $token = 'header..signature';
109 ApiUtils::validateJwtToken($token, 'foo');
110 }
111
112 /**
113 * Test validateJwtToken() with a JWT token with an empty signature.
114 *
115 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
116 * @expectedExceptionMessage Invalid JWT signature
117 */
118 public function testValidateJwtTokenInvalidSignatureEmpty()
119 {
120 $token = 'header.payload.';
121 ApiUtils::validateJwtToken($token, 'foo');
122 }
123
124 /**
125 * Test validateJwtToken() with a JWT token with an invalid signature.
126 *
127 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
128 * @expectedExceptionMessage Invalid JWT signature
129 */
130 public function testValidateJwtTokenInvalidSignature()
131 {
132 $token = 'header.payload.nope';
133 ApiUtils::validateJwtToken($token, 'foo');
134 }
135
136 /**
137 * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret.
138 *
139 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
140 * @expectedExceptionMessage Invalid JWT signature
141 */
142 public function testValidateJwtTokenInvalidSignatureSecret()
143 {
144 ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar');
145 }
146
147 /**
148 * Test validateJwtToken() with a JWT token with a an invalid header (not JSON).
149 *
150 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
151 * @expectedExceptionMessage Invalid JWT header
152 */
153 public function testValidateJwtTokenInvalidHeader()
154 {
155 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret');
156 ApiUtils::validateJwtToken($token, 'secret');
157 }
158
159 /**
160 * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON).
161 *
162 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
163 * @expectedExceptionMessage Invalid JWT payload
164 */
165 public function testValidateJwtTokenInvalidPayload()
166 {
167 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret');
168 ApiUtils::validateJwtToken($token, 'secret');
169 }
170
171 /**
172 * Test validateJwtToken() with a JWT token without issued time.
173 *
174 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
175 * @expectedExceptionMessage Invalid JWT issued time
176 */
177 public function testValidateJwtTokenInvalidTimeEmpty()
178 {
179 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret');
180 ApiUtils::validateJwtToken($token, 'secret');
181 }
182
183 /**
184 * Test validateJwtToken() with an expired JWT token.
185 *
186 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
187 * @expectedExceptionMessage Invalid JWT issued time
188 */
189 public function testValidateJwtTokenInvalidTimeExpired()
190 {
191 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret');
192 ApiUtils::validateJwtToken($token, 'secret');
193 }
194
195 /**
196 * Test validateJwtToken() with a JWT token issued in the future.
197 *
198 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
199 * @expectedExceptionMessage Invalid JWT issued time
200 */
201 public function testValidateJwtTokenInvalidTimeFuture()
202 {
203 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret');
204 ApiUtils::validateJwtToken($token, 'secret');
205 }
206
207 /**
208 * Test formatLink() with a link using all useful fields.
209 */
210 public function testFormatLinkComplete()
211 {
212 $indexUrl = 'https://domain.tld/sub/';
213 $link = [
214 'id' => 12,
215 'url' => 'http://lol.lol',
216 'shorturl' => 'abc',
217 'title' => 'Important Title',
218 'description' => 'It is very lol<tag>' . PHP_EOL . 'new line',
219 'tags' => 'blip .blop ',
220 'private' => '1',
221 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
222 'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'),
223 ];
224
225 $expected = [
226 'id' => 12,
227 'url' => 'http://lol.lol',
228 'shorturl' => 'abc',
229 'title' => 'Important Title',
230 'description' => 'It is very lol<tag>' . PHP_EOL . 'new line',
231 'tags' => ['blip', '.blop'],
232 'private' => true,
233 'created' => '2017-01-07T16:01:02+00:00',
234 'updated' => '2017-01-07T16:06:12+00:00',
235 ];
236
237 $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl));
238 }
239
240 /**
241 * Test formatLink() with only minimal fields filled, and internal link.
242 */
243 public function testFormatLinkMinimalNote()
244 {
245 $indexUrl = 'https://domain.tld/sub/';
246 $link = [
247 'id' => 12,
248 'url' => '?abc',
249 'shorturl' => 'abc',
250 'title' => 'Note',
251 'description' => '',
252 'tags' => '',
253 'private' => '',
254 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
255 ];
256
257 $expected = [
258 'id' => 12,
259 'url' => 'https://domain.tld/sub/?abc',
260 'shorturl' => 'abc',
261 'title' => 'Note',
262 'description' => '',
263 'tags' => [],
264 'private' => false,
265 'created' => '2017-01-07T16:01:02+00:00',
266 'updated' => '',
267 ];
268
269 $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl));
270 }
271 }