Allow to accept group shares

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2019-09-04 16:50:52 +02:00
parent c79a56481b
commit 520042bbd0
No known key found for this signature in database
GPG Key ID: 7076EA9751AACDDA
7 changed files with 230 additions and 56 deletions

View File

@ -962,19 +962,17 @@ class ShareAPIController extends OCSController {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); 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')); throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
} }
if ($share->getShareType() !== Share::SHARE_TYPE_USER || if ($share->getShareType() !== IShare::TYPE_USER &&
$share->getSharedWith() !== $this->currentUser) { $share->getShareType() !== IShare::TYPE_GROUP) {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); throw new OCSNotFoundException($this->l->t('Share type does not support accepting'));
} }
$share->setStatus(IShare::STATUS_ACCEPTED);
try { try {
$this->shareManager->updateShare($share); $this->shareManager->acceptShare($share, $this->currentUser);
} catch (GenericShareException $e) { } catch (GenericShareException $e) {
$code = $e->getCode() === 0 ? 403 : $e->getCode(); $code = $e->getCode() === 0 ? 403 : $e->getCode();
throw new OCSException($e->getHint(), $code); throw new OCSException($e->getHint(), $code);
@ -1117,8 +1115,8 @@ class ShareAPIController extends OCSController {
* @suppress PhanUndeclaredClassMethod * @suppress PhanUndeclaredClassMethod
*/ */
protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool { protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
if ($share->getShareType() !== Share::SHARE_TYPE_GROUP && if ($share->getShareType() !== IShare::TYPE_GROUP &&
$share->getShareType() !== Share::SHARE_TYPE_ROOM $share->getShareType() !== IShare::TYPE_ROOM
) { ) {
return false; return false;
} }

View File

@ -63,6 +63,11 @@ class Listener {
$group = $this->groupManager->get($share->getSharedWith()); $group = $this->groupManager->get($share->getSharedWith());
foreach ($group->getUsers() as $user) { foreach ($group->getUsers() as $user) {
if ($user->getUID() === $share->getShareOwner() ||
$user->getUID() === $share->getSharedBy()) {
continue;
}
$notification->setUser($user->getUID()); $notification->setUser($user->getUID());
$this->notificationManager->notify($notification); $this->notificationManager->notify($notification);
} }

View File

@ -28,7 +28,10 @@ namespace OCA\Files_Sharing\Notification;
use OCP\Files\IRootFolder; use OCP\Files\IRootFolder;
use OCP\IL10N; use OCP\IL10N;
use OCP\IGroupManager;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory; use OCP\L10N\IFactory;
use OCP\Notification\AlreadyProcessedException; use OCP\Notification\AlreadyProcessedException;
use OCP\Notification\INotification; use OCP\Notification\INotification;
@ -45,6 +48,10 @@ class Notifier implements INotifier {
private $shareManager; private $shareManager;
/** @var IRootFolder */ /** @var IRootFolder */
private $rootFolder; private $rootFolder;
/** @var IGroupManager */
protected $groupManager;
/** @var IUserManager */
protected $userManager;
/** @var IURLGenerator */ /** @var IURLGenerator */
protected $url; protected $url;
@ -52,10 +59,14 @@ class Notifier implements INotifier {
public function __construct(IFactory $l10nFactory, public function __construct(IFactory $l10nFactory,
IManager $shareManager, IManager $shareManager,
IRootFolder $rootFolder, IRootFolder $rootFolder,
IGroupManager $groupManager,
IUserManager $userManager,
IURLGenerator $url) { IURLGenerator $url) {
$this->l10nFactory = $l10nFactory; $this->l10nFactory = $l10nFactory;
$this->shareManager = $shareManager; $this->shareManager = $shareManager;
$this->rootFolder = $rootFolder; $this->rootFolder = $rootFolder;
$this->groupManager = $groupManager;
$this->userManager = $userManager;
$this->url = $url; $this->url = $url;
} }
@ -135,11 +146,12 @@ class Notifier implements INotifier {
} }
protected function parseShareInvitation(IShare $share, INotification $notification, IL10N $l): INotification { protected function parseShareInvitation(IShare $share, INotification $notification, IL10N $l): INotification {
if ($share->getShareType() === IShare::TYPE_USER) { if ($share->getShareType() === IShare::TYPE_USER) {
if ($share->getSharedWith() !== $notification->getUser()) { if ($share->getStatus() !== IShare::STATUS_PENDING) {
throw new AlreadyProcessedException(); throw new AlreadyProcessedException();
} }
} else if ($share->getShareType() === IShare::TYPE_GROUP) {
if ($share->getStatus() !== IShare::STATUS_PENDING) { if ($share->getStatus() !== IShare::STATUS_PENDING) {
throw new AlreadyProcessedException(); throw new AlreadyProcessedException();
} }
@ -147,6 +159,10 @@ class Notifier implements INotifier {
switch ($notification->getSubject()) { switch ($notification->getSubject()) {
case 'incoming_user_share': case 'incoming_user_share':
if ($share->getSharedWith() !== $notification->getUser()) {
throw new AlreadyProcessedException();
}
$subject = $l->t('You received {share} as a share from {user}'); $subject = $l->t('You received {share} as a share from {user}');
$subjectParameters = [ $subjectParameters = [
'share' => [ 'share' => [
@ -160,34 +176,70 @@ class Notifier implements INotifier {
'name' => $share->getShareOwner(), 'name' => $share->getShareOwner(),
], ],
]; ];
break;
$placeholders = $replacements = []; case 'incoming_group_share':
foreach ($subjectParameters as $placeholder => $parameter) { $user = $this->userManager->get($notification->getUser());
$placeholders[] = '{' . $placeholder . '}'; if (!$user instanceof IUser) {
$replacements[] = $parameter['name']; throw new AlreadyProcessedException();
} }
$notification->setParsedSubject(str_replace($placeholders, $replacements, $subject)) $group = $this->groupManager->get($share->getSharedWith());
->setRichSubject($subject, $subjectParameters) if (!$group->inGroup($user)) {
->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); throw new AlreadyProcessedException();
}
$acceptAction = $notification->createAction(); if ($share->getPermissions() === 0) {
$acceptAction->setParsedLabel($l->t('Accept')) // Already rejected
->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.acceptShare', ['id' => $share->getId()]), 'POST') throw new AlreadyProcessedException();
->setPrimary(true); }
$notification->addParsedAction($acceptAction);
$rejectAction = $notification->createAction(); $subject = $l->t('You received {share} to group {group} as a share from {user}');
$rejectAction->setParsedLabel($l->t('Reject')) $subjectParameters = [
->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.deleteShare', ['id' => $share->getId()]), 'DELETE') 'share' => [
->setPrimary(false); 'type' => 'highlight',
$notification->addParsedAction($rejectAction); 'id' => $notification->getObjectId(),
'name' => $share->getNode()->getName(),
return $notification; ],
'group' => [
'type' => 'user-group',
'id' => $group->getGID(),
'name' => $group->getDisplayName(),
],
'user' => [
'type' => 'user',
'id' => $share->getShareOwner(),
'name' => $share->getShareOwner(),
],
];
break; break;
default: default:
throw new \InvalidArgumentException('Invalid subject'); 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;
} }
} }

View File

@ -320,6 +320,71 @@ class DefaultShareProvider implements IShareProvider {
return $share; return $share;
} }
/**
* 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 * Get all children of this share
* FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * 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 * Unshare a share from the recipient. If this is a group share
* this means we need a special entry in the share db. * 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 * @param string $recipient UserId of recipient
* @throws BackendError * @throws BackendError
* @throws ProviderException * @throws ProviderException
*/ */
public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { public function deleteFromSelf(IShare $share, $recipient) {
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { if ($share->getShareType() === IShare::TYPE_GROUP) {
$group = $this->groupManager->get($share->getSharedWith()); $group = $this->groupManager->get($share->getSharedWith());
$user = $this->userManager->get($recipient); $user = $this->userManager->get($recipient);
@ -423,37 +488,23 @@ class DefaultShareProvider implements IShareProvider {
* If there is update it (if required). * If there is update it (if required).
*/ */
if ($data === false) { if ($data === false) {
$qb = $this->dbConn->getQueryBuilder(); $id = $this->createUserSpecificGroupShare($share, $recipient);
$permissions = $share->getPermissions();
$type = $share->getNodeType(); } else {
$permissions = $data['permissions'];
//Insert new share $id = $data['id'];
$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) {
if ($permissions !== 0) {
// Update existing usergroup share // Update existing usergroup share
$qb = $this->dbConn->getQueryBuilder(); $qb = $this->dbConn->getQueryBuilder();
$qb->update('share') $qb->update('share')
->set('permissions', $qb->createNamedParameter(0)) ->set('permissions', $qb->createNamedParameter(0))
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
->execute(); ->execute();
} }
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { } else if ($share->getShareType() === IShare::TYPE_USER) {
if ($share->getSharedWith() !== $recipient) { if ($share->getSharedWith() !== $recipient) {
throw new ProviderException('Recipient does not match'); 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 * @inheritdoc
* *

View File

@ -928,6 +928,30 @@ class Manager implements IManager {
return $share; return $share;
} }
/**
* 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 * Updates the password of the given share if it is not the same as the
* password of the original share. * password of the original share.

View File

@ -54,6 +54,7 @@ interface IManager {
* Update a share. * Update a share.
* The target of the share can't be changed this way: use moveShare * 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 share can't be removed this way (permission 0): use deleteShare
* The state can't be changed this way: use acceptShare
* *
* @param IShare $share * @param IShare $share
* @return IShare The share object * @return IShare The share object
@ -62,6 +63,17 @@ interface IManager {
*/ */
public function updateShare(IShare $share); 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 * Delete a share
* *

View File

@ -63,6 +63,16 @@ interface IShareProvider {
*/ */
public function update(\OCP\Share\IShare $share); 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 * Delete a share
* *