5 use malkusch\lock\mutex\FlockMutex
;
6 use Shaarli\Api\Exceptions\ApiAuthorizationException
;
7 use Shaarli\Api\Exceptions\ApiException
;
8 use Shaarli\Bookmark\BookmarkFileService
;
9 use Shaarli\Config\ConfigManager
;
11 use Slim\Http\Request
;
12 use Slim\Http\Response
;
17 * This will be called before accessing any API Controller.
18 * Its role is to make sure that the API is enabled, configured, and to validate the JWT token.
20 * If the request is validated, the controller is called, otherwise a JSON error response is returned.
27 * @var int JWT token validity in seconds (9 min).
29 public static $TOKEN_DURATION = 540;
32 * @var Container: contains conf, plugins, etc.
37 * @var ConfigManager instance.
42 * ApiMiddleware constructor.
44 * @param Container $container instance.
46 public function __construct($container)
48 $this->container
= $container;
49 $this->conf
= $this->container
->get('conf');
50 $this->setLinkDb($this->conf
);
54 * Middleware execution:
55 * - check the API request
56 * - execute the controller
57 * - return the response
59 * @param Request $request Slim request
60 * @param Response $response Slim response
61 * @param callable $next Next action
63 * @return Response response.
65 public function __invoke($request, $response, $next)
68 $this->checkRequest($request);
69 $response = $next($request, $response);
70 } catch (ApiException
$e) {
71 $e->setResponse($response);
72 $e->setDebug($this->conf
->get('dev.debug', false));
73 $response = $e->getApiResponse();
77 ->withHeader('Access-Control-Allow-Origin', '*')
79 'Access-Control-Allow-Headers',
80 'X-Requested-With, Content-Type, Accept, Origin, Authorization'
82 ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
87 * Check the request validity (HTTP method, request value, etc.),
88 * that the API is enabled, and the JWT token validity.
90 * @param Request $request Slim request
92 * @throws ApiAuthorizationException The API is disabled or the token is invalid.
94 protected function checkRequest($request)
96 if (! $this->conf
->get('api.enabled', true)) {
97 throw new ApiAuthorizationException('API is disabled');
99 $this->checkToken($request);
103 * Check that the JWT token is set and valid.
104 * The API secret setting must be set.
106 * @param Request $request Slim request
108 * @throws ApiAuthorizationException The token couldn't be validated.
110 protected function checkToken($request)
113 !$request->hasHeader('Authorization')
114 && !isset($this->container
->environment
['REDIRECT_HTTP_AUTHORIZATION'])
116 throw new ApiAuthorizationException('JWT token not provided');
119 if (empty($this->conf
->get('api.secret'))) {
120 throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
123 if (isset($this->container
->environment
['REDIRECT_HTTP_AUTHORIZATION'])) {
124 $authorization = $this->container
->environment
['REDIRECT_HTTP_AUTHORIZATION'];
126 $authorization = $request->getHeaderLine('Authorization');
129 if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) {
130 throw new ApiAuthorizationException('Invalid JWT header');
133 ApiUtils
::validateJwtToken($matches[1], $this->conf
->get('api.secret'));
137 * Instantiate a new LinkDB including private bookmarks,
138 * and load in the Slim container.
140 * FIXME! LinkDB could use a refactoring to avoid this trick.
142 * @param ConfigManager $conf instance.
144 protected function setLinkDb($conf)
146 $linkDb = new BookmarkFileService(
148 $this->container
->get('history'),
149 new FlockMutex(fopen(SHAARLI_MUTEX_FILE
, 'r'), 2),
152 $this->container
['db'] = $linkDb;