Manager.php 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Share20;
  8. use OC\Files\Mount\MoveableMount;
  9. use OC\KnownUser\KnownUserService;
  10. use OC\Share20\Exception\ProviderException;
  11. use OCA\Files_Sharing\AppInfo\Application;
  12. use OCA\Files_Sharing\SharedStorage;
  13. use OCP\EventDispatcher\IEventDispatcher;
  14. use OCP\Files\File;
  15. use OCP\Files\Folder;
  16. use OCP\Files\IRootFolder;
  17. use OCP\Files\Mount\IMountManager;
  18. use OCP\Files\Node;
  19. use OCP\HintException;
  20. use OCP\IConfig;
  21. use OCP\IDateTimeZone;
  22. use OCP\IGroupManager;
  23. use OCP\IL10N;
  24. use OCP\IURLGenerator;
  25. use OCP\IUser;
  26. use OCP\IUserManager;
  27. use OCP\IUserSession;
  28. use OCP\L10N\IFactory;
  29. use OCP\Mail\IMailer;
  30. use OCP\Security\Events\ValidatePasswordPolicyEvent;
  31. use OCP\Security\IHasher;
  32. use OCP\Security\ISecureRandom;
  33. use OCP\Share;
  34. use OCP\Share\Events\BeforeShareDeletedEvent;
  35. use OCP\Share\Events\ShareAcceptedEvent;
  36. use OCP\Share\Events\ShareCreatedEvent;
  37. use OCP\Share\Events\ShareDeletedEvent;
  38. use OCP\Share\Events\ShareDeletedFromSelfEvent;
  39. use OCP\Share\Exceptions\AlreadySharedException;
  40. use OCP\Share\Exceptions\GenericShareException;
  41. use OCP\Share\Exceptions\ShareNotFound;
  42. use OCP\Share\IManager;
  43. use OCP\Share\IProviderFactory;
  44. use OCP\Share\IShare;
  45. use OCP\Share\IShareProvider;
  46. use OCP\Share\IShareProviderSupportsAccept;
  47. use OCP\Share\IShareProviderWithNotification;
  48. use Psr\Log\LoggerInterface;
  49. /**
  50. * This class is the communication hub for all sharing related operations.
  51. */
  52. class Manager implements IManager {
  53. private IL10N|null $l;
  54. private LegacyHooks $legacyHooks;
  55. public function __construct(
  56. private LoggerInterface $logger,
  57. private IConfig $config,
  58. private ISecureRandom $secureRandom,
  59. private IHasher $hasher,
  60. private IMountManager $mountManager,
  61. private IGroupManager $groupManager,
  62. private IFactory $l10nFactory,
  63. private IProviderFactory $factory,
  64. private IUserManager $userManager,
  65. private IRootFolder $rootFolder,
  66. private IMailer $mailer,
  67. private IURLGenerator $urlGenerator,
  68. private \OC_Defaults $defaults,
  69. private IEventDispatcher $dispatcher,
  70. private IUserSession $userSession,
  71. private KnownUserService $knownUserService,
  72. private ShareDisableChecker $shareDisableChecker,
  73. private IDateTimeZone $dateTimeZone
  74. ) {
  75. $this->l = $this->l10nFactory->get('lib');
  76. // The constructor of LegacyHooks registers the listeners of share events
  77. // do not remove if those are not properly migrated
  78. $this->legacyHooks = new LegacyHooks($this->dispatcher);
  79. }
  80. /**
  81. * Convert from a full share id to a tuple (providerId, shareId)
  82. *
  83. * @param string $id
  84. * @return string[]
  85. */
  86. private function splitFullId($id) {
  87. return explode(':', $id, 2);
  88. }
  89. /**
  90. * Verify if a password meets all requirements
  91. *
  92. * @param string $password
  93. * @throws \Exception
  94. */
  95. protected function verifyPassword($password) {
  96. if ($password === null) {
  97. // No password is set, check if this is allowed.
  98. if ($this->shareApiLinkEnforcePassword()) {
  99. throw new \InvalidArgumentException($this->l->t('Passwords are enforced for link and mail shares'));
  100. }
  101. return;
  102. }
  103. // Let others verify the password
  104. try {
  105. $this->dispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password));
  106. } catch (HintException $e) {
  107. throw new \Exception($e->getHint());
  108. }
  109. }
  110. /**
  111. * Check for generic requirements before creating a share
  112. *
  113. * @param IShare $share
  114. * @throws \InvalidArgumentException
  115. * @throws GenericShareException
  116. *
  117. * @suppress PhanUndeclaredClassMethod
  118. */
  119. protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
  120. if ($share->getShareType() === IShare::TYPE_USER) {
  121. // We expect a valid user as sharedWith for user shares
  122. if (!$this->userManager->userExists($share->getSharedWith())) {
  123. throw new \InvalidArgumentException($this->l->t('SharedWith is not a valid user'));
  124. }
  125. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  126. // We expect a valid group as sharedWith for group shares
  127. if (!$this->groupManager->groupExists($share->getSharedWith())) {
  128. throw new \InvalidArgumentException($this->l->t('SharedWith is not a valid group'));
  129. }
  130. } elseif ($share->getShareType() === IShare::TYPE_LINK) {
  131. // No check for TYPE_EMAIL here as we have a recipient for them
  132. if ($share->getSharedWith() !== null) {
  133. throw new \InvalidArgumentException($this->l->t('SharedWith should be empty'));
  134. }
  135. } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
  136. if ($share->getSharedWith() === null) {
  137. throw new \InvalidArgumentException($this->l->t('SharedWith should not be empty'));
  138. }
  139. } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
  140. if ($share->getSharedWith() === null) {
  141. throw new \InvalidArgumentException($this->l->t('SharedWith should not be empty'));
  142. }
  143. } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
  144. if ($share->getSharedWith() === null) {
  145. throw new \InvalidArgumentException($this->l->t('SharedWith should not be empty'));
  146. }
  147. } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
  148. $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
  149. if ($circle === null) {
  150. throw new \InvalidArgumentException($this->l->t('SharedWith is not a valid circle'));
  151. }
  152. } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
  153. } elseif ($share->getShareType() === IShare::TYPE_DECK) {
  154. } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
  155. } else {
  156. // We cannot handle other types yet
  157. throw new \InvalidArgumentException($this->l->t('Unknown share type'));
  158. }
  159. // Verify the initiator of the share is set
  160. if ($share->getSharedBy() === null) {
  161. throw new \InvalidArgumentException($this->l->t('SharedBy should be set'));
  162. }
  163. // Cannot share with yourself
  164. if ($share->getShareType() === IShare::TYPE_USER &&
  165. $share->getSharedWith() === $share->getSharedBy()) {
  166. throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
  167. }
  168. // The path should be set
  169. if ($share->getNode() === null) {
  170. throw new \InvalidArgumentException($this->l->t('Path should be set'));
  171. }
  172. // And it should be a file or a folder
  173. if (!($share->getNode() instanceof \OCP\Files\File) &&
  174. !($share->getNode() instanceof \OCP\Files\Folder)) {
  175. throw new \InvalidArgumentException($this->l->t('Path should be either a file or a folder'));
  176. }
  177. // And you cannot share your rootfolder
  178. if ($this->userManager->userExists($share->getSharedBy())) {
  179. $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
  180. } else {
  181. $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
  182. }
  183. if ($userFolder->getId() === $share->getNode()->getId()) {
  184. throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
  185. }
  186. // Check if we actually have share permissions
  187. if (!$share->getNode()->isShareable()) {
  188. throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
  189. }
  190. // Permissions should be set
  191. if ($share->getPermissions() === null) {
  192. throw new \InvalidArgumentException($this->l->t('A share requires permissions'));
  193. }
  194. $permissions = 0;
  195. $nodesForUser = $userFolder->getById($share->getNodeId());
  196. foreach ($nodesForUser as $node) {
  197. if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
  198. // for the root of non-movable mount, the permissions we see if limited by the mount itself,
  199. // so we instead use the "raw" permissions from the storage
  200. $permissions |= $node->getStorage()->getPermissions('');
  201. } else {
  202. $permissions |= $node->getPermissions();
  203. }
  204. }
  205. // Check that we do not share with more permissions than we have
  206. if ($share->getPermissions() & ~$permissions) {
  207. $path = $userFolder->getRelativePath($share->getNode()->getPath());
  208. throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
  209. }
  210. // Check that read permissions are always set
  211. // Link shares are allowed to have no read permissions to allow upload to hidden folders
  212. $noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
  213. || $share->getShareType() === IShare::TYPE_EMAIL;
  214. if (!$noReadPermissionRequired &&
  215. ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
  216. throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
  217. }
  218. if ($share->getNode() instanceof \OCP\Files\File) {
  219. if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
  220. throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
  221. }
  222. if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
  223. throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
  224. }
  225. }
  226. }
  227. /**
  228. * Validate if the expiration date fits the system settings
  229. *
  230. * @param IShare $share The share to validate the expiration date of
  231. * @return IShare The modified share object
  232. * @throws GenericShareException
  233. * @throws \InvalidArgumentException
  234. * @throws \Exception
  235. */
  236. protected function validateExpirationDateInternal(IShare $share) {
  237. $isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
  238. $expirationDate = $share->getExpirationDate();
  239. if ($isRemote) {
  240. $defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
  241. $defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
  242. $configProp = 'remote_defaultExpDays';
  243. $isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
  244. } else {
  245. $defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
  246. $defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
  247. $configProp = 'internal_defaultExpDays';
  248. $isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
  249. }
  250. // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
  251. // Then skip expiration date validation as null is accepted
  252. if (!$share->getNoExpirationDate() || $isEnforced) {
  253. if ($expirationDate !== null) {
  254. $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
  255. $expirationDate->setTime(0, 0, 0);
  256. $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  257. $date->setTime(0, 0, 0);
  258. if ($date >= $expirationDate) {
  259. throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
  260. }
  261. }
  262. // If expiredate is empty set a default one if there is a default
  263. $fullId = null;
  264. try {
  265. $fullId = $share->getFullId();
  266. } catch (\UnexpectedValueException $e) {
  267. // This is a new share
  268. }
  269. if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
  270. $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  271. $expirationDate->setTime(0, 0, 0);
  272. $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
  273. if ($days > $defaultExpireDays) {
  274. $days = $defaultExpireDays;
  275. }
  276. $expirationDate->add(new \DateInterval('P' . $days . 'D'));
  277. }
  278. // If we enforce the expiration date check that is does not exceed
  279. if ($isEnforced) {
  280. if (empty($expirationDate)) {
  281. throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
  282. }
  283. $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  284. $date->setTime(0, 0, 0);
  285. $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
  286. if ($date < $expirationDate) {
  287. throw new GenericShareException($this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays), code: 404);
  288. }
  289. }
  290. }
  291. $accepted = true;
  292. $message = '';
  293. \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
  294. 'expirationDate' => &$expirationDate,
  295. 'accepted' => &$accepted,
  296. 'message' => &$message,
  297. 'passwordSet' => $share->getPassword() !== null,
  298. ]);
  299. if (!$accepted) {
  300. throw new \Exception($message);
  301. }
  302. $share->setExpirationDate($expirationDate);
  303. return $share;
  304. }
  305. /**
  306. * Validate if the expiration date fits the system settings
  307. *
  308. * @param IShare $share The share to validate the expiration date of
  309. * @return IShare The modified share object
  310. * @throws GenericShareException
  311. * @throws \InvalidArgumentException
  312. * @throws \Exception
  313. */
  314. protected function validateExpirationDateLink(IShare $share) {
  315. $expirationDate = $share->getExpirationDate();
  316. $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
  317. // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
  318. // Then skip expiration date validation as null is accepted
  319. if (!($share->getNoExpirationDate() && !$isEnforced)) {
  320. if ($expirationDate !== null) {
  321. $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
  322. $expirationDate->setTime(0, 0, 0);
  323. $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  324. $date->setTime(0, 0, 0);
  325. if ($date >= $expirationDate) {
  326. throw new GenericShareException($this->l->t('Expiration date is in the past'), code: 404);
  327. }
  328. }
  329. // If expiredate is empty set a default one if there is a default
  330. $fullId = null;
  331. try {
  332. $fullId = $share->getFullId();
  333. } catch (\UnexpectedValueException $e) {
  334. // This is a new share
  335. }
  336. if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
  337. $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  338. $expirationDate->setTime(0, 0, 0);
  339. $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
  340. if ($days > $this->shareApiLinkDefaultExpireDays()) {
  341. $days = $this->shareApiLinkDefaultExpireDays();
  342. }
  343. $expirationDate->add(new \DateInterval('P' . $days . 'D'));
  344. }
  345. // If we enforce the expiration date check that is does not exceed
  346. if ($isEnforced) {
  347. if (empty($expirationDate)) {
  348. throw new \InvalidArgumentException($this->l->t('Expiration date is enforced'));
  349. }
  350. $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
  351. $date->setTime(0, 0, 0);
  352. $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
  353. if ($date < $expirationDate) {
  354. throw new GenericShareException(
  355. $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays()),
  356. code: 404,
  357. );
  358. }
  359. }
  360. }
  361. $accepted = true;
  362. $message = '';
  363. \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
  364. 'expirationDate' => &$expirationDate,
  365. 'accepted' => &$accepted,
  366. 'message' => &$message,
  367. 'passwordSet' => $share->getPassword() !== null,
  368. ]);
  369. if (!$accepted) {
  370. throw new \Exception($message);
  371. }
  372. $share->setExpirationDate($expirationDate);
  373. return $share;
  374. }
  375. /**
  376. * Check for pre share requirements for user shares
  377. *
  378. * @param IShare $share
  379. * @throws \Exception
  380. */
  381. protected function userCreateChecks(IShare $share) {
  382. // Check if we can share with group members only
  383. if ($this->shareWithGroupMembersOnly()) {
  384. $sharedBy = $this->userManager->get($share->getSharedBy());
  385. $sharedWith = $this->userManager->get($share->getSharedWith());
  386. // Verify we can share with this user
  387. $groups = array_intersect(
  388. $this->groupManager->getUserGroupIds($sharedBy),
  389. $this->groupManager->getUserGroupIds($sharedWith)
  390. );
  391. // optional excluded groups
  392. $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
  393. $groups = array_diff($groups, $excludedGroups);
  394. if (empty($groups)) {
  395. throw new \Exception($this->l->t('Sharing is only allowed with group members'));
  396. }
  397. }
  398. /*
  399. * TODO: Could be costly, fix
  400. *
  401. * Also this is not what we want in the future.. then we want to squash identical shares.
  402. */
  403. $provider = $this->factory->getProviderForType(IShare::TYPE_USER);
  404. $existingShares = $provider->getSharesByPath($share->getNode());
  405. foreach ($existingShares as $existingShare) {
  406. // Ignore if it is the same share
  407. try {
  408. if ($existingShare->getFullId() === $share->getFullId()) {
  409. continue;
  410. }
  411. } catch (\UnexpectedValueException $e) {
  412. //Shares are not identical
  413. }
  414. // Identical share already exists
  415. if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
  416. throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
  417. }
  418. // The share is already shared with this user via a group share
  419. if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
  420. $group = $this->groupManager->get($existingShare->getSharedWith());
  421. if (!is_null($group)) {
  422. $user = $this->userManager->get($share->getSharedWith());
  423. if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
  424. throw new AlreadySharedException($this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]), $existingShare);
  425. }
  426. }
  427. }
  428. }
  429. }
  430. /**
  431. * Check for pre share requirements for group shares
  432. *
  433. * @param IShare $share
  434. * @throws \Exception
  435. */
  436. protected function groupCreateChecks(IShare $share) {
  437. // Verify group shares are allowed
  438. if (!$this->allowGroupSharing()) {
  439. throw new \Exception($this->l->t('Group sharing is now allowed'));
  440. }
  441. // Verify if the user can share with this group
  442. if ($this->shareWithGroupMembersOnly()) {
  443. $sharedBy = $this->userManager->get($share->getSharedBy());
  444. $sharedWith = $this->groupManager->get($share->getSharedWith());
  445. // optional excluded groups
  446. $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
  447. if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
  448. throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
  449. }
  450. }
  451. /*
  452. * TODO: Could be costly, fix
  453. *
  454. * Also this is not what we want in the future.. then we want to squash identical shares.
  455. */
  456. $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
  457. $existingShares = $provider->getSharesByPath($share->getNode());
  458. foreach ($existingShares as $existingShare) {
  459. try {
  460. if ($existingShare->getFullId() === $share->getFullId()) {
  461. continue;
  462. }
  463. } catch (\UnexpectedValueException $e) {
  464. //It is a new share so just continue
  465. }
  466. if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
  467. throw new AlreadySharedException($this->l->t('Path is already shared with this group'), $existingShare);
  468. }
  469. }
  470. }
  471. /**
  472. * Check for pre share requirements for link shares
  473. *
  474. * @param IShare $share
  475. * @throws \Exception
  476. */
  477. protected function linkCreateChecks(IShare $share) {
  478. // Are link shares allowed?
  479. if (!$this->shareApiAllowLinks()) {
  480. throw new \Exception($this->l->t('Link sharing is not allowed'));
  481. }
  482. // Check if public upload is allowed
  483. if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
  484. ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
  485. throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
  486. }
  487. }
  488. /**
  489. * To make sure we don't get invisible link shares we set the parent
  490. * of a link if it is a reshare. This is a quick word around
  491. * until we can properly display multiple link shares in the UI
  492. *
  493. * See: https://github.com/owncloud/core/issues/22295
  494. *
  495. * FIXME: Remove once multiple link shares can be properly displayed
  496. *
  497. * @param IShare $share
  498. */
  499. protected function setLinkParent(IShare $share) {
  500. // No sense in checking if the method is not there.
  501. if (method_exists($share, 'setParent')) {
  502. $storage = $share->getNode()->getStorage();
  503. if ($storage->instanceOfStorage(SharedStorage::class)) {
  504. /** @var \OCA\Files_Sharing\SharedStorage $storage */
  505. $share->setParent($storage->getShareId());
  506. }
  507. }
  508. }
  509. /**
  510. * @param File|Folder $path
  511. */
  512. protected function pathCreateChecks($path) {
  513. // Make sure that we do not share a path that contains a shared mountpoint
  514. if ($path instanceof \OCP\Files\Folder) {
  515. $mounts = $this->mountManager->findIn($path->getPath());
  516. foreach ($mounts as $mount) {
  517. if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
  518. throw new \InvalidArgumentException($this->l->t('Path contains files shared with you'));
  519. }
  520. }
  521. }
  522. }
  523. /**
  524. * Check if the user that is sharing can actually share
  525. *
  526. * @param IShare $share
  527. * @throws \Exception
  528. */
  529. protected function canShare(IShare $share) {
  530. if (!$this->shareApiEnabled()) {
  531. throw new \Exception($this->l->t('Sharing is disabled'));
  532. }
  533. if ($this->sharingDisabledForUser($share->getSharedBy())) {
  534. throw new \Exception($this->l->t('Sharing is disabled for you'));
  535. }
  536. }
  537. /**
  538. * Share a path
  539. *
  540. * @param IShare $share
  541. * @return IShare The share object
  542. * @throws \Exception
  543. *
  544. * TODO: handle link share permissions or check them
  545. */
  546. public function createShare(IShare $share) {
  547. $this->canShare($share);
  548. $this->generalCreateChecks($share);
  549. // Verify if there are any issues with the path
  550. $this->pathCreateChecks($share->getNode());
  551. /*
  552. * On creation of a share the owner is always the owner of the path
  553. * Except for mounted federated shares.
  554. */
  555. $storage = $share->getNode()->getStorage();
  556. if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
  557. $parent = $share->getNode()->getParent();
  558. while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
  559. $parent = $parent->getParent();
  560. }
  561. $share->setShareOwner($parent->getOwner()->getUID());
  562. } else {
  563. if ($share->getNode()->getOwner()) {
  564. $share->setShareOwner($share->getNode()->getOwner()->getUID());
  565. } else {
  566. $share->setShareOwner($share->getSharedBy());
  567. }
  568. }
  569. try {
  570. // Verify share type
  571. if ($share->getShareType() === IShare::TYPE_USER) {
  572. $this->userCreateChecks($share);
  573. // Verify the expiration date
  574. $share = $this->validateExpirationDateInternal($share);
  575. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  576. $this->groupCreateChecks($share);
  577. // Verify the expiration date
  578. $share = $this->validateExpirationDateInternal($share);
  579. } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
  580. // Verify the expiration date
  581. $share = $this->validateExpirationDateInternal($share);
  582. } elseif ($share->getShareType() === IShare::TYPE_LINK
  583. || $share->getShareType() === IShare::TYPE_EMAIL) {
  584. $this->linkCreateChecks($share);
  585. $this->setLinkParent($share);
  586. // For now ignore a set token.
  587. $share->setToken(
  588. $this->secureRandom->generate(
  589. \OC\Share\Constants::TOKEN_LENGTH,
  590. \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
  591. )
  592. );
  593. // Verify the expiration date
  594. $share = $this->validateExpirationDateLink($share);
  595. // Verify the password
  596. $this->verifyPassword($share->getPassword());
  597. // If a password is set. Hash it!
  598. if ($share->getShareType() === IShare::TYPE_LINK
  599. && $share->getPassword() !== null) {
  600. $share->setPassword($this->hasher->hash($share->getPassword()));
  601. }
  602. }
  603. // Cannot share with the owner
  604. if ($share->getShareType() === IShare::TYPE_USER &&
  605. $share->getSharedWith() === $share->getShareOwner()) {
  606. throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
  607. }
  608. // Generate the target
  609. $defaultShareFolder = $this->config->getSystemValue('share_folder', '/');
  610. $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
  611. if ($allowCustomShareFolder) {
  612. $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $defaultShareFolder);
  613. } else {
  614. $shareFolder = $defaultShareFolder;
  615. }
  616. $target = $shareFolder . '/' . $share->getNode()->getName();
  617. $target = \OC\Files\Filesystem::normalizePath($target);
  618. $share->setTarget($target);
  619. // Pre share event
  620. $event = new Share\Events\BeforeShareCreatedEvent($share);
  621. $this->dispatcher->dispatchTyped($event);
  622. if ($event->isPropagationStopped() && $event->getError()) {
  623. throw new \Exception($event->getError());
  624. }
  625. $oldShare = $share;
  626. $provider = $this->factory->getProviderForType($share->getShareType());
  627. $share = $provider->create($share);
  628. // Reuse the node we already have
  629. $share->setNode($oldShare->getNode());
  630. // Reset the target if it is null for the new share
  631. if ($share->getTarget() === '') {
  632. $share->setTarget($target);
  633. }
  634. } catch (AlreadySharedException $e) {
  635. // If a share for the same target already exists, dont create a new one,
  636. // but do trigger the hooks and notifications again
  637. $oldShare = $share;
  638. // Reuse the node we already have
  639. $share = $e->getExistingShare();
  640. $share->setNode($oldShare->getNode());
  641. }
  642. // Post share event
  643. $this->dispatcher->dispatchTyped(new ShareCreatedEvent($share));
  644. // Send email if needed
  645. if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)) {
  646. if ($share->getMailSend()) {
  647. $provider = $this->factory->getProviderForType($share->getShareType());
  648. if ($provider instanceof IShareProviderWithNotification) {
  649. $provider->sendMailNotification($share);
  650. } else {
  651. $this->logger->debug('Share notification not sent because the provider does not support it.', ['app' => 'share']);
  652. }
  653. } else {
  654. $this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
  655. }
  656. } else {
  657. $this->logger->debug('Share notification not sent because sharing notification emails is disabled.', ['app' => 'share']);
  658. }
  659. return $share;
  660. }
  661. /**
  662. * Update a share
  663. *
  664. * @param IShare $share
  665. * @return IShare The share object
  666. * @throws \InvalidArgumentException
  667. */
  668. public function updateShare(IShare $share) {
  669. $expirationDateUpdated = false;
  670. $this->canShare($share);
  671. try {
  672. $originalShare = $this->getShareById($share->getFullId());
  673. } catch (\UnexpectedValueException $e) {
  674. throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
  675. }
  676. // We cannot change the share type!
  677. if ($share->getShareType() !== $originalShare->getShareType()) {
  678. throw new \InvalidArgumentException($this->l->t('Cannot change share type'));
  679. }
  680. // We can only change the recipient on user shares
  681. if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
  682. $share->getShareType() !== IShare::TYPE_USER) {
  683. throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
  684. }
  685. // Cannot share with the owner
  686. if ($share->getShareType() === IShare::TYPE_USER &&
  687. $share->getSharedWith() === $share->getShareOwner()) {
  688. throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
  689. }
  690. $this->generalCreateChecks($share, true);
  691. if ($share->getShareType() === IShare::TYPE_USER) {
  692. $this->userCreateChecks($share);
  693. if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
  694. // Verify the expiration date
  695. $this->validateExpirationDateInternal($share);
  696. $expirationDateUpdated = true;
  697. }
  698. } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
  699. $this->groupCreateChecks($share);
  700. if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
  701. // Verify the expiration date
  702. $this->validateExpirationDateInternal($share);
  703. $expirationDateUpdated = true;
  704. }
  705. } elseif ($share->getShareType() === IShare::TYPE_LINK
  706. || $share->getShareType() === IShare::TYPE_EMAIL) {
  707. $this->linkCreateChecks($share);
  708. // The new password is not set again if it is the same as the old
  709. // one, unless when switching from sending by Talk to sending by
  710. // mail.
  711. $plainTextPassword = $share->getPassword();
  712. $updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
  713. /**
  714. * Cannot enable the getSendPasswordByTalk if there is no password set
  715. */
  716. if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
  717. throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk with an empty password'));
  718. }
  719. /**
  720. * If we're in a mail share, we need to force a password change
  721. * as either the user is not aware of the password or is already (received by mail)
  722. * Thus the SendPasswordByTalk feature would not make sense
  723. */
  724. if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
  725. if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
  726. throw new \InvalidArgumentException($this->l->t('Cannot enable sending the password by Talk without setting a new password'));
  727. }
  728. if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
  729. throw new \InvalidArgumentException($this->l->t('Cannot disable sending the password by Talk without setting a new password'));
  730. }
  731. }
  732. if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
  733. // Verify the expiration date
  734. $this->validateExpirationDateLink($share);
  735. $expirationDateUpdated = true;
  736. }
  737. } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
  738. if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
  739. // Verify the expiration date
  740. $this->validateExpirationDateInternal($share);
  741. $expirationDateUpdated = true;
  742. }
  743. }
  744. $this->pathCreateChecks($share->getNode());
  745. // Now update the share!
  746. $provider = $this->factory->getProviderForType($share->getShareType());
  747. if ($share->getShareType() === IShare::TYPE_EMAIL) {
  748. $share = $provider->update($share, $plainTextPassword);
  749. } else {
  750. $share = $provider->update($share);
  751. }
  752. if ($expirationDateUpdated === true) {
  753. \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
  754. 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
  755. 'itemSource' => $share->getNode()->getId(),
  756. 'date' => $share->getExpirationDate(),
  757. 'uidOwner' => $share->getSharedBy(),
  758. ]);
  759. }
  760. if ($share->getPassword() !== $originalShare->getPassword()) {
  761. \OC_Hook::emit(Share::class, 'post_update_password', [
  762. 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
  763. 'itemSource' => $share->getNode()->getId(),
  764. 'uidOwner' => $share->getSharedBy(),
  765. 'token' => $share->getToken(),
  766. 'disabled' => is_null($share->getPassword()),
  767. ]);
  768. }
  769. if ($share->getPermissions() !== $originalShare->getPermissions()) {
  770. if ($this->userManager->userExists($share->getShareOwner())) {
  771. $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
  772. } else {
  773. $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
  774. }
  775. \OC_Hook::emit(Share::class, 'post_update_permissions', [
  776. 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
  777. 'itemSource' => $share->getNode()->getId(),
  778. 'shareType' => $share->getShareType(),
  779. 'shareWith' => $share->getSharedWith(),
  780. 'uidOwner' => $share->getSharedBy(),
  781. 'permissions' => $share->getPermissions(),
  782. 'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
  783. 'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
  784. ]);
  785. }
  786. return $share;
  787. }
  788. /**
  789. * Accept a share.
  790. *
  791. * @param IShare $share
  792. * @param string $recipientId
  793. * @return IShare The share object
  794. * @throws \InvalidArgumentException Thrown if the provider does not implement `IShareProviderSupportsAccept`
  795. * @since 9.0.0
  796. */
  797. public function acceptShare(IShare $share, string $recipientId): IShare {
  798. [$providerId,] = $this->splitFullId($share->getFullId());
  799. $provider = $this->factory->getProvider($providerId);
  800. if (!($provider instanceof IShareProviderSupportsAccept)) {
  801. throw new \InvalidArgumentException($this->l->t('Share provider does not support accepting'));
  802. }
  803. /** @var IShareProvider&IShareProviderSupportsAccept $provider */
  804. $provider->acceptShare($share, $recipientId);
  805. $event = new ShareAcceptedEvent($share);
  806. $this->dispatcher->dispatchTyped($event);
  807. return $share;
  808. }
  809. /**
  810. * Updates the password of the given share if it is not the same as the
  811. * password of the original share.
  812. *
  813. * @param IShare $share the share to update its password.
  814. * @param IShare $originalShare the original share to compare its
  815. * password with.
  816. * @return boolean whether the password was updated or not.
  817. */
  818. private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
  819. $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
  820. (($share->getPassword() !== null && $originalShare->getPassword() === null) ||
  821. ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
  822. ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
  823. !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
  824. // Password updated.
  825. if ($passwordsAreDifferent) {
  826. // Verify the password
  827. $this->verifyPassword($share->getPassword());
  828. // If a password is set. Hash it!
  829. if (!empty($share->getPassword())) {
  830. $share->setPassword($this->hasher->hash($share->getPassword()));
  831. if ($share->getShareType() === IShare::TYPE_EMAIL) {
  832. // Shares shared by email have temporary passwords
  833. $this->setSharePasswordExpirationTime($share);
  834. }
  835. return true;
  836. } else {
  837. // Empty string and null are seen as NOT password protected
  838. $share->setPassword(null);
  839. if ($share->getShareType() === IShare::TYPE_EMAIL) {
  840. $share->setPasswordExpirationTime(null);
  841. }
  842. return true;
  843. }
  844. } else {
  845. // Reset the password to the original one, as it is either the same
  846. // as the "new" password or a hashed version of it.
  847. $share->setPassword($originalShare->getPassword());
  848. }
  849. return false;
  850. }
  851. /**
  852. * Set the share's password expiration time
  853. */
  854. private function setSharePasswordExpirationTime(IShare $share): void {
  855. if (!$this->config->getSystemValueBool('sharing.enable_mail_link_password_expiration', false)) {
  856. // Sets password expiration date to NULL
  857. $share->setPasswordExpirationTime();
  858. return;
  859. }
  860. // Sets password expiration date
  861. $expirationTime = null;
  862. $now = new \DateTime();
  863. $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
  864. $expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
  865. $share->setPasswordExpirationTime($expirationTime);
  866. }
  867. /**
  868. * Delete all the children of this share
  869. * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
  870. *
  871. * @param IShare $share
  872. * @return IShare[] List of deleted shares
  873. */
  874. protected function deleteChildren(IShare $share) {
  875. $deletedShares = [];
  876. $provider = $this->factory->getProviderForType($share->getShareType());
  877. foreach ($provider->getChildren($share) as $child) {
  878. $this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($child));
  879. $deletedChildren = $this->deleteChildren($child);
  880. $deletedShares = array_merge($deletedShares, $deletedChildren);
  881. $provider->delete($child);
  882. $this->dispatcher->dispatchTyped(new ShareDeletedEvent($child));
  883. $deletedShares[] = $child;
  884. }
  885. return $deletedShares;
  886. }
  887. /**
  888. * Delete a share
  889. *
  890. * @param IShare $share
  891. * @throws ShareNotFound
  892. * @throws \InvalidArgumentException
  893. */
  894. public function deleteShare(IShare $share) {
  895. try {
  896. $share->getFullId();
  897. } catch (\UnexpectedValueException $e) {
  898. throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
  899. }
  900. $this->dispatcher->dispatchTyped(new BeforeShareDeletedEvent($share));
  901. // Get all children and delete them as well
  902. $this->deleteChildren($share);
  903. // Do the actual delete
  904. $provider = $this->factory->getProviderForType($share->getShareType());
  905. $provider->delete($share);
  906. $this->dispatcher->dispatchTyped(new ShareDeletedEvent($share));
  907. }
  908. /**
  909. * Unshare a file as the recipient.
  910. * This can be different from a regular delete for example when one of
  911. * the users in a groups deletes that share. But the provider should
  912. * handle this.
  913. *
  914. * @param IShare $share
  915. * @param string $recipientId
  916. */
  917. public function deleteFromSelf(IShare $share, $recipientId) {
  918. [$providerId,] = $this->splitFullId($share->getFullId());
  919. $provider = $this->factory->getProvider($providerId);
  920. $provider->deleteFromSelf($share, $recipientId);
  921. $event = new ShareDeletedFromSelfEvent($share);
  922. $this->dispatcher->dispatchTyped($event);
  923. }
  924. public function restoreShare(IShare $share, string $recipientId): IShare {
  925. [$providerId,] = $this->splitFullId($share->getFullId());
  926. $provider = $this->factory->getProvider($providerId);
  927. return $provider->restore($share, $recipientId);
  928. }
  929. /**
  930. * @inheritdoc
  931. */
  932. public function moveShare(IShare $share, $recipientId) {
  933. if ($share->getShareType() === IShare::TYPE_LINK
  934. || $share->getShareType() === IShare::TYPE_EMAIL) {
  935. throw new \InvalidArgumentException($this->l->t('Cannot change target of link share'));
  936. }
  937. if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
  938. throw new \InvalidArgumentException($this->l->t('Invalid recipient'));
  939. }
  940. if ($share->getShareType() === IShare::TYPE_GROUP) {
  941. $sharedWith = $this->groupManager->get($share->getSharedWith());
  942. if (is_null($sharedWith)) {
  943. throw new \InvalidArgumentException($this->l->t('Group "%s" does not exist', [$share->getSharedWith()]));
  944. }
  945. $recipient = $this->userManager->get($recipientId);
  946. if (!$sharedWith->inGroup($recipient)) {
  947. throw new \InvalidArgumentException($this->l->t('Invalid recipient'));
  948. }
  949. }
  950. [$providerId,] = $this->splitFullId($share->getFullId());
  951. $provider = $this->factory->getProvider($providerId);
  952. return $provider->move($share, $recipientId);
  953. }
  954. public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
  955. $providers = $this->factory->getAllProviders();
  956. if (!$shallow) {
  957. throw new \Exception('non-shallow getSharesInFolder is no longer supported');
  958. }
  959. return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
  960. $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
  961. foreach ($newShares as $fid => $data) {
  962. if (!isset($shares[$fid])) {
  963. $shares[$fid] = [];
  964. }
  965. $shares[$fid] = array_merge($shares[$fid], $data);
  966. }
  967. return $shares;
  968. }, []);
  969. }
  970. /**
  971. * @inheritdoc
  972. */
  973. public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
  974. if ($path !== null &&
  975. !($path instanceof \OCP\Files\File) &&
  976. !($path instanceof \OCP\Files\Folder)) {
  977. throw new \InvalidArgumentException($this->l->t('Invalid path'));
  978. }
  979. try {
  980. $provider = $this->factory->getProviderForType($shareType);
  981. } catch (ProviderException $e) {
  982. return [];
  983. }
  984. $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
  985. /*
  986. * Work around so we don't return expired shares but still follow
  987. * proper pagination.
  988. */
  989. $shares2 = [];
  990. while (true) {
  991. $added = 0;
  992. foreach ($shares as $share) {
  993. try {
  994. $this->checkShare($share);
  995. } catch (ShareNotFound $e) {
  996. // Ignore since this basically means the share is deleted
  997. continue;
  998. }
  999. $added++;
  1000. $shares2[] = $share;
  1001. if (count($shares2) === $limit) {
  1002. break;
  1003. }
  1004. }
  1005. // If we did not fetch more shares than the limit then there are no more shares
  1006. if (count($shares) < $limit) {
  1007. break;
  1008. }
  1009. if (count($shares2) === $limit) {
  1010. break;
  1011. }
  1012. // If there was no limit on the select we are done
  1013. if ($limit === -1) {
  1014. break;
  1015. }
  1016. $offset += $added;
  1017. // Fetch again $limit shares
  1018. $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
  1019. // No more shares means we are done
  1020. if (empty($shares)) {
  1021. break;
  1022. }
  1023. }
  1024. $shares = $shares2;
  1025. return $shares;
  1026. }
  1027. /**
  1028. * @inheritdoc
  1029. */
  1030. public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
  1031. try {
  1032. $provider = $this->factory->getProviderForType($shareType);
  1033. } catch (ProviderException $e) {
  1034. return [];
  1035. }
  1036. $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
  1037. // remove all shares which are already expired
  1038. foreach ($shares as $key => $share) {
  1039. try {
  1040. $this->checkShare($share);
  1041. } catch (ShareNotFound $e) {
  1042. unset($shares[$key]);
  1043. }
  1044. }
  1045. return $shares;
  1046. }
  1047. /**
  1048. * @inheritdoc
  1049. */
  1050. public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
  1051. $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
  1052. // Only get deleted shares
  1053. $shares = array_filter($shares, function (IShare $share) {
  1054. return $share->getPermissions() === 0;
  1055. });
  1056. // Only get shares where the owner still exists
  1057. $shares = array_filter($shares, function (IShare $share) {
  1058. return $this->userManager->userExists($share->getShareOwner());
  1059. });
  1060. return $shares;
  1061. }
  1062. /**
  1063. * @inheritdoc
  1064. */
  1065. public function getShareById($id, $recipient = null) {
  1066. if ($id === null) {
  1067. throw new ShareNotFound();
  1068. }
  1069. [$providerId, $id] = $this->splitFullId($id);
  1070. try {
  1071. $provider = $this->factory->getProvider($providerId);
  1072. } catch (ProviderException $e) {
  1073. throw new ShareNotFound();
  1074. }
  1075. $share = $provider->getShareById($id, $recipient);
  1076. $this->checkShare($share);
  1077. return $share;
  1078. }
  1079. /**
  1080. * Get all the shares for a given path
  1081. *
  1082. * @param \OCP\Files\Node $path
  1083. * @param int $page
  1084. * @param int $perPage
  1085. *
  1086. * @return Share[]
  1087. */
  1088. public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
  1089. return [];
  1090. }
  1091. /**
  1092. * Get the share by token possible with password
  1093. *
  1094. * @param string $token
  1095. * @return IShare
  1096. *
  1097. * @throws ShareNotFound
  1098. */
  1099. public function getShareByToken($token) {
  1100. // tokens cannot be valid local user names
  1101. if ($this->userManager->userExists($token)) {
  1102. throw new ShareNotFound();
  1103. }
  1104. $share = null;
  1105. try {
  1106. if ($this->shareApiAllowLinks()) {
  1107. $provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
  1108. $share = $provider->getShareByToken($token);
  1109. }
  1110. } catch (ProviderException $e) {
  1111. } catch (ShareNotFound $e) {
  1112. }
  1113. // If it is not a link share try to fetch a federated share by token
  1114. if ($share === null) {
  1115. try {
  1116. $provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
  1117. $share = $provider->getShareByToken($token);
  1118. } catch (ProviderException $e) {
  1119. } catch (ShareNotFound $e) {
  1120. }
  1121. }
  1122. // If it is not a link share try to fetch a mail share by token
  1123. if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
  1124. try {
  1125. $provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
  1126. $share = $provider->getShareByToken($token);
  1127. } catch (ProviderException $e) {
  1128. } catch (ShareNotFound $e) {
  1129. }
  1130. }
  1131. if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
  1132. try {
  1133. $provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
  1134. $share = $provider->getShareByToken($token);
  1135. } catch (ProviderException $e) {
  1136. } catch (ShareNotFound $e) {
  1137. }
  1138. }
  1139. if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
  1140. try {
  1141. $provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
  1142. $share = $provider->getShareByToken($token);
  1143. } catch (ProviderException $e) {
  1144. } catch (ShareNotFound $e) {
  1145. }
  1146. }
  1147. if ($share === null) {
  1148. throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
  1149. }
  1150. $this->checkShare($share);
  1151. /*
  1152. * Reduce the permissions for link or email shares if public upload is not enabled
  1153. */
  1154. if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
  1155. && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
  1156. $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
  1157. }
  1158. return $share;
  1159. }
  1160. /**
  1161. * Check expire date and disabled owner
  1162. *
  1163. * @throws ShareNotFound
  1164. */
  1165. protected function checkShare(IShare $share): void {
  1166. if ($share->isExpired()) {
  1167. $this->deleteShare($share);
  1168. throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
  1169. }
  1170. if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
  1171. $uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
  1172. foreach ($uids as $uid) {
  1173. $user = $this->userManager->get($uid);
  1174. if ($user?->isEnabled() === false) {
  1175. throw new ShareNotFound($this->l->t('The requested share comes from a disabled user'));
  1176. }
  1177. }
  1178. }
  1179. }
  1180. /**
  1181. * Verify the password of a public share
  1182. *
  1183. * @param IShare $share
  1184. * @param ?string $password
  1185. * @return bool
  1186. */
  1187. public function checkPassword(IShare $share, $password) {
  1188. // if there is no password on the share object / passsword is null, there is nothing to check
  1189. if ($password === null || $share->getPassword() === null) {
  1190. return false;
  1191. }
  1192. // Makes sure password hasn't expired
  1193. $expirationTime = $share->getPasswordExpirationTime();
  1194. if ($expirationTime !== null && $expirationTime < new \DateTime()) {
  1195. return false;
  1196. }
  1197. $newHash = '';
  1198. if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
  1199. return false;
  1200. }
  1201. if (!empty($newHash)) {
  1202. $share->setPassword($newHash);
  1203. $provider = $this->factory->getProviderForType($share->getShareType());
  1204. $provider->update($share);
  1205. }
  1206. return true;
  1207. }
  1208. /**
  1209. * @inheritdoc
  1210. */
  1211. public function userDeleted($uid) {
  1212. $types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
  1213. foreach ($types as $type) {
  1214. try {
  1215. $provider = $this->factory->getProviderForType($type);
  1216. } catch (ProviderException $e) {
  1217. continue;
  1218. }
  1219. $provider->userDeleted($uid, $type);
  1220. }
  1221. }
  1222. /**
  1223. * @inheritdoc
  1224. */
  1225. public function groupDeleted($gid) {
  1226. $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
  1227. $provider->groupDeleted($gid);
  1228. $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
  1229. if ($excludedGroups === '') {
  1230. return;
  1231. }
  1232. $excludedGroups = json_decode($excludedGroups, true);
  1233. if (json_last_error() !== JSON_ERROR_NONE) {
  1234. return;
  1235. }
  1236. $excludedGroups = array_diff($excludedGroups, [$gid]);
  1237. $this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
  1238. }
  1239. /**
  1240. * @inheritdoc
  1241. */
  1242. public function userDeletedFromGroup($uid, $gid) {
  1243. $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
  1244. $provider->userDeletedFromGroup($uid, $gid);
  1245. }
  1246. /**
  1247. * Get access list to a path. This means
  1248. * all the users that can access a given path.
  1249. *
  1250. * Consider:
  1251. * -root
  1252. * |-folder1 (23)
  1253. * |-folder2 (32)
  1254. * |-fileA (42)
  1255. *
  1256. * fileA is shared with user1 and user1@server1 and email1@maildomain1
  1257. * folder2 is shared with group2 (user4 is a member of group2)
  1258. * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
  1259. * and email2@maildomain2
  1260. *
  1261. * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
  1262. * [
  1263. * users => [
  1264. * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
  1265. * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
  1266. * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
  1267. * ],
  1268. * remote => [
  1269. * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
  1270. * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
  1271. * ],
  1272. * public => bool
  1273. * mail => [
  1274. * 'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
  1275. * 'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
  1276. * ]
  1277. * ]
  1278. *
  1279. * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
  1280. * [
  1281. * users => ['user1', 'user2', 'user4'],
  1282. * remote => bool,
  1283. * public => bool
  1284. * mail => ['email1@maildomain1', 'email2@maildomain2']
  1285. * ]
  1286. *
  1287. * This is required for encryption/activity
  1288. *
  1289. * @param \OCP\Files\Node $path
  1290. * @param bool $recursive Should we check all parent folders as well
  1291. * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
  1292. * @return array
  1293. */
  1294. public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
  1295. $owner = $path->getOwner();
  1296. if ($owner === null) {
  1297. return [];
  1298. }
  1299. $owner = $owner->getUID();
  1300. if ($currentAccess) {
  1301. $al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
  1302. } else {
  1303. $al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
  1304. }
  1305. if (!$this->userManager->userExists($owner)) {
  1306. return $al;
  1307. }
  1308. //Get node for the owner and correct the owner in case of external storage
  1309. $userFolder = $this->rootFolder->getUserFolder($owner);
  1310. if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
  1311. $path = $userFolder->getFirstNodeById($path->getId());
  1312. if ($path === null || $path->getOwner() === null) {
  1313. return [];
  1314. }
  1315. $owner = $path->getOwner()->getUID();
  1316. }
  1317. $providers = $this->factory->getAllProviders();
  1318. /** @var Node[] $nodes */
  1319. $nodes = [];
  1320. if ($currentAccess) {
  1321. $ownerPath = $path->getPath();
  1322. $ownerPath = explode('/', $ownerPath, 4);
  1323. if (count($ownerPath) < 4) {
  1324. $ownerPath = '';
  1325. } else {
  1326. $ownerPath = $ownerPath[3];
  1327. }
  1328. $al['users'][$owner] = [
  1329. 'node_id' => $path->getId(),
  1330. 'node_path' => '/' . $ownerPath,
  1331. ];
  1332. } else {
  1333. $al['users'][] = $owner;
  1334. }
  1335. // Collect all the shares
  1336. while ($path->getPath() !== $userFolder->getPath()) {
  1337. $nodes[] = $path;
  1338. if (!$recursive) {
  1339. break;
  1340. }
  1341. $path = $path->getParent();
  1342. }
  1343. foreach ($providers as $provider) {
  1344. $tmp = $provider->getAccessList($nodes, $currentAccess);
  1345. foreach ($tmp as $k => $v) {
  1346. if (isset($al[$k])) {
  1347. if (is_array($al[$k])) {
  1348. if ($currentAccess) {
  1349. $al[$k] += $v;
  1350. } else {
  1351. $al[$k] = array_merge($al[$k], $v);
  1352. $al[$k] = array_unique($al[$k]);
  1353. $al[$k] = array_values($al[$k]);
  1354. }
  1355. } else {
  1356. $al[$k] = $al[$k] || $v;
  1357. }
  1358. } else {
  1359. $al[$k] = $v;
  1360. }
  1361. }
  1362. }
  1363. return $al;
  1364. }
  1365. /**
  1366. * Create a new share
  1367. *
  1368. * @return IShare
  1369. */
  1370. public function newShare() {
  1371. return new \OC\Share20\Share($this->rootFolder, $this->userManager);
  1372. }
  1373. /**
  1374. * Is the share API enabled
  1375. *
  1376. * @return bool
  1377. */
  1378. public function shareApiEnabled() {
  1379. return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
  1380. }
  1381. /**
  1382. * Is public link sharing enabled
  1383. *
  1384. * @return bool
  1385. */
  1386. public function shareApiAllowLinks() {
  1387. if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
  1388. return false;
  1389. }
  1390. $user = $this->userSession->getUser();
  1391. if ($user) {
  1392. $excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
  1393. if ($excludedGroups) {
  1394. $userGroups = $this->groupManager->getUserGroupIds($user);
  1395. return !(bool)array_intersect($excludedGroups, $userGroups);
  1396. }
  1397. }
  1398. return true;
  1399. }
  1400. /**
  1401. * Is password on public link requires
  1402. *
  1403. * @param bool Check group membership exclusion
  1404. * @return bool
  1405. */
  1406. public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
  1407. $excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
  1408. if ($excludedGroups !== '' && $checkGroupMembership) {
  1409. $excludedGroups = json_decode($excludedGroups);
  1410. $user = $this->userSession->getUser();
  1411. if ($user) {
  1412. $userGroups = $this->groupManager->getUserGroupIds($user);
  1413. if ((bool)array_intersect($excludedGroups, $userGroups)) {
  1414. return false;
  1415. }
  1416. }
  1417. }
  1418. return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
  1419. }
  1420. /**
  1421. * Is default link expire date enabled
  1422. *
  1423. * @return bool
  1424. */
  1425. public function shareApiLinkDefaultExpireDate() {
  1426. return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
  1427. }
  1428. /**
  1429. * Is default link expire date enforced
  1430. *`
  1431. *
  1432. * @return bool
  1433. */
  1434. public function shareApiLinkDefaultExpireDateEnforced() {
  1435. return $this->shareApiLinkDefaultExpireDate() &&
  1436. $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
  1437. }
  1438. /**
  1439. * Number of default link expire days
  1440. *
  1441. * @return int
  1442. */
  1443. public function shareApiLinkDefaultExpireDays() {
  1444. return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
  1445. }
  1446. /**
  1447. * Is default internal expire date enabled
  1448. *
  1449. * @return bool
  1450. */
  1451. public function shareApiInternalDefaultExpireDate(): bool {
  1452. return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
  1453. }
  1454. /**
  1455. * Is default remote expire date enabled
  1456. *
  1457. * @return bool
  1458. */
  1459. public function shareApiRemoteDefaultExpireDate(): bool {
  1460. return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
  1461. }
  1462. /**
  1463. * Is default expire date enforced
  1464. *
  1465. * @return bool
  1466. */
  1467. public function shareApiInternalDefaultExpireDateEnforced(): bool {
  1468. return $this->shareApiInternalDefaultExpireDate() &&
  1469. $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
  1470. }
  1471. /**
  1472. * Is default expire date enforced for remote shares
  1473. *
  1474. * @return bool
  1475. */
  1476. public function shareApiRemoteDefaultExpireDateEnforced(): bool {
  1477. return $this->shareApiRemoteDefaultExpireDate() &&
  1478. $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
  1479. }
  1480. /**
  1481. * Number of default expire days
  1482. *
  1483. * @return int
  1484. */
  1485. public function shareApiInternalDefaultExpireDays(): int {
  1486. return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
  1487. }
  1488. /**
  1489. * Number of default expire days for remote shares
  1490. *
  1491. * @return int
  1492. */
  1493. public function shareApiRemoteDefaultExpireDays(): int {
  1494. return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
  1495. }
  1496. /**
  1497. * Allow public upload on link shares
  1498. *
  1499. * @return bool
  1500. */
  1501. public function shareApiLinkAllowPublicUpload() {
  1502. return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
  1503. }
  1504. /**
  1505. * check if user can only share with group members
  1506. *
  1507. * @return bool
  1508. */
  1509. public function shareWithGroupMembersOnly() {
  1510. return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
  1511. }
  1512. /**
  1513. * If shareWithGroupMembersOnly is enabled, return an optional
  1514. * list of groups that must be excluded from the principle of
  1515. * belonging to the same group.
  1516. *
  1517. * @return array
  1518. */
  1519. public function shareWithGroupMembersOnlyExcludeGroupsList() {
  1520. if (!$this->shareWithGroupMembersOnly()) {
  1521. return [];
  1522. }
  1523. $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
  1524. return json_decode($excludeGroups, true) ?? [];
  1525. }
  1526. /**
  1527. * Check if users can share with groups
  1528. *
  1529. * @return bool
  1530. */
  1531. public function allowGroupSharing() {
  1532. return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
  1533. }
  1534. public function allowEnumeration(): bool {
  1535. return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
  1536. }
  1537. public function limitEnumerationToGroups(): bool {
  1538. return $this->allowEnumeration() &&
  1539. $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
  1540. }
  1541. public function limitEnumerationToPhone(): bool {
  1542. return $this->allowEnumeration() &&
  1543. $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
  1544. }
  1545. public function allowEnumerationFullMatch(): bool {
  1546. return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
  1547. }
  1548. public function matchEmail(): bool {
  1549. return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
  1550. }
  1551. public function ignoreSecondDisplayName(): bool {
  1552. return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
  1553. }
  1554. public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
  1555. if ($this->allowEnumerationFullMatch()) {
  1556. return true;
  1557. }
  1558. if (!$this->allowEnumeration()) {
  1559. return false;
  1560. }
  1561. if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
  1562. // Enumeration is enabled and not restricted: OK
  1563. return true;
  1564. }
  1565. if (!$currentUser instanceof IUser) {
  1566. // Enumeration restrictions require an account
  1567. return false;
  1568. }
  1569. // Enumeration is limited to phone match
  1570. if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
  1571. return true;
  1572. }
  1573. // Enumeration is limited to groups
  1574. if ($this->limitEnumerationToGroups()) {
  1575. $currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
  1576. $targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
  1577. if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
  1578. return true;
  1579. }
  1580. }
  1581. return false;
  1582. }
  1583. /**
  1584. * Copied from \OC_Util::isSharingDisabledForUser
  1585. *
  1586. * TODO: Deprecate function from OC_Util
  1587. *
  1588. * @param string $userId
  1589. * @return bool
  1590. */
  1591. public function sharingDisabledForUser($userId) {
  1592. return $this->shareDisableChecker->sharingDisabledForUser($userId);
  1593. }
  1594. /**
  1595. * @inheritdoc
  1596. */
  1597. public function outgoingServer2ServerSharesAllowed() {
  1598. return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
  1599. }
  1600. /**
  1601. * @inheritdoc
  1602. */
  1603. public function outgoingServer2ServerGroupSharesAllowed() {
  1604. return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
  1605. }
  1606. /**
  1607. * @inheritdoc
  1608. */
  1609. public function shareProviderExists($shareType) {
  1610. try {
  1611. $this->factory->getProviderForType($shareType);
  1612. } catch (ProviderException $e) {
  1613. return false;
  1614. }
  1615. return true;
  1616. }
  1617. public function registerShareProvider(string $shareProviderClass): void {
  1618. $this->factory->registerProvider($shareProviderClass);
  1619. }
  1620. public function getAllShares(): iterable {
  1621. $providers = $this->factory->getAllProviders();
  1622. foreach ($providers as $provider) {
  1623. yield from $provider->getAllShares();
  1624. }
  1625. }
  1626. }