LegacyPublicAuth.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\DAV\Connector;
  8. use OCA\DAV\Connector\Sabre\PublicAuth;
  9. use OCP\Defaults;
  10. use OCP\IRequest;
  11. use OCP\ISession;
  12. use OCP\Security\Bruteforce\IThrottler;
  13. use OCP\Share\Exceptions\ShareNotFound;
  14. use OCP\Share\IManager;
  15. use OCP\Share\IShare;
  16. use Sabre\DAV\Auth\Backend\AbstractBasic;
  17. /**
  18. * Class PublicAuth
  19. *
  20. * @package OCA\DAV\Connector
  21. */
  22. class LegacyPublicAuth extends AbstractBasic {
  23. private const BRUTEFORCE_ACTION = 'legacy_public_webdav_auth';
  24. private ?IShare $share = null;
  25. public function __construct(
  26. private IRequest $request,
  27. private IManager $shareManager,
  28. private ISession $session,
  29. private IThrottler $throttler,
  30. ) {
  31. // setup realm
  32. $defaults = new Defaults();
  33. $this->realm = $defaults->getName() ?: 'Nextcloud';
  34. }
  35. /**
  36. * Validates a username and password
  37. *
  38. * This method should return true or false depending on if login
  39. * succeeded.
  40. *
  41. * @param string $username
  42. * @param string $password
  43. *
  44. * @return bool
  45. * @throws \Sabre\DAV\Exception\NotAuthenticated
  46. */
  47. protected function validateUserPass($username, $password) {
  48. $this->throttler->sleepDelayOrThrowOnMax($this->request->getRemoteAddress(), self::BRUTEFORCE_ACTION);
  49. try {
  50. $share = $this->shareManager->getShareByToken($username);
  51. } catch (ShareNotFound $e) {
  52. $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
  53. return false;
  54. }
  55. $this->share = $share;
  56. \OC_User::setIncognitoMode(true);
  57. // check if the share is password protected
  58. if ($share->getPassword() !== null) {
  59. if ($share->getShareType() === IShare::TYPE_LINK
  60. || $share->getShareType() === IShare::TYPE_EMAIL
  61. || $share->getShareType() === IShare::TYPE_CIRCLE) {
  62. if ($this->shareManager->checkPassword($share, $password)) {
  63. return true;
  64. } elseif ($this->session->exists(PublicAuth::DAV_AUTHENTICATED)
  65. && $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId()) {
  66. return true;
  67. } else {
  68. if (in_array('XMLHttpRequest', explode(',', $this->request->getHeader('X-Requested-With')))) {
  69. // do not re-authenticate over ajax, use dummy auth name to prevent browser popup
  70. http_response_code(401);
  71. header('WWW-Authenticate: DummyBasic realm="' . $this->realm . '"');
  72. throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
  73. }
  74. $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
  75. return false;
  76. }
  77. } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
  78. return true;
  79. } else {
  80. $this->throttler->registerAttempt(self::BRUTEFORCE_ACTION, $this->request->getRemoteAddress());
  81. return false;
  82. }
  83. }
  84. return true;
  85. }
  86. public function getShare(): IShare {
  87. assert($this->share !== null);
  88. return $this->share;
  89. }
  90. }