ApiMiddleware.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <?php
  2. namespace Shaarli\Api;
  3. use Shaarli\Api\Exceptions\ApiException;
  4. use Shaarli\Api\Exceptions\ApiAuthorizationException;
  5. use Slim\Container;
  6. use Slim\Http\Request;
  7. use Slim\Http\Response;
  8. /**
  9. * Class ApiMiddleware
  10. *
  11. * This will be called before accessing any API Controller.
  12. * Its role is to make sure that the API is enabled, configured, and to validate the JWT token.
  13. *
  14. * If the request is validated, the controller is called, otherwise a JSON error response is returned.
  15. *
  16. * @package Api
  17. */
  18. class ApiMiddleware
  19. {
  20. /**
  21. * @var int JWT token validity in seconds (9 min).
  22. */
  23. public static $TOKEN_DURATION = 540;
  24. /**
  25. * @var Container: contains conf, plugins, etc.
  26. */
  27. protected $container;
  28. /**
  29. * @var \ConfigManager instance.
  30. */
  31. protected $conf;
  32. /**
  33. * ApiMiddleware constructor.
  34. *
  35. * @param Container $container instance.
  36. */
  37. public function __construct($container)
  38. {
  39. $this->container = $container;
  40. $this->conf = $this->container->get('conf');
  41. $this->setLinkDb($this->conf);
  42. }
  43. /**
  44. * Middleware execution:
  45. * - check the API request
  46. * - execute the controller
  47. * - return the response
  48. *
  49. * @param Request $request Slim request
  50. * @param Response $response Slim response
  51. * @param callable $next Next action
  52. *
  53. * @return Response response.
  54. */
  55. public function __invoke($request, $response, $next)
  56. {
  57. try {
  58. $this->checkRequest($request);
  59. $response = $next($request, $response);
  60. } catch(ApiException $e) {
  61. $e->setResponse($response);
  62. $e->setDebug($this->conf->get('dev.debug', false));
  63. $response = $e->getApiResponse();
  64. }
  65. return $response;
  66. }
  67. /**
  68. * Check the request validity (HTTP method, request value, etc.),
  69. * that the API is enabled, and the JWT token validity.
  70. *
  71. * @param Request $request Slim request
  72. *
  73. * @throws ApiAuthorizationException The API is disabled or the token is invalid.
  74. */
  75. protected function checkRequest($request)
  76. {
  77. if (! $this->conf->get('api.enabled', true)) {
  78. throw new ApiAuthorizationException('API is disabled');
  79. }
  80. $this->checkToken($request);
  81. }
  82. /**
  83. * Check that the JWT token is set and valid.
  84. * The API secret setting must be set.
  85. *
  86. * @param Request $request Slim request
  87. *
  88. * @throws ApiAuthorizationException The token couldn't be validated.
  89. */
  90. protected function checkToken($request) {
  91. $jwt = $request->getHeaderLine('jwt');
  92. if (empty($jwt)) {
  93. throw new ApiAuthorizationException('JWT token not provided');
  94. }
  95. if (empty($this->conf->get('api.secret'))) {
  96. throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
  97. }
  98. ApiUtils::validateJwtToken($jwt, $this->conf->get('api.secret'));
  99. }
  100. /**
  101. * Instantiate a new LinkDB including private links,
  102. * and load in the Slim container.
  103. *
  104. * FIXME! LinkDB could use a refactoring to avoid this trick.
  105. *
  106. * @param \ConfigManager $conf instance.
  107. */
  108. protected function setLinkDb($conf)
  109. {
  110. $linkDb = new \LinkDB(
  111. $conf->get('resource.datastore'),
  112. true,
  113. $conf->get('privacy.hide_public_links'),
  114. $conf->get('redirector.url'),
  115. $conf->get('redirector.encode_url')
  116. );
  117. $this->container['db'] = $linkDb;
  118. }
  119. }