FederatedShareProvider.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bjoern Schiessle <bjoern@schiessle.org>
  6. * @author Björn Schießle <bjoern@schiessle.org>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. * @author Lukas Reschke <lukas@statuscode.ch>
  9. * @author Morris Jobke <hey@morrisjobke.de>
  10. * @author Robin Appelman <robin@icewind.nl>
  11. * @author Roeland Jago Douma <roeland@famdouma.nl>
  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\FederatedFileSharing;
  30. use OC\Share20\Share;
  31. use OCP\Federation\ICloudFederationProviderManager;
  32. use OCP\Federation\ICloudIdManager;
  33. use OCP\DB\QueryBuilder\IQueryBuilder;
  34. use OCP\Files\Folder;
  35. use OCP\Files\IRootFolder;
  36. use OCP\IConfig;
  37. use OCP\IL10N;
  38. use OCP\ILogger;
  39. use OCP\IUserManager;
  40. use OCP\Share\Exceptions\GenericShareException;
  41. use OCP\Share\IShare;
  42. use OCP\Share\IShareProvider;
  43. use OC\Share20\Exception\InvalidShare;
  44. use OCP\Share\Exceptions\ShareNotFound;
  45. use OCP\Files\NotFoundException;
  46. use OCP\IDBConnection;
  47. use OCP\Files\Node;
  48. /**
  49. * Class FederatedShareProvider
  50. *
  51. * @package OCA\FederatedFileSharing
  52. */
  53. class FederatedShareProvider implements IShareProvider {
  54. const SHARE_TYPE_REMOTE = 6;
  55. /** @var IDBConnection */
  56. private $dbConnection;
  57. /** @var AddressHandler */
  58. private $addressHandler;
  59. /** @var Notifications */
  60. private $notifications;
  61. /** @var TokenHandler */
  62. private $tokenHandler;
  63. /** @var IL10N */
  64. private $l;
  65. /** @var ILogger */
  66. private $logger;
  67. /** @var IRootFolder */
  68. private $rootFolder;
  69. /** @var IConfig */
  70. private $config;
  71. /** @var string */
  72. private $externalShareTable = 'share_external';
  73. /** @var IUserManager */
  74. private $userManager;
  75. /** @var ICloudIdManager */
  76. private $cloudIdManager;
  77. /** @var \OCP\GlobalScale\IConfig */
  78. private $gsConfig;
  79. /** @var ICloudFederationProviderManager */
  80. private $cloudFederationProviderManager;
  81. /** @var array list of supported share types */
  82. private $supportedShareType = [\OCP\Share::SHARE_TYPE_REMOTE_GROUP, \OCP\Share::SHARE_TYPE_REMOTE];
  83. /**
  84. * DefaultShareProvider constructor.
  85. *
  86. * @param IDBConnection $connection
  87. * @param AddressHandler $addressHandler
  88. * @param Notifications $notifications
  89. * @param TokenHandler $tokenHandler
  90. * @param IL10N $l10n
  91. * @param ILogger $logger
  92. * @param IRootFolder $rootFolder
  93. * @param IConfig $config
  94. * @param IUserManager $userManager
  95. * @param ICloudIdManager $cloudIdManager
  96. * @param \OCP\GlobalScale\IConfig $globalScaleConfig
  97. * @param ICloudFederationProviderManager $cloudFederationProviderManager
  98. */
  99. public function __construct(
  100. IDBConnection $connection,
  101. AddressHandler $addressHandler,
  102. Notifications $notifications,
  103. TokenHandler $tokenHandler,
  104. IL10N $l10n,
  105. ILogger $logger,
  106. IRootFolder $rootFolder,
  107. IConfig $config,
  108. IUserManager $userManager,
  109. ICloudIdManager $cloudIdManager,
  110. \OCP\GlobalScale\IConfig $globalScaleConfig,
  111. ICloudFederationProviderManager $cloudFederationProviderManager
  112. ) {
  113. $this->dbConnection = $connection;
  114. $this->addressHandler = $addressHandler;
  115. $this->notifications = $notifications;
  116. $this->tokenHandler = $tokenHandler;
  117. $this->l = $l10n;
  118. $this->logger = $logger;
  119. $this->rootFolder = $rootFolder;
  120. $this->config = $config;
  121. $this->userManager = $userManager;
  122. $this->cloudIdManager = $cloudIdManager;
  123. $this->gsConfig = $globalScaleConfig;
  124. $this->cloudFederationProviderManager = $cloudFederationProviderManager;
  125. }
  126. /**
  127. * Return the identifier of this provider.
  128. *
  129. * @return string Containing only [a-zA-Z0-9]
  130. */
  131. public function identifier() {
  132. return 'ocFederatedSharing';
  133. }
  134. /**
  135. * Share a path
  136. *
  137. * @param IShare $share
  138. * @return IShare The share object
  139. * @throws ShareNotFound
  140. * @throws \Exception
  141. */
  142. public function create(IShare $share) {
  143. $shareWith = $share->getSharedWith();
  144. $itemSource = $share->getNodeId();
  145. $itemType = $share->getNodeType();
  146. $permissions = $share->getPermissions();
  147. $sharedBy = $share->getSharedBy();
  148. $shareType = $share->getShareType();
  149. if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE_GROUP &&
  150. !$this->isOutgoingServer2serverGroupShareEnabled()
  151. ) {
  152. $message = 'It is not allowed to send federated group shares from this server.';
  153. $message_t = $this->l->t('It is not allowed to send federated group shares from this server.');
  154. $this->logger->debug($message, ['app' => 'Federated File Sharing']);
  155. throw new \Exception($message_t);
  156. }
  157. /*
  158. * Check if file is not already shared with the remote user
  159. */
  160. $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
  161. $alreadySharedGroup = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE_GROUP, $share->getNode(), 1, 0);
  162. if (!empty($alreadyShared) || !empty($alreadySharedGroup)) {
  163. $message = 'Sharing %1$s failed, because this item is already shared with %2$s';
  164. $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with %2$s', array($share->getNode()->getName(), $shareWith));
  165. $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
  166. throw new \Exception($message_t);
  167. }
  168. // don't allow federated shares if source and target server are the same
  169. $cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
  170. $currentServer = $this->addressHandler->generateRemoteURL();
  171. $currentUser = $sharedBy;
  172. if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
  173. $message = 'Not allowed to create a federated share with the same user.';
  174. $message_t = $this->l->t('Not allowed to create a federated share with the same user');
  175. $this->logger->debug($message, ['app' => 'Federated File Sharing']);
  176. throw new \Exception($message_t);
  177. }
  178. $share->setSharedWith($cloudId->getId());
  179. try {
  180. $remoteShare = $this->getShareFromExternalShareTable($share);
  181. } catch (ShareNotFound $e) {
  182. $remoteShare = null;
  183. }
  184. if ($remoteShare) {
  185. try {
  186. $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
  187. $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType);
  188. $share->setId($shareId);
  189. list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
  190. // remote share was create successfully if we get a valid token as return
  191. $send = is_string($token) && $token !== '';
  192. } catch (\Exception $e) {
  193. // fall back to old re-share behavior if the remote server
  194. // doesn't support flat re-shares (was introduced with Nextcloud 9.1)
  195. $this->removeShareFromTable($share);
  196. $shareId = $this->createFederatedShare($share);
  197. }
  198. if ($send) {
  199. $this->updateSuccessfulReshare($shareId, $token);
  200. $this->storeRemoteId($shareId, $remoteId);
  201. } else {
  202. $this->removeShareFromTable($share);
  203. $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
  204. throw new \Exception($message_t);
  205. }
  206. } else {
  207. $shareId = $this->createFederatedShare($share);
  208. }
  209. $data = $this->getRawShare($shareId);
  210. return $this->createShareObject($data);
  211. }
  212. /**
  213. * create federated share and inform the recipient
  214. *
  215. * @param IShare $share
  216. * @return int
  217. * @throws ShareNotFound
  218. * @throws \Exception
  219. */
  220. protected function createFederatedShare(IShare $share) {
  221. $token = $this->tokenHandler->generateToken();
  222. $shareId = $this->addShareToDB(
  223. $share->getNodeId(),
  224. $share->getNodeType(),
  225. $share->getSharedWith(),
  226. $share->getSharedBy(),
  227. $share->getShareOwner(),
  228. $share->getPermissions(),
  229. $token,
  230. $share->getShareType()
  231. );
  232. $failure = false;
  233. try {
  234. $sharedByFederatedId = $share->getSharedBy();
  235. if ($this->userManager->userExists($sharedByFederatedId)) {
  236. $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
  237. $sharedByFederatedId = $cloudId->getId();
  238. }
  239. $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
  240. $send = $this->notifications->sendRemoteShare(
  241. $token,
  242. $share->getSharedWith(),
  243. $share->getNode()->getName(),
  244. $shareId,
  245. $share->getShareOwner(),
  246. $ownerCloudId->getId(),
  247. $share->getSharedBy(),
  248. $sharedByFederatedId,
  249. $share->getShareType()
  250. );
  251. if ($send === false) {
  252. $failure = true;
  253. }
  254. } catch (\Exception $e) {
  255. $this->logger->logException($e, [
  256. 'message' => 'Failed to notify remote server of federated share, removing share.',
  257. 'level' => ILogger::ERROR,
  258. 'app' => 'federatedfilesharing',
  259. ]);
  260. $failure = true;
  261. }
  262. if($failure) {
  263. $this->removeShareFromTableById($shareId);
  264. $message_t = $this->l->t('Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate.',
  265. [$share->getNode()->getName(), $share->getSharedWith()]);
  266. throw new \Exception($message_t);
  267. }
  268. return $shareId;
  269. }
  270. /**
  271. * @param string $shareWith
  272. * @param IShare $share
  273. * @param string $shareId internal share Id
  274. * @return array
  275. * @throws \Exception
  276. */
  277. protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
  278. $remoteShare = $this->getShareFromExternalShareTable($share);
  279. $token = $remoteShare['share_token'];
  280. $remoteId = $remoteShare['remote_id'];
  281. $remote = $remoteShare['remote'];
  282. list($token, $remoteId) = $this->notifications->requestReShare(
  283. $token,
  284. $remoteId,
  285. $shareId,
  286. $remote,
  287. $shareWith,
  288. $share->getPermissions(),
  289. $share->getNode()->getName()
  290. );
  291. return [$token, $remoteId];
  292. }
  293. /**
  294. * get federated share from the share_external table but exclude mounted link shares
  295. *
  296. * @param IShare $share
  297. * @return array
  298. * @throws ShareNotFound
  299. */
  300. protected function getShareFromExternalShareTable(IShare $share) {
  301. $query = $this->dbConnection->getQueryBuilder();
  302. $query->select('*')->from($this->externalShareTable)
  303. ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
  304. ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
  305. $result = $query->execute()->fetchAll();
  306. if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
  307. return $result[0];
  308. }
  309. throw new ShareNotFound('share not found in share_external table');
  310. }
  311. /**
  312. * add share to the database and return the ID
  313. *
  314. * @param int $itemSource
  315. * @param string $itemType
  316. * @param string $shareWith
  317. * @param string $sharedBy
  318. * @param string $uidOwner
  319. * @param int $permissions
  320. * @param string $token
  321. * @param int $shareType
  322. * @return int
  323. */
  324. private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType) {
  325. $qb = $this->dbConnection->getQueryBuilder();
  326. $qb->insert('share')
  327. ->setValue('share_type', $qb->createNamedParameter($shareType))
  328. ->setValue('item_type', $qb->createNamedParameter($itemType))
  329. ->setValue('item_source', $qb->createNamedParameter($itemSource))
  330. ->setValue('file_source', $qb->createNamedParameter($itemSource))
  331. ->setValue('share_with', $qb->createNamedParameter($shareWith))
  332. ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
  333. ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
  334. ->setValue('permissions', $qb->createNamedParameter($permissions))
  335. ->setValue('token', $qb->createNamedParameter($token))
  336. ->setValue('stime', $qb->createNamedParameter(time()));
  337. /*
  338. * Added to fix https://github.com/owncloud/core/issues/22215
  339. * Can be removed once we get rid of ajax/share.php
  340. */
  341. $qb->setValue('file_target', $qb->createNamedParameter(''));
  342. $qb->execute();
  343. $id = $qb->getLastInsertId();
  344. return (int)$id;
  345. }
  346. /**
  347. * Update a share
  348. *
  349. * @param IShare $share
  350. * @return IShare The share object
  351. */
  352. public function update(IShare $share) {
  353. /*
  354. * We allow updating the permissions of federated shares
  355. */
  356. $qb = $this->dbConnection->getQueryBuilder();
  357. $qb->update('share')
  358. ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
  359. ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
  360. ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
  361. ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
  362. ->execute();
  363. // send the updated permission to the owner/initiator, if they are not the same
  364. if ($share->getShareOwner() !== $share->getSharedBy()) {
  365. $this->sendPermissionUpdate($share);
  366. }
  367. return $share;
  368. }
  369. /**
  370. * send the updated permission to the owner/initiator, if they are not the same
  371. *
  372. * @param IShare $share
  373. * @throws ShareNotFound
  374. * @throws \OC\HintException
  375. */
  376. protected function sendPermissionUpdate(IShare $share) {
  377. $remoteId = $this->getRemoteId($share);
  378. // if the local user is the owner we send the permission change to the initiator
  379. if ($this->userManager->userExists($share->getShareOwner())) {
  380. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
  381. } else { // ... if not we send the permission change to the owner
  382. list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
  383. }
  384. $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
  385. }
  386. /**
  387. * update successful reShare with the correct token
  388. *
  389. * @param int $shareId
  390. * @param string $token
  391. */
  392. protected function updateSuccessfulReShare($shareId, $token) {
  393. $query = $this->dbConnection->getQueryBuilder();
  394. $query->update('share')
  395. ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
  396. ->set('token', $query->createNamedParameter($token))
  397. ->execute();
  398. }
  399. /**
  400. * store remote ID in federated reShare table
  401. *
  402. * @param $shareId
  403. * @param $remoteId
  404. */
  405. public function storeRemoteId($shareId, $remoteId) {
  406. $query = $this->dbConnection->getQueryBuilder();
  407. $query->insert('federated_reshares')
  408. ->values(
  409. [
  410. 'share_id' => $query->createNamedParameter($shareId),
  411. 'remote_id' => $query->createNamedParameter($remoteId),
  412. ]
  413. );
  414. $query->execute();
  415. }
  416. /**
  417. * get share ID on remote server for federated re-shares
  418. *
  419. * @param IShare $share
  420. * @return int
  421. * @throws ShareNotFound
  422. */
  423. public function getRemoteId(IShare $share) {
  424. $query = $this->dbConnection->getQueryBuilder();
  425. $query->select('remote_id')->from('federated_reshares')
  426. ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
  427. $data = $query->execute()->fetch();
  428. if (!is_array($data) || !isset($data['remote_id'])) {
  429. throw new ShareNotFound();
  430. }
  431. return (int)$data['remote_id'];
  432. }
  433. /**
  434. * @inheritdoc
  435. */
  436. public function move(IShare $share, $recipient) {
  437. /*
  438. * This function does nothing yet as it is just for outgoing
  439. * federated shares.
  440. */
  441. return $share;
  442. }
  443. /**
  444. * Get all children of this share
  445. *
  446. * @param IShare $parent
  447. * @return IShare[]
  448. */
  449. public function getChildren(IShare $parent) {
  450. $children = [];
  451. $qb = $this->dbConnection->getQueryBuilder();
  452. $qb->select('*')
  453. ->from('share')
  454. ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
  455. ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
  456. ->orderBy('id');
  457. $cursor = $qb->execute();
  458. while($data = $cursor->fetch()) {
  459. $children[] = $this->createShareObject($data);
  460. }
  461. $cursor->closeCursor();
  462. return $children;
  463. }
  464. /**
  465. * Delete a share (owner unShares the file)
  466. *
  467. * @param IShare $share
  468. * @throws ShareNotFound
  469. * @throws \OC\HintException
  470. */
  471. public function delete(IShare $share) {
  472. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
  473. // if the local user is the owner we can send the unShare request directly...
  474. if ($this->userManager->userExists($share->getShareOwner())) {
  475. $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
  476. $this->revokeShare($share, true);
  477. } else { // ... if not we need to correct ID for the unShare request
  478. $remoteId = $this->getRemoteId($share);
  479. $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
  480. $this->revokeShare($share, false);
  481. }
  482. // only remove the share when all messages are send to not lose information
  483. // about the share to early
  484. $this->removeShareFromTable($share);
  485. }
  486. /**
  487. * in case of a re-share we need to send the other use (initiator or owner)
  488. * a message that the file was unshared
  489. *
  490. * @param IShare $share
  491. * @param bool $isOwner the user can either be the owner or the user who re-sahred it
  492. * @throws ShareNotFound
  493. * @throws \OC\HintException
  494. */
  495. protected function revokeShare($share, $isOwner) {
  496. // also send a unShare request to the initiator, if this is a different user than the owner
  497. if ($share->getShareOwner() !== $share->getSharedBy()) {
  498. if ($isOwner) {
  499. list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
  500. } else {
  501. list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
  502. }
  503. $remoteId = $this->getRemoteId($share);
  504. $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
  505. }
  506. }
  507. /**
  508. * remove share from table
  509. *
  510. * @param IShare $share
  511. */
  512. public function removeShareFromTable(IShare $share) {
  513. $this->removeShareFromTableById($share->getId());
  514. }
  515. /**
  516. * remove share from table
  517. *
  518. * @param string $shareId
  519. */
  520. private function removeShareFromTableById($shareId) {
  521. $qb = $this->dbConnection->getQueryBuilder();
  522. $qb->delete('share')
  523. ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
  524. $qb->execute();
  525. $qb->delete('federated_reshares')
  526. ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
  527. $qb->execute();
  528. }
  529. /**
  530. * @inheritdoc
  531. */
  532. public function deleteFromSelf(IShare $share, $recipient) {
  533. // nothing to do here. Technically deleteFromSelf in the context of federated
  534. // shares is a umount of a external storage. This is handled here
  535. // apps/files_sharing/lib/external/manager.php
  536. // TODO move this code over to this app
  537. }
  538. public function restore(IShare $share, string $recipient): IShare {
  539. throw new GenericShareException('not implemented');
  540. }
  541. public function getSharesInFolder($userId, Folder $node, $reshares) {
  542. $qb = $this->dbConnection->getQueryBuilder();
  543. $qb->select('*')
  544. ->from('share', 's')
  545. ->andWhere($qb->expr()->orX(
  546. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  547. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  548. ))
  549. ->andWhere(
  550. $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
  551. );
  552. /**
  553. * Reshares for this user are shares where they are the owner.
  554. */
  555. if ($reshares === false) {
  556. $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
  557. } else {
  558. $qb->andWhere(
  559. $qb->expr()->orX(
  560. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  561. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  562. )
  563. );
  564. }
  565. $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
  566. $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
  567. $qb->orderBy('id');
  568. $cursor = $qb->execute();
  569. $shares = [];
  570. while ($data = $cursor->fetch()) {
  571. $shares[$data['fileid']][] = $this->createShareObject($data);
  572. }
  573. $cursor->closeCursor();
  574. return $shares;
  575. }
  576. /**
  577. * @inheritdoc
  578. */
  579. public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
  580. $qb = $this->dbConnection->getQueryBuilder();
  581. $qb->select('*')
  582. ->from('share');
  583. $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
  584. /**
  585. * Reshares for this user are shares where they are the owner.
  586. */
  587. if ($reshares === false) {
  588. //Special case for old shares created via the web UI
  589. $or1 = $qb->expr()->andX(
  590. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  591. $qb->expr()->isNull('uid_initiator')
  592. );
  593. $qb->andWhere(
  594. $qb->expr()->orX(
  595. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
  596. $or1
  597. )
  598. );
  599. } else {
  600. $qb->andWhere(
  601. $qb->expr()->orX(
  602. $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
  603. $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
  604. )
  605. );
  606. }
  607. if ($node !== null) {
  608. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  609. }
  610. if ($limit !== -1) {
  611. $qb->setMaxResults($limit);
  612. }
  613. $qb->setFirstResult($offset);
  614. $qb->orderBy('id');
  615. $cursor = $qb->execute();
  616. $shares = [];
  617. while($data = $cursor->fetch()) {
  618. $shares[] = $this->createShareObject($data);
  619. }
  620. $cursor->closeCursor();
  621. return $shares;
  622. }
  623. /**
  624. * @inheritdoc
  625. */
  626. public function getShareById($id, $recipientId = null) {
  627. $qb = $this->dbConnection->getQueryBuilder();
  628. $qb->select('*')
  629. ->from('share')
  630. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
  631. ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
  632. $cursor = $qb->execute();
  633. $data = $cursor->fetch();
  634. $cursor->closeCursor();
  635. if ($data === false) {
  636. throw new ShareNotFound('Can not find share with ID: ' . $id);
  637. }
  638. try {
  639. $share = $this->createShareObject($data);
  640. } catch (InvalidShare $e) {
  641. throw new ShareNotFound();
  642. }
  643. return $share;
  644. }
  645. /**
  646. * Get shares for a given path
  647. *
  648. * @param \OCP\Files\Node $path
  649. * @return IShare[]
  650. */
  651. public function getSharesByPath(Node $path) {
  652. $qb = $this->dbConnection->getQueryBuilder();
  653. // get federated user shares
  654. $cursor = $qb->select('*')
  655. ->from('share')
  656. ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
  657. ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
  658. ->execute();
  659. $shares = [];
  660. while($data = $cursor->fetch()) {
  661. $shares[] = $this->createShareObject($data);
  662. }
  663. $cursor->closeCursor();
  664. return $shares;
  665. }
  666. /**
  667. * @inheritdoc
  668. */
  669. public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
  670. /** @var IShare[] $shares */
  671. $shares = [];
  672. //Get shares directly with this user
  673. $qb = $this->dbConnection->getQueryBuilder();
  674. $qb->select('*')
  675. ->from('share');
  676. // Order by id
  677. $qb->orderBy('id');
  678. // Set limit and offset
  679. if ($limit !== -1) {
  680. $qb->setMaxResults($limit);
  681. }
  682. $qb->setFirstResult($offset);
  683. $qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
  684. $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
  685. // Filter by node if provided
  686. if ($node !== null) {
  687. $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
  688. }
  689. $cursor = $qb->execute();
  690. while($data = $cursor->fetch()) {
  691. $shares[] = $this->createShareObject($data);
  692. }
  693. $cursor->closeCursor();
  694. return $shares;
  695. }
  696. /**
  697. * Get a share by token
  698. *
  699. * @param string $token
  700. * @return IShare
  701. * @throws ShareNotFound
  702. */
  703. public function getShareByToken($token) {
  704. $qb = $this->dbConnection->getQueryBuilder();
  705. $cursor = $qb->select('*')
  706. ->from('share')
  707. ->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
  708. ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
  709. ->execute();
  710. $data = $cursor->fetch();
  711. if ($data === false) {
  712. throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
  713. }
  714. try {
  715. $share = $this->createShareObject($data);
  716. } catch (InvalidShare $e) {
  717. throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
  718. }
  719. return $share;
  720. }
  721. /**
  722. * get database row of a give share
  723. *
  724. * @param $id
  725. * @return array
  726. * @throws ShareNotFound
  727. */
  728. private function getRawShare($id) {
  729. // Now fetch the inserted share and create a complete share object
  730. $qb = $this->dbConnection->getQueryBuilder();
  731. $qb->select('*')
  732. ->from('share')
  733. ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
  734. $cursor = $qb->execute();
  735. $data = $cursor->fetch();
  736. $cursor->closeCursor();
  737. if ($data === false) {
  738. throw new ShareNotFound;
  739. }
  740. return $data;
  741. }
  742. /**
  743. * Create a share object from an database row
  744. *
  745. * @param array $data
  746. * @return IShare
  747. * @throws InvalidShare
  748. * @throws ShareNotFound
  749. */
  750. private function createShareObject($data) {
  751. $share = new Share($this->rootFolder, $this->userManager);
  752. $share->setId((int)$data['id'])
  753. ->setShareType((int)$data['share_type'])
  754. ->setPermissions((int)$data['permissions'])
  755. ->setTarget($data['file_target'])
  756. ->setMailSend((bool)$data['mail_send'])
  757. ->setToken($data['token']);
  758. $shareTime = new \DateTime();
  759. $shareTime->setTimestamp((int)$data['stime']);
  760. $share->setShareTime($shareTime);
  761. $share->setSharedWith($data['share_with']);
  762. if ($data['uid_initiator'] !== null) {
  763. $share->setShareOwner($data['uid_owner']);
  764. $share->setSharedBy($data['uid_initiator']);
  765. } else {
  766. //OLD SHARE
  767. $share->setSharedBy($data['uid_owner']);
  768. $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
  769. $owner = $path->getOwner();
  770. $share->setShareOwner($owner->getUID());
  771. }
  772. $share->setNodeId((int)$data['file_source']);
  773. $share->setNodeType($data['item_type']);
  774. $share->setProviderId($this->identifier());
  775. return $share;
  776. }
  777. /**
  778. * Get the node with file $id for $user
  779. *
  780. * @param string $userId
  781. * @param int $id
  782. * @return \OCP\Files\File|\OCP\Files\Folder
  783. * @throws InvalidShare
  784. */
  785. private function getNode($userId, $id) {
  786. try {
  787. $userFolder = $this->rootFolder->getUserFolder($userId);
  788. } catch (NotFoundException $e) {
  789. throw new InvalidShare();
  790. }
  791. $nodes = $userFolder->getById($id);
  792. if (empty($nodes)) {
  793. throw new InvalidShare();
  794. }
  795. return $nodes[0];
  796. }
  797. /**
  798. * A user is deleted from the system
  799. * So clean up the relevant shares.
  800. *
  801. * @param string $uid
  802. * @param int $shareType
  803. */
  804. public function userDeleted($uid, $shareType) {
  805. //TODO: probabaly a good idea to send unshare info to remote servers
  806. $qb = $this->dbConnection->getQueryBuilder();
  807. $qb->delete('share')
  808. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
  809. ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
  810. ->execute();
  811. }
  812. /**
  813. * This provider does not handle groups
  814. *
  815. * @param string $gid
  816. */
  817. public function groupDeleted($gid) {
  818. // We don't handle groups here
  819. }
  820. /**
  821. * This provider does not handle groups
  822. *
  823. * @param string $uid
  824. * @param string $gid
  825. */
  826. public function userDeletedFromGroup($uid, $gid) {
  827. // We don't handle groups here
  828. }
  829. /**
  830. * check if users from other Nextcloud instances are allowed to mount public links share by this instance
  831. *
  832. * @return bool
  833. */
  834. public function isOutgoingServer2serverShareEnabled() {
  835. if ($this->gsConfig->onlyInternalFederation()) {
  836. return false;
  837. }
  838. $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
  839. return ($result === 'yes');
  840. }
  841. /**
  842. * check if users are allowed to mount public links from other Nextclouds
  843. *
  844. * @return bool
  845. */
  846. public function isIncomingServer2serverShareEnabled() {
  847. if ($this->gsConfig->onlyInternalFederation()) {
  848. return false;
  849. }
  850. $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
  851. return ($result === 'yes');
  852. }
  853. /**
  854. * check if users from other Nextcloud instances are allowed to send federated group shares
  855. *
  856. * @return bool
  857. */
  858. public function isOutgoingServer2serverGroupShareEnabled() {
  859. if ($this->gsConfig->onlyInternalFederation()) {
  860. return false;
  861. }
  862. $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no');
  863. return ($result === 'yes');
  864. }
  865. /**
  866. * check if users are allowed to receive federated group shares
  867. *
  868. * @return bool
  869. */
  870. public function isIncomingServer2serverGroupShareEnabled() {
  871. if ($this->gsConfig->onlyInternalFederation()) {
  872. return false;
  873. }
  874. $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no');
  875. return ($result === 'yes');
  876. }
  877. /**
  878. * check if federated group sharing is supported, therefore the OCM API need to be enabled
  879. *
  880. * @return bool
  881. */
  882. public function isFederatedGroupSharingSupported() {
  883. return $this->cloudFederationProviderManager->isReady();
  884. }
  885. /**
  886. * Check if querying sharees on the lookup server is enabled
  887. *
  888. * @return bool
  889. */
  890. public function isLookupServerQueriesEnabled() {
  891. // in a global scale setup we should always query the lookup server
  892. if ($this->gsConfig->isGlobalScaleEnabled()) {
  893. return true;
  894. }
  895. $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
  896. return ($result === 'yes');
  897. }
  898. /**
  899. * Check if it is allowed to publish user specific data to the lookup server
  900. *
  901. * @return bool
  902. */
  903. public function isLookupServerUploadEnabled() {
  904. // in a global scale setup the admin is responsible to keep the lookup server up-to-date
  905. if ($this->gsConfig->isGlobalScaleEnabled()) {
  906. return false;
  907. }
  908. $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
  909. return ($result === 'yes');
  910. }
  911. /**
  912. * @inheritdoc
  913. */
  914. public function getAccessList($nodes, $currentAccess) {
  915. $ids = [];
  916. foreach ($nodes as $node) {
  917. $ids[] = $node->getId();
  918. }
  919. $qb = $this->dbConnection->getQueryBuilder();
  920. $qb->select('share_with', 'token', 'file_source')
  921. ->from('share')
  922. ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
  923. ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
  924. ->andWhere($qb->expr()->orX(
  925. $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
  926. $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
  927. ));
  928. $cursor = $qb->execute();
  929. if ($currentAccess === false) {
  930. $remote = $cursor->fetch() !== false;
  931. $cursor->closeCursor();
  932. return ['remote' => $remote];
  933. }
  934. $remote = [];
  935. while ($row = $cursor->fetch()) {
  936. $remote[$row['share_with']] = [
  937. 'node_id' => $row['file_source'],
  938. 'token' => $row['token'],
  939. ];
  940. }
  941. $cursor->closeCursor();
  942. return ['remote' => $remote];
  943. }
  944. }