You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Backend.php 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Lukas Reschke <lukas@statuscode.ch>
  8. * @author Morris Jobke <hey@morrisjobke.de>
  9. * @author Roeland Jago Douma <roeland@famdouma.nl>
  10. * @author Thomas Citharel <nextcloud@tcit.fr>
  11. * @author Thomas Müller <thomas.mueller@tmit.eu>
  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\DAV\Sharing;
  29. use OCA\DAV\Connector\Sabre\Principal;
  30. use OCP\IDBConnection;
  31. use OCP\IGroupManager;
  32. use OCP\IUserManager;
  33. class Backend {
  34. /** @var IDBConnection */
  35. private $db;
  36. /** @var IUserManager */
  37. private $userManager;
  38. /** @var IGroupManager */
  39. private $groupManager;
  40. /** @var Principal */
  41. private $principalBackend;
  42. /** @var string */
  43. private $resourceType;
  44. public const ACCESS_OWNER = 1;
  45. public const ACCESS_READ_WRITE = 2;
  46. public const ACCESS_READ = 3;
  47. /**
  48. * @param IDBConnection $db
  49. * @param IUserManager $userManager
  50. * @param IGroupManager $groupManager
  51. * @param Principal $principalBackend
  52. * @param string $resourceType
  53. */
  54. public function __construct(IDBConnection $db, IUserManager $userManager, IGroupManager $groupManager, Principal $principalBackend, $resourceType) {
  55. $this->db = $db;
  56. $this->userManager = $userManager;
  57. $this->groupManager = $groupManager;
  58. $this->principalBackend = $principalBackend;
  59. $this->resourceType = $resourceType;
  60. }
  61. /**
  62. * @param IShareable $shareable
  63. * @param string[] $add
  64. * @param string[] $remove
  65. */
  66. public function updateShares(IShareable $shareable, array $add, array $remove) {
  67. foreach ($add as $element) {
  68. $principal = $this->principalBackend->findByUri($element['href'], '');
  69. if ($principal !== '') {
  70. $this->shareWith($shareable, $element);
  71. }
  72. }
  73. foreach ($remove as $element) {
  74. $principal = $this->principalBackend->findByUri($element, '');
  75. if ($principal !== '') {
  76. $this->unshare($shareable, $element);
  77. }
  78. }
  79. }
  80. /**
  81. * @param IShareable $shareable
  82. * @param string $element
  83. */
  84. private function shareWith($shareable, $element) {
  85. $user = $element['href'];
  86. $parts = explode(':', $user, 2);
  87. if ($parts[0] !== 'principal') {
  88. return;
  89. }
  90. // don't share with owner
  91. if ($shareable->getOwner() === $parts[1]) {
  92. return;
  93. }
  94. $principal = explode('/', $parts[1], 3);
  95. if (count($principal) !== 3 || $principal[0] !== 'principals' || !in_array($principal[1], ['users', 'groups', 'circles'], true)) {
  96. // Invalid principal
  97. return;
  98. }
  99. if (($principal[1] === 'users' && !$this->userManager->userExists($principal[2])) ||
  100. ($principal[1] === 'groups' && !$this->groupManager->groupExists($principal[2]))) {
  101. // User or group does not exist
  102. return;
  103. }
  104. // remove the share if it already exists
  105. $this->unshare($shareable, $element['href']);
  106. $access = self::ACCESS_READ;
  107. if (isset($element['readOnly'])) {
  108. $access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE;
  109. }
  110. $query = $this->db->getQueryBuilder();
  111. $query->insert('dav_shares')
  112. ->values([
  113. 'principaluri' => $query->createNamedParameter($parts[1]),
  114. 'type' => $query->createNamedParameter($this->resourceType),
  115. 'access' => $query->createNamedParameter($access),
  116. 'resourceid' => $query->createNamedParameter($shareable->getResourceId())
  117. ]);
  118. $query->execute();
  119. }
  120. /**
  121. * @param $resourceId
  122. */
  123. public function deleteAllShares($resourceId) {
  124. $query = $this->db->getQueryBuilder();
  125. $query->delete('dav_shares')
  126. ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
  127. ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
  128. ->execute();
  129. }
  130. public function deleteAllSharesByUser($principaluri) {
  131. $query = $this->db->getQueryBuilder();
  132. $query->delete('dav_shares')
  133. ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri)))
  134. ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
  135. ->execute();
  136. }
  137. /**
  138. * @param IShareable $shareable
  139. * @param string $element
  140. */
  141. private function unshare($shareable, $element) {
  142. $parts = explode(':', $element, 2);
  143. if ($parts[0] !== 'principal') {
  144. return;
  145. }
  146. // don't share with owner
  147. if ($shareable->getOwner() === $parts[1]) {
  148. return;
  149. }
  150. $query = $this->db->getQueryBuilder();
  151. $query->delete('dav_shares')
  152. ->where($query->expr()->eq('resourceid', $query->createNamedParameter($shareable->getResourceId())))
  153. ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
  154. ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1])))
  155. ;
  156. $query->execute();
  157. }
  158. /**
  159. * Returns the list of people whom this resource is shared with.
  160. *
  161. * Every element in this array should have the following properties:
  162. * * href - Often a mailto: address
  163. * * commonName - Optional, for example a first + last name
  164. * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
  165. * * readOnly - boolean
  166. * * summary - Optional, a description for the share
  167. *
  168. * @param int $resourceId
  169. * @return array
  170. */
  171. public function getShares($resourceId) {
  172. $query = $this->db->getQueryBuilder();
  173. $result = $query->select(['principaluri', 'access'])
  174. ->from('dav_shares')
  175. ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
  176. ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
  177. ->execute();
  178. $shares = [];
  179. while ($row = $result->fetch()) {
  180. $p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
  181. $shares[] = [
  182. 'href' => "principal:${row['principaluri']}",
  183. 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '',
  184. 'status' => 1,
  185. 'readOnly' => (int) $row['access'] === self::ACCESS_READ,
  186. '{http://owncloud.org/ns}principal' => $row['principaluri'],
  187. '{http://owncloud.org/ns}group-share' => is_null($p)
  188. ];
  189. }
  190. return $shares;
  191. }
  192. /**
  193. * For shared resources the sharee is set in the ACL of the resource
  194. *
  195. * @param int $resourceId
  196. * @param array $acl
  197. * @return array
  198. */
  199. public function applyShareAcl($resourceId, $acl) {
  200. $shares = $this->getShares($resourceId);
  201. foreach ($shares as $share) {
  202. $acl[] = [
  203. 'privilege' => '{DAV:}read',
  204. 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
  205. 'protected' => true,
  206. ];
  207. if (!$share['readOnly']) {
  208. $acl[] = [
  209. 'privilege' => '{DAV:}write',
  210. 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
  211. 'protected' => true,
  212. ];
  213. } elseif ($this->resourceType === 'calendar') {
  214. // Allow changing the properties of read only calendars,
  215. // so users can change the visibility.
  216. $acl[] = [
  217. 'privilege' => '{DAV:}write-properties',
  218. 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
  219. 'protected' => true,
  220. ];
  221. }
  222. }
  223. return $acl;
  224. }
  225. }