4 use Shaarli\Config\ConfigManager
;
6 use Shaarli\Plugin\PluginManager
;
8 use Slim\Http\Environment
;
10 use Slim\Http\Response
;
13 * Class ApiMiddlewareTest
15 * Test the REST API Slim Middleware.
17 * Note that we can't test a valid use case here, because the middleware
18 * needs to call a valid controller/action during its execution.
22 class ApiMiddlewareTest
extends \Shaarli\TestCase
25 * @var string datastore to test write operations
27 protected static $testDatastore = 'sandbox/datastore.php';
30 * @var ConfigManager instance
35 * @var \ReferenceLinkDB instance.
37 protected $refDB = null;
40 * @var Container instance.
45 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
47 protected function setUp(): void
49 $this->conf
= new ConfigManager('tests/utils/config/configJson');
50 $this->conf
->set('api.secret', 'NapoleonWasALizard');
52 $this->refDB
= new \
ReferenceLinkDB();
53 $this->refDB
->write(self
::$testDatastore);
55 $history = new History('sandbox/history.php');
57 $this->container
= new Container();
58 $this->container
['conf'] = $this->conf
;
59 $this->container
['history'] = $history;
60 $this->container
['pluginManager'] = new PluginManager($this->conf
);
64 * After every test, remove the test datastore.
66 protected function tearDown(): void
68 @unlink(self
::$testDatastore);
72 * Invoke the middleware with a valid token
74 public function testInvokeMiddlewareWithValidToken(): void
76 $next = function (Request
$request, Response
$response): Response
{
79 $mw = new ApiMiddleware($this->container
);
80 $env = Environment
::mock([
81 'REQUEST_METHOD' => 'GET',
82 'REQUEST_URI' => '/echo',
83 'HTTP_AUTHORIZATION'=> 'Bearer ' . ApiUtilsTest
::generateValidJwtToken('NapoleonWasALizard'),
85 $request = Request
::createFromEnvironment($env);
86 $response = new Response();
87 /** @var Response $response */
88 $response = $mw($request, $response, $next);
90 $this->assertEquals(200, $response->getStatusCode());
94 * Invoke the middleware with a valid token
95 * Using specific Apache CGI redirected authorization.
97 public function testInvokeMiddlewareWithValidTokenFromRedirectedHeader(): void
99 $next = function (Request
$request, Response
$response): Response
{
103 $token = 'Bearer ' . ApiUtilsTest
::generateValidJwtToken('NapoleonWasALizard');
104 $this->container
->environment
['REDIRECT_HTTP_AUTHORIZATION'] = $token;
105 $mw = new ApiMiddleware($this->container
);
106 $env = Environment
::mock([
107 'REQUEST_METHOD' => 'GET',
108 'REQUEST_URI' => '/echo',
110 $request = Request
::createFromEnvironment($env);
111 $response = new Response();
112 /** @var Response $response */
113 $response = $mw($request, $response, $next);
115 $this->assertEquals(200, $response->getStatusCode());
119 * Invoke the middleware with the API disabled:
120 * should return a 401 error Unauthorized.
122 public function testInvokeMiddlewareApiDisabled()
124 $this->conf
->set('api.enabled', false);
125 $mw = new ApiMiddleware($this->container
);
126 $env = Environment
::mock([
127 'REQUEST_METHOD' => 'GET',
128 'REQUEST_URI' => '/echo',
130 $request = Request
::createFromEnvironment($env);
131 $response = new Response();
132 /** @var Response $response */
133 $response = $mw($request, $response, null);
135 $this->assertEquals(401, $response->getStatusCode());
136 $body = json_decode((string) $response->getBody());
137 $this->assertEquals('Not authorized', $body);
141 * Invoke the middleware with the API disabled in debug mode:
142 * should return a 401 error Unauthorized - with a specific message and a stacktrace.
144 public function testInvokeMiddlewareApiDisabledDebug()
146 $this->conf
->set('api.enabled', false);
147 $this->conf
->set('dev.debug', true);
148 $mw = new ApiMiddleware($this->container
);
149 $env = Environment
::mock([
150 'REQUEST_METHOD' => 'GET',
151 'REQUEST_URI' => '/echo',
153 $request = Request
::createFromEnvironment($env);
154 $response = new Response();
155 /** @var Response $response */
156 $response = $mw($request, $response, null);
158 $this->assertEquals(401, $response->getStatusCode());
159 $body = json_decode((string) $response->getBody());
160 $this->assertEquals('Not authorized: API is disabled', $body->message
);
161 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace
);
165 * Invoke the middleware without a token (debug):
166 * should return a 401 error Unauthorized - with a specific message and a stacktrace.
168 public function testInvokeMiddlewareNoTokenProvidedDebug()
170 $this->conf
->set('dev.debug', true);
171 $mw = new ApiMiddleware($this->container
);
172 $env = Environment
::mock([
173 'REQUEST_METHOD' => 'GET',
174 'REQUEST_URI' => '/echo',
176 $request = Request
::createFromEnvironment($env);
177 $response = new Response();
178 /** @var Response $response */
179 $response = $mw($request, $response, null);
181 $this->assertEquals(401, $response->getStatusCode());
182 $body = json_decode((string) $response->getBody());
183 $this->assertEquals('Not authorized: JWT token not provided', $body->message
);
184 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace
);
188 * Invoke the middleware without a secret set in settings (debug):
189 * should return a 401 error Unauthorized - with a specific message and a stacktrace.
191 public function testInvokeMiddlewareNoSecretSetDebug()
193 $this->conf
->set('dev.debug', true);
194 $this->conf
->set('api.secret', '');
195 $mw = new ApiMiddleware($this->container
);
196 $env = Environment
::mock([
197 'REQUEST_METHOD' => 'GET',
198 'REQUEST_URI' => '/echo',
199 'HTTP_AUTHORIZATION'=> 'Bearer jwt',
201 $request = Request
::createFromEnvironment($env);
202 $response = new Response();
203 /** @var Response $response */
204 $response = $mw($request, $response, null);
206 $this->assertEquals(401, $response->getStatusCode());
207 $body = json_decode((string) $response->getBody());
208 $this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message
);
209 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace
);
213 * Invoke the middleware with an invalid JWT token header
215 public function testInvalidJwtAuthHeaderDebug()
217 $this->conf
->set('dev.debug', true);
218 $mw = new ApiMiddleware($this->container
);
219 $env = Environment
::mock([
220 'REQUEST_METHOD' => 'GET',
221 'REQUEST_URI' => '/echo',
222 'HTTP_AUTHORIZATION'=> 'PolarBearer jwt',
224 $request = Request
::createFromEnvironment($env);
225 $response = new Response();
226 /** @var Response $response */
227 $response = $mw($request, $response, null);
229 $this->assertEquals(401, $response->getStatusCode());
230 $body = json_decode((string) $response->getBody());
231 $this->assertEquals('Not authorized: Invalid JWT header', $body->message
);
232 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace
);
236 * Invoke the middleware with an invalid JWT token (debug):
237 * should return a 401 error Unauthorized - with a specific message and a stacktrace.
239 * Note: specific JWT errors tests are handled in ApiUtilsTest.
241 public function testInvokeMiddlewareInvalidJwtDebug()
243 $this->conf
->set('dev.debug', true);
244 $mw = new ApiMiddleware($this->container
);
245 $env = Environment
::mock([
246 'REQUEST_METHOD' => 'GET',
247 'REQUEST_URI' => '/echo',
248 'HTTP_AUTHORIZATION'=> 'Bearer jwt',
250 $request = Request
::createFromEnvironment($env);
251 $response = new Response();
252 /** @var Response $response */
253 $response = $mw($request, $response, null);
255 $this->assertEquals(401, $response->getStatusCode());
256 $body = json_decode((string) $response->getBody());
257 $this->assertEquals('Not authorized: Malformed JWT token', $body->message
);
258 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace
);