]>
Commit | Line | Data |
---|---|---|
18e67967 | 1 | <?php |
18e67967 A |
2 | namespace Shaarli\Api; |
3 | ||
4 | use Shaarli\Api\Exceptions\ApiException; | |
5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | |
3c66e564 | 6 | |
813849e5 | 7 | use Shaarli\Config\ConfigManager; |
18e67967 A |
8 | use Slim\Container; |
9 | use Slim\Http\Request; | |
10 | use 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 | */ | |
22 | class 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 | /** | |
813849e5 | 35 | * @var ConfigManager instance. |
18e67967 A |
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) { | |
63ef5497 | 102 | if (! $request->hasHeader('Authorization')) { |
18e67967 A |
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 | ||
63ef5497 V |
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')); | |
18e67967 A |
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 | * | |
813849e5 | 125 | * @param ConfigManager $conf instance. |
18e67967 A |
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 | } |