DavAclPlugin.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Lukas Reschke <lukas@statuscode.ch>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Robin Appelman <robin@icewind.nl>
  9. * @author Roeland Jago Douma <roeland@famdouma.nl>
  10. * @author Thomas Müller <thomas.mueller@tmit.eu>
  11. * @author Richard Steinmetz <richard@steinmetz.cloud>
  12. *
  13. * @license AGPL-3.0
  14. *
  15. * This code is free software: you can redistribute it and/or modify
  16. * it under the terms of the GNU Affero General Public License, version 3,
  17. * as published by the Free Software Foundation.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU Affero General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Affero General Public License, version 3,
  25. * along with this program. If not, see <http://www.gnu.org/licenses/>
  26. *
  27. */
  28. namespace OCA\DAV\Connector\Sabre;
  29. use OCA\DAV\CalDAV\CachedSubscription;
  30. use OCA\DAV\CalDAV\Calendar;
  31. use OCA\DAV\CardDAV\AddressBook;
  32. use Sabre\CalDAV\Principal\User;
  33. use Sabre\DAV\Exception\NotFound;
  34. use Sabre\DAV\INode;
  35. use Sabre\DAV\PropFind;
  36. use Sabre\HTTP\RequestInterface;
  37. use Sabre\HTTP\ResponseInterface;
  38. /**
  39. * Class DavAclPlugin is a wrapper around \Sabre\DAVACL\Plugin that returns 404
  40. * responses in case the resource to a response has been forbidden instead of
  41. * a 403. This is used to prevent enumeration of valid resources.
  42. *
  43. * @see https://github.com/owncloud/core/issues/22578
  44. * @package OCA\DAV\Connector\Sabre
  45. */
  46. class DavAclPlugin extends \Sabre\DAVACL\Plugin {
  47. public function __construct() {
  48. $this->hideNodesFromListings = true;
  49. $this->allowUnauthenticatedAccess = false;
  50. }
  51. public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
  52. $access = parent::checkPrivileges($uri, $privileges, $recursion, false);
  53. if ($access === false && $throwExceptions) {
  54. /** @var INode $node */
  55. $node = $this->server->tree->getNodeForPath($uri);
  56. switch (get_class($node)) {
  57. case AddressBook::class:
  58. $type = 'Addressbook';
  59. break;
  60. case Calendar::class:
  61. case CachedSubscription::class:
  62. $type = 'Calendar';
  63. break;
  64. default:
  65. $type = 'Node';
  66. break;
  67. }
  68. throw new NotFound(
  69. sprintf(
  70. "%s with name '%s' could not be found",
  71. $type,
  72. $node->getName()
  73. )
  74. );
  75. }
  76. return $access;
  77. }
  78. public function propFind(PropFind $propFind, INode $node) {
  79. if ($node instanceof Node) {
  80. // files don't use dav acls
  81. return;
  82. }
  83. // If the node is neither readable nor writable then fail unless its of
  84. // the standard user-principal
  85. if (!($node instanceof User)) {
  86. $path = $propFind->getPath();
  87. $readPermissions = $this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false);
  88. $writePermissions = $this->checkPrivileges($path, '{DAV:}write', self::R_PARENT, false);
  89. if ($readPermissions === false && $writePermissions === false) {
  90. $this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, true);
  91. $this->checkPrivileges($path, '{DAV:}write', self::R_PARENT, true);
  92. }
  93. }
  94. return parent::propFind($propFind, $node);
  95. }
  96. public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
  97. $path = $request->getPath();
  98. // prevent the plugin from causing an unneeded overhead for file requests
  99. if (str_starts_with($path, 'files/')) {
  100. return;
  101. }
  102. parent::beforeMethod($request, $response);
  103. if (!str_starts_with($path, 'addressbooks/') && !str_starts_with($path, 'calendars/')) {
  104. return;
  105. }
  106. [$parentName] = \Sabre\Uri\split($path);
  107. if ($request->getMethod() === 'REPORT') {
  108. // is calendars/users/bob or addressbooks/users/bob readable?
  109. $this->checkPrivileges($parentName, '{DAV:}read');
  110. } elseif ($request->getMethod() === 'MKCALENDAR' || $request->getMethod() === 'MKCOL') {
  111. // is calendars/users/bob or addressbooks/users/bob writeable?
  112. $this->checkPrivileges($parentName, '{DAV:}write');
  113. }
  114. }
  115. }