PublicShareMiddleware.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\AppFramework\Middleware\PublicShare;
  7. use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException;
  8. use OCP\AppFramework\AuthPublicShareController;
  9. use OCP\AppFramework\Http\NotFoundResponse;
  10. use OCP\AppFramework\Middleware;
  11. use OCP\AppFramework\PublicShareController;
  12. use OCP\Files\NotFoundException;
  13. use OCP\IConfig;
  14. use OCP\IRequest;
  15. use OCP\ISession;
  16. use OCP\Security\Bruteforce\IThrottler;
  17. class PublicShareMiddleware extends Middleware {
  18. /** @var IRequest */
  19. private $request;
  20. /** @var ISession */
  21. private $session;
  22. /** @var IConfig */
  23. private $config;
  24. /** @var IThrottler */
  25. private $throttler;
  26. public function __construct(IRequest $request, ISession $session, IConfig $config, IThrottler $throttler) {
  27. $this->request = $request;
  28. $this->session = $session;
  29. $this->config = $config;
  30. $this->throttler = $throttler;
  31. }
  32. public function beforeController($controller, $methodName) {
  33. if (!($controller instanceof PublicShareController)) {
  34. return;
  35. }
  36. $controllerClassPath = explode('\\', get_class($controller));
  37. $controllerShortClass = end($controllerClassPath);
  38. $bruteforceProtectionAction = $controllerShortClass . '::' . $methodName;
  39. $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), $bruteforceProtectionAction);
  40. if (!$this->isLinkSharingEnabled()) {
  41. throw new NotFoundException('Link sharing is disabled');
  42. }
  43. // We require the token parameter to be set
  44. $token = $this->request->getParam('token');
  45. if ($token === null) {
  46. throw new NotFoundException();
  47. }
  48. // Set the token
  49. $controller->setToken($token);
  50. if (!$controller->isValidToken()) {
  51. $this->throttle($bruteforceProtectionAction, $token);
  52. $controller->shareNotFound();
  53. throw new NotFoundException();
  54. }
  55. // No need to check for authentication when we try to authenticate
  56. if ($methodName === 'authenticate' || $methodName === 'showAuthenticate') {
  57. return;
  58. }
  59. // If authentication succeeds just continue
  60. if ($controller->isAuthenticated()) {
  61. return;
  62. }
  63. // If we can authenticate to this controller do it else we throw a 404 to not leak any info
  64. if ($controller instanceof AuthPublicShareController) {
  65. $this->session->set('public_link_authenticate_redirect', json_encode($this->request->getParams()));
  66. throw new NeedAuthenticationException();
  67. }
  68. $this->throttle($bruteforceProtectionAction, $token);
  69. throw new NotFoundException();
  70. }
  71. public function afterException($controller, $methodName, \Exception $exception) {
  72. if (!($controller instanceof PublicShareController)) {
  73. throw $exception;
  74. }
  75. if ($exception instanceof NotFoundException) {
  76. return new NotFoundResponse();
  77. }
  78. if ($controller instanceof AuthPublicShareController && $exception instanceof NeedAuthenticationException) {
  79. return $controller->getAuthenticationRedirect($this->getFunctionForRoute($this->request->getParam('_route')));
  80. }
  81. throw $exception;
  82. }
  83. private function getFunctionForRoute(string $route): string {
  84. $tmp = explode('.', $route);
  85. return array_pop($tmp);
  86. }
  87. /**
  88. * Check if link sharing is allowed
  89. */
  90. private function isLinkSharingEnabled(): bool {
  91. // Check if the shareAPI is enabled
  92. if ($this->config->getAppValue('core', 'shareapi_enabled', 'yes') !== 'yes') {
  93. return false;
  94. }
  95. // Check whether public sharing is enabled
  96. if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
  97. return false;
  98. }
  99. return true;
  100. }
  101. private function throttle($bruteforceProtectionAction, $token): void {
  102. $ip = $this->request->getRemoteAddress();
  103. $this->throttler->sleepDelay($ip, $bruteforceProtectionAction);
  104. $this->throttler->registerAttempt($bruteforceProtectionAction, $ip, ['token' => $token]);
  105. }
  106. }