diff options
Diffstat (limited to 'application/api/ApiMiddleware.php')
-rw-r--r-- | application/api/ApiMiddleware.php | 138 |
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 | ||
2 | namespace Shaarli\Api; | ||
3 | |||
4 | use Shaarli\Api\Exceptions\ApiException; | ||
5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | ||
6 | |||
7 | use Shaarli\Config\ConfigManager; | ||
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 | /** | ||
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 | } | ||