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

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