PublicShareMiddleware.php 3.8 KB

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