aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/api/ApiMiddleware.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/api/ApiMiddleware.php')
-rw-r--r--application/api/ApiMiddleware.php138
1 files changed, 138 insertions, 0 deletions
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php
new file mode 100644
index 00000000..ff209393
--- /dev/null
+++ b/application/api/ApiMiddleware.php
@@ -0,0 +1,138 @@
1<?php
2namespace Shaarli\Api;
3
4use Shaarli\Api\Exceptions\ApiException;
5use Shaarli\Api\Exceptions\ApiAuthorizationException;
6
7use Shaarli\Config\ConfigManager;
8use Slim\Container;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * Class ApiMiddleware
14 *
15 * This will be called before accessing any API Controller.
16 * Its role is to make sure that the API is enabled, configured, and to validate the JWT token.
17 *
18 * If the request is validated, the controller is called, otherwise a JSON error response is returned.
19 *
20 * @package Api
21 */
22class ApiMiddleware
23{
24 /**
25 * @var int JWT token validity in seconds (9 min).
26 */
27 public static $TOKEN_DURATION = 540;
28
29 /**
30 * @var Container: contains conf, plugins, etc.
31 */
32 protected $container;
33
34 /**
35 * @var ConfigManager instance.
36 */
37 protected $conf;
38
39 /**
40 * ApiMiddleware constructor.
41 *
42 * @param Container $container instance.
43 */
44 public function __construct($container)
45 {
46 $this->container = $container;
47 $this->conf = $this->container->get('conf');
48 $this->setLinkDb($this->conf);
49 }
50
51 /**
52 * Middleware execution:
53 * - check the API request
54 * - execute the controller
55 * - return the response
56 *
57 * @param Request $request Slim request
58 * @param Response $response Slim response
59 * @param callable $next Next action
60 *
61 * @return Response response.
62 */
63 public function __invoke($request, $response, $next)
64 {
65 try {
66 $this->checkRequest($request);
67 $response = $next($request, $response);
68 } catch(ApiException $e) {
69 $e->setResponse($response);
70 $e->setDebug($this->conf->get('dev.debug', false));
71 $response = $e->getApiResponse();
72 }
73
74 return $response;
75 }
76
77 /**
78 * Check the request validity (HTTP method, request value, etc.),
79 * that the API is enabled, and the JWT token validity.
80 *
81 * @param Request $request Slim request
82 *
83 * @throws ApiAuthorizationException The API is disabled or the token is invalid.
84 */
85 protected function checkRequest($request)
86 {
87 if (! $this->conf->get('api.enabled', true)) {
88 throw new ApiAuthorizationException('API is disabled');
89 }
90 $this->checkToken($request);
91 }
92
93 /**
94 * Check that the JWT token is set and valid.
95 * The API secret setting must be set.
96 *
97 * @param Request $request Slim request
98 *
99 * @throws ApiAuthorizationException The token couldn't be validated.
100 */
101 protected function checkToken($request) {
102 if (! $request->hasHeader('Authorization')) {
103 throw new ApiAuthorizationException('JWT token not provided');
104 }
105
106 if (empty($this->conf->get('api.secret'))) {
107 throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
108 }
109
110 $authorization = $request->getHeaderLine('Authorization');
111
112 if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) {
113 throw new ApiAuthorizationException('Invalid JWT header');
114 }
115
116 ApiUtils::validateJwtToken($matches[1], $this->conf->get('api.secret'));
117 }
118
119 /**
120 * Instantiate a new LinkDB including private links,
121 * and load in the Slim container.
122 *
123 * FIXME! LinkDB could use a refactoring to avoid this trick.
124 *
125 * @param ConfigManager $conf instance.
126 */
127 protected function setLinkDb($conf)
128 {
129 $linkDb = new \LinkDB(
130 $conf->get('resource.datastore'),
131 true,
132 $conf->get('privacy.hide_public_links'),
133 $conf->get('redirector.url'),
134 $conf->get('redirector.encode_url')
135 );
136 $this->container['db'] = $linkDb;
137 }
138}