]>
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; |
cf92b4dd | 6 | use Shaarli\Bookmark\BookmarkFileService; |
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); | |
f211e417 | 68 | } catch (ApiException $e) { |
18e67967 A |
69 | $e->setResponse($response); |
70 | $e->setDebug($this->conf->get('dev.debug', false)); | |
71 | $response = $e->getApiResponse(); | |
72 | } | |
73 | ||
1a8ac737 A |
74 | return $response |
75 | ->withHeader('Access-Control-Allow-Origin', '*') | |
76 | ->withHeader( | |
77 | 'Access-Control-Allow-Headers', | |
78 | 'X-Requested-With, Content-Type, Accept, Origin, Authorization' | |
79 | ) | |
80 | ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') | |
81 | ; | |
18e67967 A |
82 | } |
83 | ||
84 | /** | |
85 | * Check the request validity (HTTP method, request value, etc.), | |
86 | * that the API is enabled, and the JWT token validity. | |
87 | * | |
88 | * @param Request $request Slim request | |
89 | * | |
90 | * @throws ApiAuthorizationException The API is disabled or the token is invalid. | |
91 | */ | |
92 | protected function checkRequest($request) | |
93 | { | |
94 | if (! $this->conf->get('api.enabled', true)) { | |
95 | throw new ApiAuthorizationException('API is disabled'); | |
96 | } | |
97 | $this->checkToken($request); | |
98 | } | |
99 | ||
100 | /** | |
101 | * Check that the JWT token is set and valid. | |
102 | * The API secret setting must be set. | |
103 | * | |
104 | * @param Request $request Slim request | |
105 | * | |
106 | * @throws ApiAuthorizationException The token couldn't be validated. | |
107 | */ | |
f211e417 V |
108 | protected function checkToken($request) |
109 | { | |
63ef5497 | 110 | if (! $request->hasHeader('Authorization')) { |
18e67967 A |
111 | throw new ApiAuthorizationException('JWT token not provided'); |
112 | } | |
113 | ||
114 | if (empty($this->conf->get('api.secret'))) { | |
115 | throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration'); | |
116 | } | |
117 | ||
63ef5497 V |
118 | $authorization = $request->getHeaderLine('Authorization'); |
119 | ||
120 | if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) { | |
121 | throw new ApiAuthorizationException('Invalid JWT header'); | |
122 | } | |
123 | ||
124 | ApiUtils::validateJwtToken($matches[1], $this->conf->get('api.secret')); | |
18e67967 A |
125 | } |
126 | ||
127 | /** | |
cf92b4dd | 128 | * Instantiate a new LinkDB including private bookmarks, |
18e67967 A |
129 | * and load in the Slim container. |
130 | * | |
131 | * FIXME! LinkDB could use a refactoring to avoid this trick. | |
132 | * | |
813849e5 | 133 | * @param ConfigManager $conf instance. |
18e67967 A |
134 | */ |
135 | protected function setLinkDb($conf) | |
136 | { | |
cf92b4dd A |
137 | $linkDb = new BookmarkFileService( |
138 | $conf, | |
139 | $this->container->get('history'), | |
140 | true | |
18e67967 A |
141 | ); |
142 | $this->container['db'] = $linkDb; | |
143 | } | |
144 | } |