]>
Commit | Line | Data |
---|---|---|
18e67967 | 1 | <?php |
18e67967 A |
2 | namespace Shaarli\Api; |
3 | ||
18e67967 | 4 | use Shaarli\Api\Exceptions\ApiAuthorizationException; |
dea72c71 | 5 | use Shaarli\Api\Exceptions\ApiException; |
813849e5 | 6 | use Shaarli\Config\ConfigManager; |
18e67967 A |
7 | use Slim\Container; |
8 | use Slim\Http\Request; | |
9 | use Slim\Http\Response; | |
10 | ||
11 | /** | |
12 | * Class ApiMiddleware | |
13 | * | |
14 | * This will be called before accessing any API Controller. | |
15 | * Its role is to make sure that the API is enabled, configured, and to validate the JWT token. | |
16 | * | |
17 | * If the request is validated, the controller is called, otherwise a JSON error response is returned. | |
18 | * | |
19 | * @package Api | |
20 | */ | |
21 | class ApiMiddleware | |
22 | { | |
23 | /** | |
24 | * @var int JWT token validity in seconds (9 min). | |
25 | */ | |
26 | public static $TOKEN_DURATION = 540; | |
27 | ||
28 | /** | |
29 | * @var Container: contains conf, plugins, etc. | |
30 | */ | |
31 | protected $container; | |
32 | ||
33 | /** | |
813849e5 | 34 | * @var ConfigManager instance. |
18e67967 A |
35 | */ |
36 | protected $conf; | |
37 | ||
38 | /** | |
39 | * ApiMiddleware constructor. | |
40 | * | |
41 | * @param Container $container instance. | |
42 | */ | |
43 | public function __construct($container) | |
44 | { | |
45 | $this->container = $container; | |
46 | $this->conf = $this->container->get('conf'); | |
47 | $this->setLinkDb($this->conf); | |
48 | } | |
49 | ||
50 | /** | |
51 | * Middleware execution: | |
52 | * - check the API request | |
53 | * - execute the controller | |
54 | * - return the response | |
55 | * | |
56 | * @param Request $request Slim request | |
57 | * @param Response $response Slim response | |
58 | * @param callable $next Next action | |
59 | * | |
60 | * @return Response response. | |
61 | */ | |
62 | public function __invoke($request, $response, $next) | |
63 | { | |
64 | try { | |
65 | $this->checkRequest($request); | |
66 | $response = $next($request, $response); | |
f211e417 | 67 | } catch (ApiException $e) { |
18e67967 A |
68 | $e->setResponse($response); |
69 | $e->setDebug($this->conf->get('dev.debug', false)); | |
70 | $response = $e->getApiResponse(); | |
71 | } | |
72 | ||
73 | return $response; | |
74 | } | |
75 | ||
76 | /** | |
77 | * Check the request validity (HTTP method, request value, etc.), | |
78 | * that the API is enabled, and the JWT token validity. | |
79 | * | |
80 | * @param Request $request Slim request | |
81 | * | |
82 | * @throws ApiAuthorizationException The API is disabled or the token is invalid. | |
83 | */ | |
84 | protected function checkRequest($request) | |
85 | { | |
86 | if (! $this->conf->get('api.enabled', true)) { | |
87 | throw new ApiAuthorizationException('API is disabled'); | |
88 | } | |
89 | $this->checkToken($request); | |
90 | } | |
91 | ||
92 | /** | |
93 | * Check that the JWT token is set and valid. | |
94 | * The API secret setting must be set. | |
95 | * | |
96 | * @param Request $request Slim request | |
97 | * | |
98 | * @throws ApiAuthorizationException The token couldn't be validated. | |
99 | */ | |
f211e417 V |
100 | protected function checkToken($request) |
101 | { | |
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 | { | |
f24896b2 | 129 | $linkDb = new \Shaarli\Bookmark\LinkDB( |
18e67967 A |
130 | $conf->get('resource.datastore'), |
131 | true, | |
520d2957 | 132 | $conf->get('privacy.hide_public_links') |
18e67967 A |
133 | ); |
134 | $this->container['db'] = $linkDb; | |
135 | } | |
136 | } |