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.5KB

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