diff options
-rw-r--r-- | apps/files_sharing/lib/Controller/ShareAPIController.php | 16 | ||||
-rw-r--r-- | apps/files_sharing/lib/Notification/Listener.php | 5 | ||||
-rw-r--r-- | apps/files_sharing/lib/Notification/Notifier.php | 94 | ||||
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 125 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 24 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 12 | ||||
-rw-r--r-- | lib/public/Share/IShareProvider.php | 10 |
7 files changed, 230 insertions, 56 deletions
diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index acb95a0a3d3..9c5f6abee68 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -962,19 +962,17 @@ class ShareAPIController extends OCSController { throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); } - if (!$this->canAccessShare($share, false)) { + if (!$this->canAccessShare($share)) { throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); } - if ($share->getShareType() !== Share::SHARE_TYPE_USER || - $share->getSharedWith() !== $this->currentUser) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + if ($share->getShareType() !== IShare::TYPE_USER && + $share->getShareType() !== IShare::TYPE_GROUP) { + throw new OCSNotFoundException($this->l->t('Share type does not support accepting')); } - $share->setStatus(IShare::STATUS_ACCEPTED); - try { - $this->shareManager->updateShare($share); + $this->shareManager->acceptShare($share, $this->currentUser); } catch (GenericShareException $e) { $code = $e->getCode() === 0 ? 403 : $e->getCode(); throw new OCSException($e->getHint(), $code); @@ -1117,8 +1115,8 @@ class ShareAPIController extends OCSController { * @suppress PhanUndeclaredClassMethod */ protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool { - if ($share->getShareType() !== Share::SHARE_TYPE_GROUP && - $share->getShareType() !== Share::SHARE_TYPE_ROOM + if ($share->getShareType() !== IShare::TYPE_GROUP && + $share->getShareType() !== IShare::TYPE_ROOM ) { return false; } diff --git a/apps/files_sharing/lib/Notification/Listener.php b/apps/files_sharing/lib/Notification/Listener.php index b2e00613e70..fd4daca28e7 100644 --- a/apps/files_sharing/lib/Notification/Listener.php +++ b/apps/files_sharing/lib/Notification/Listener.php @@ -63,6 +63,11 @@ class Listener { $group = $this->groupManager->get($share->getSharedWith()); foreach ($group->getUsers() as $user) { + if ($user->getUID() === $share->getShareOwner() || + $user->getUID() === $share->getSharedBy()) { + continue; + } + $notification->setUser($user->getUID()); $this->notificationManager->notify($notification); } diff --git a/apps/files_sharing/lib/Notification/Notifier.php b/apps/files_sharing/lib/Notification/Notifier.php index 6ae009895d3..03d7038e6fe 100644 --- a/apps/files_sharing/lib/Notification/Notifier.php +++ b/apps/files_sharing/lib/Notification/Notifier.php @@ -28,7 +28,10 @@ namespace OCA\Files_Sharing\Notification; use OCP\Files\IRootFolder; use OCP\IL10N; +use OCP\IGroupManager; use OCP\IURLGenerator; +use OCP\IUser; +use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Notification\AlreadyProcessedException; use OCP\Notification\INotification; @@ -45,6 +48,10 @@ class Notifier implements INotifier { private $shareManager; /** @var IRootFolder */ private $rootFolder; + /** @var IGroupManager */ + protected $groupManager; + /** @var IUserManager */ + protected $userManager; /** @var IURLGenerator */ protected $url; @@ -52,10 +59,14 @@ class Notifier implements INotifier { public function __construct(IFactory $l10nFactory, IManager $shareManager, IRootFolder $rootFolder, + IGroupManager $groupManager, + IUserManager $userManager, IURLGenerator $url) { $this->l10nFactory = $l10nFactory; $this->shareManager = $shareManager; $this->rootFolder = $rootFolder; + $this->groupManager = $groupManager; + $this->userManager = $userManager; $this->url = $url; } @@ -135,11 +146,12 @@ class Notifier implements INotifier { } protected function parseShareInvitation(IShare $share, INotification $notification, IL10N $l): INotification { + if ($share->getShareType() === IShare::TYPE_USER) { - if ($share->getSharedWith() !== $notification->getUser()) { + if ($share->getStatus() !== IShare::STATUS_PENDING) { throw new AlreadyProcessedException(); } - + } else if ($share->getShareType() === IShare::TYPE_GROUP) { if ($share->getStatus() !== IShare::STATUS_PENDING) { throw new AlreadyProcessedException(); } @@ -147,6 +159,10 @@ class Notifier implements INotifier { switch ($notification->getSubject()) { case 'incoming_user_share': + if ($share->getSharedWith() !== $notification->getUser()) { + throw new AlreadyProcessedException(); + } + $subject = $l->t('You received {share} as a share from {user}'); $subjectParameters = [ 'share' => [ @@ -160,34 +176,70 @@ class Notifier implements INotifier { 'name' => $share->getShareOwner(), ], ]; + break; - $placeholders = $replacements = []; - foreach ($subjectParameters as $placeholder => $parameter) { - $placeholders[] = '{' . $placeholder . '}'; - $replacements[] = $parameter['name']; + case 'incoming_group_share': + $user = $this->userManager->get($notification->getUser()); + if (!$user instanceof IUser) { + throw new AlreadyProcessedException(); } - $notification->setParsedSubject(str_replace($placeholders, $replacements, $subject)) - ->setRichSubject($subject, $subjectParameters) - ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); - - $acceptAction = $notification->createAction(); - $acceptAction->setParsedLabel($l->t('Accept')) - ->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.acceptShare', ['id' => $share->getId()]), 'POST') - ->setPrimary(true); - $notification->addParsedAction($acceptAction); + $group = $this->groupManager->get($share->getSharedWith()); + if (!$group->inGroup($user)) { + throw new AlreadyProcessedException(); + } - $rejectAction = $notification->createAction(); - $rejectAction->setParsedLabel($l->t('Reject')) - ->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.deleteShare', ['id' => $share->getId()]), 'DELETE') - ->setPrimary(false); - $notification->addParsedAction($rejectAction); + if ($share->getPermissions() === 0) { + // Already rejected + throw new AlreadyProcessedException(); + } - return $notification; + $subject = $l->t('You received {share} to group {group} as a share from {user}'); + $subjectParameters = [ + 'share' => [ + 'type' => 'highlight', + 'id' => $notification->getObjectId(), + 'name' => $share->getNode()->getName(), + ], + 'group' => [ + 'type' => 'user-group', + 'id' => $group->getGID(), + 'name' => $group->getDisplayName(), + ], + 'user' => [ + 'type' => 'user', + 'id' => $share->getShareOwner(), + 'name' => $share->getShareOwner(), + ], + ]; break; default: throw new \InvalidArgumentException('Invalid subject'); } + + $placeholders = $replacements = []; + foreach ($subjectParameters as $placeholder => $parameter) { + $placeholders[] = '{' . $placeholder . '}'; + $replacements[] = $parameter['name']; + } + + $notification->setParsedSubject(str_replace($placeholders, $replacements, $subject)) + ->setRichSubject($subject, $subjectParameters) + ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); + + $acceptAction = $notification->createAction(); + $acceptAction->setParsedLabel($l->t('Accept')) + ->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.acceptShare', ['id' => $share->getId()]), 'POST') + ->setPrimary(true); + $notification->addParsedAction($acceptAction); + + $rejectAction = $notification->createAction(); + $rejectAction->setParsedLabel($l->t('Reject')) + ->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.deleteShare', ['id' => $share->getId()]), 'DELETE') + ->setPrimary(false); + $notification->addParsedAction($rejectAction); + + return $notification; } } diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 66d28869dbd..ffcaf4f1065 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -321,6 +321,71 @@ class DefaultShareProvider implements IShareProvider { } /** + * Accept a share. + * + * @param IShare $share + * @param string $recipient + * @return IShare The share object + * @since 9.0.0 + */ + public function acceptShare(IShare $share, string $recipient): IShare { + if ($share->getShareType() === IShare::TYPE_GROUP) { + $group = $this->groupManager->get($share->getSharedWith()); + $user = $this->userManager->get($recipient); + + if (is_null($group)) { + throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist'); + } + + if (!$group->inGroup($user)) { + throw new ProviderException('Recipient not in receiving group'); + } + + // Try to fetch user specific share + $qb = $this->dbConn->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) + ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->execute(); + + $data = $stmt->fetch(); + + /* + * Check if there already is a user specific group share. + * If there is update it (if required). + */ + if ($data === false) { + $id = $this->createUserSpecificGroupShare($share, $recipient); + } else { + $id = $data['id']; + } + + } else if ($share->getShareType() === IShare::TYPE_USER) { + if ($share->getSharedWith() !== $recipient) { + throw new ProviderException('Recipient does not match'); + } + + $id = $share->getId(); + } else { + throw new ProviderException('Invalid shareType'); + } + + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED)) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->execute(); + + return $share; + } + + /** * Get all children of this share * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * @@ -384,13 +449,13 @@ class DefaultShareProvider implements IShareProvider { * Unshare a share from the recipient. If this is a group share * this means we need a special entry in the share db. * - * @param \OCP\Share\IShare $share + * @param IShare $share * @param string $recipient UserId of recipient * @throws BackendError * @throws ProviderException */ - public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + public function deleteFromSelf(IShare $share, $recipient) { + if ($share->getShareType() === IShare::TYPE_GROUP) { $group = $this->groupManager->get($share->getSharedWith()); $user = $this->userManager->get($recipient); @@ -423,37 +488,23 @@ class DefaultShareProvider implements IShareProvider { * If there is update it (if required). */ if ($data === false) { - $qb = $this->dbConn->getQueryBuilder(); - - $type = $share->getNodeType(); - - //Insert new share - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), - 'share_with' => $qb->createNamedParameter($recipient), - 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), - 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), - 'parent' => $qb->createNamedParameter($share->getId()), - 'item_type' => $qb->createNamedParameter($type), - 'item_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_target' => $qb->createNamedParameter($share->getTarget()), - 'permissions' => $qb->createNamedParameter(0), - 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), - ])->execute(); - - } else if ($data['permissions'] !== 0) { + $id = $this->createUserSpecificGroupShare($share, $recipient); + $permissions = $share->getPermissions(); + } else { + $permissions = $data['permissions']; + $id = $data['id']; + } + if ($permissions !== 0) { // Update existing usergroup share $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createNamedParameter(0)) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->execute(); } - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + } else if ($share->getShareType() === IShare::TYPE_USER) { if ($share->getSharedWith() !== $recipient) { throw new ProviderException('Recipient does not match'); @@ -466,6 +517,28 @@ class DefaultShareProvider implements IShareProvider { } } + protected function createUserSpecificGroupShare(IShare $share, string $recipient): int { + $type = $share->getNodeType(); + + $qb = $this->dbConn->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), + 'share_with' => $qb->createNamedParameter($recipient), + 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), + 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), + 'parent' => $qb->createNamedParameter($share->getId()), + 'item_type' => $qb->createNamedParameter($type), + 'item_source' => $qb->createNamedParameter($share->getNodeId()), + 'file_source' => $qb->createNamedParameter($share->getNodeId()), + 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'permissions' => $qb->createNamedParameter($share->getPermissions()), + 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), + ])->execute(); + + return $qb->getLastInsertId(); + } + /** * @inheritdoc * diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 728dd60e759..ba370e7724a 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -929,6 +929,30 @@ class Manager implements IManager { } /** + * Accept a share. + * + * @param IShare $share + * @param string $recipientId + * @return IShare The share object + * @throws \InvalidArgumentException + * @since 9.0.0 + */ + public function acceptShare(IShare $share, string $recipientId): IShare { + [$providerId, ] = $this->splitFullId($share->getFullId()); + $provider = $this->factory->getProvider($providerId); + + if (!method_exists($provider, 'acceptShare')) { + // TODO FIX ME + throw new \InvalidArgumentException('not supported'); + } + $provider->acceptShare($share, $recipientId); + $event = new GenericEvent($share); + $this->eventDispatcher->dispatch('OCP\Share::postAcceptShare', $event); + + return $share; + } + + /** * Updates the password of the given share if it is not the same as the * password of the original share. * diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 8bb7291d6ba..3127c74be5e 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -54,6 +54,7 @@ interface IManager { * Update a share. * The target of the share can't be changed this way: use moveShare * The share can't be removed this way (permission 0): use deleteShare + * The state can't be changed this way: use acceptShare * * @param IShare $share * @return IShare The share object @@ -63,6 +64,17 @@ interface IManager { public function updateShare(IShare $share); /** + * Accept a share. + * + * @param IShare $share + * @param string $recipientId + * @return IShare The share object + * @throws \InvalidArgumentException + * @since 18.0.0 + */ + public function acceptShare(IShare $share, string $recipientId): IShare; + + /** * Delete a share * * @param IShare $share diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index c8815928269..49ab4bef3e6 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -64,6 +64,16 @@ interface IShareProvider { public function update(\OCP\Share\IShare $share); /** + * Accept a share. + * + * @param IShare $share + * @param string $recipient + * @return IShare The share object + * @since 17.0.0 + */ +// public function acceptShare(IShare $share, string $recipient): IShare; + + /** * Delete a share * * @param \OCP\Share\IShare $share |