Skip to content

Commit

Permalink
implement federated group shares (WIP)
Browse files Browse the repository at this point in the history
Signed-off-by: Bjoern Schiessle <[email protected]>
  • Loading branch information
schiessle committed Jun 25, 2018
1 parent 9bf896f commit 037a610
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 41 deletions.
9 changes: 6 additions & 3 deletions apps/cloud_federation_api/lib/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ public function __construct(ICloudFederationProviderManager $cloudFederationProv
*
* @param string $resourceType
* @return array
* @throws \OCP\Federation\Exceptions\ProviderDoesNotExistsException
*/
public function getSupportedShareTypes($resourceType) {
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
return $provider->getSupportedShareTypes();
try {
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
return $provider->getSupportedShareTypes();
} catch (\Exception $e) {
return [];
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
use OCP\Federation\ICloudIdManager;
use OCP\IGroupManager;
use OCP\ILogger;
use OCP\IRequest;
use OCP\IURLGenerator;
Expand All @@ -57,6 +58,9 @@ class RequestHandlerController extends Controller {
/** @var IUserManager */
private $userManager;

/** @var IGroupManager */
private $groupManager;

/** @var IURLGenerator */
private $urlGenerator;

Expand All @@ -76,6 +80,7 @@ public function __construct($appName,
IRequest $request,
ILogger $logger,
IUserManager $userManager,
IGroupManager $groupManager,
IURLGenerator $urlGenerator,
ICloudFederationProviderManager $cloudFederationProviderManager,
Config $config,
Expand All @@ -86,6 +91,7 @@ public function __construct($appName,

$this->logger = $logger;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->urlGenerator = $urlGenerator;
$this->cloudFederationProviderManager = $cloudFederationProviderManager;
$this->config = $config;
Expand Down Expand Up @@ -136,15 +142,35 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
);
}

$supportedShareTypes = $this->config->getSupportedShareTypes($resourceType);
if (!in_array($shareType, $supportedShareTypes)) {
return new JSONResponse(
['message' => 'Share type "' . $shareType . '" not implemented'],
Http::STATUS_NOT_IMPLEMENTED
);
}

$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
$shareWithLocalId = $cloudId->getUser();
$shareWith = $this->mapUid($shareWithLocalId);

if (!$this->userManager->userExists($shareWith)) {
return new JSONResponse(
['message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
Http::STATUS_BAD_REQUEST
);
if ($shareType === 'user') {
$shareWith = $this->mapUid($shareWithLocalId);

if (!$this->userManager->userExists($shareWith)) {
return new JSONResponse(
['message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
Http::STATUS_BAD_REQUEST
);
}
}

if ($shareType === 'group') {
if(!$this->groupManager->groupExists($shareWithLocalId)) {
return new JSONResponse(
['message' => 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
Http::STATUS_BAD_REQUEST
);
}
}

// if no explicit display name is given, we use the uid as display name
Expand All @@ -161,7 +187,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
$share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
$share->setProtocol($protocol);
$id = $provider->shareReceived($share);
$provider->shareReceived($share);
} catch (ProviderDoesNotExistsException $e) {
return new JSONResponse(
['message' => $e->getMessage()],
Expand Down
3 changes: 2 additions & 1 deletion apps/federatedfilesharing/lib/FederatedShareProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ protected function createFederatedShare(IShare $share) {
$share->getShareOwner(),
$ownerCloudId->getId(),
$share->getSharedBy(),
$sharedByFederatedId
$sharedByFederatedId,
$share->setShareType()
);

if ($send === false) {
Expand Down
7 changes: 5 additions & 2 deletions apps/federatedfilesharing/lib/Notifications.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,12 @@ public function __construct(
* @param string $ownerFederatedId
* @param string $sharedBy
* @param string $sharedByFederatedId
* @param int $shareType (can be a remote user or group share)
* @return bool
* @throws \OC\HintException
* @throws \OC\ServerNotAvailableException
*/
public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId, $shareType) {

list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);

Expand All @@ -109,6 +110,7 @@ public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $
'sharedBy' => $sharedBy,
'sharedByFederatedId' => $sharedByFederatedId,
'remote' => $local,
'shareType' => $shareType
);

$result = $this->tryHttpPostToShareEndpoint($remote, '', $fields);
Expand Down Expand Up @@ -416,7 +418,7 @@ protected function tryOCMEndPoint($remoteDomain, $fields, $action) {
$fields['sharedByFederatedId'],
$fields['sharedBy'],
$fields['token'],
'user',
$fields['shareType'],
'file'
);
return $this->federationProviderManager->sendShare($share);
Expand All @@ -430,6 +432,7 @@ protected function tryOCMEndPoint($remoteDomain, $fields, $action) {
'sharedSecret' => $fields['token'],
'shareWith' => $fields['shareWith'],
'senderId' => $fields['localId'],
'shareType' => $fields['shareType'],
'message' => 'Ask owner to reshare the file'
]
);
Expand Down
85 changes: 64 additions & 21 deletions apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@
use OCP\Federation\ICloudIdManager;
use OCP\Files\NotFoundException;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
use OCP\Share;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IShare;
use OCP\Util;
Expand Down Expand Up @@ -86,6 +88,9 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
/** @var IDBConnection */
private $connection;

/** @var IGroupManager */
private $groupManager;

/**
* CloudFederationProvider constructor.
*
Expand All @@ -101,6 +106,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider {
* @param ICloudFederationFactory $cloudFederationFactory
* @param ICloudFederationProviderManager $cloudFederationProviderManager
* @param IDBConnection $connection
* @param IGroupManager $groupManager
*/
public function __construct(IAppManager $appManager,
FederatedShareProvider $federatedShareProvider,
Expand All @@ -113,7 +119,8 @@ public function __construct(IAppManager $appManager,
IURLGenerator $urlGenerator,
ICloudFederationFactory $cloudFederationFactory,
ICloudFederationProviderManager $cloudFederationProviderManager,
IDBConnection $connection
IDBConnection $connection,
IGroupManager $groupManager
) {
$this->appManager = $appManager;
$this->federatedShareProvider = $federatedShareProvider;
Expand All @@ -127,6 +134,7 @@ public function __construct(IAppManager $appManager,
$this->cloudFederationFactory = $cloudFederationFactory;
$this->cloudFederationProviderManager = $cloudFederationProviderManager;
$this->connection = $connection;
$this->groupManager = $groupManager;
}


Expand Down Expand Up @@ -175,6 +183,7 @@ public function shareReceived(ICloudFederationShare $share) {
$remoteId = $share->getProviderId();
$sharedByFederatedId = $share->getSharedBy();
$ownerFederatedId = $share->getOwner();
$shareType = $this->mapShareTypeToNextcloud($share->getShareType());

// if no explicit information about the person who created the share was send
// we assume that the share comes from the owner
Expand Down Expand Up @@ -217,7 +226,7 @@ public function shareReceived(ICloudFederationShare $share) {
);

try {
$externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
$externalManager->addShare($remote, $token, '', $name, $owner, $shareType,false, $shareWith, $remoteId);
$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');

$event = $this->activityManager->generateEvent();
Expand All @@ -228,25 +237,14 @@ public function shareReceived(ICloudFederationShare $share) {
->setObject('remote_share', (int)$shareId, $name);
\OC::$server->getActivityManager()->publish($event);

$notification = $this->notificationManager->createNotification();
$notification->setApp('files_sharing')
->setUser($shareWith)
->setDateTime(new \DateTime())
->setObject('remote_share', $shareId)
->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);

$declineAction = $notification->createAction();
$declineAction->setLabel('decline')
->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
$notification->addAction($declineAction);

$acceptAction = $notification->createAction();
$acceptAction->setLabel('accept')
->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
$notification->addAction($acceptAction);

$this->notificationManager->notify($notification);

if ($shareType === Share::SHARE_TYPE_USER) {
$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
} else {
$groupMembers = $this->groupManager->get($shareWith)->getUsers();
foreach ($groupMembers as $user) {
$this->notifyAboutNewShare($user, $shareId, $ownerFederatedId, $sharedByFederatedId, $name);
}
}
return $shareId;
} catch (\Exception $e) {
$this->logger->logException($e, [
Expand Down Expand Up @@ -297,6 +295,51 @@ public function notificationReceived($notificationType, $providerId, array $noti
throw new BadRequestException([$notificationType]);
}

/**
* map OCM share type (strings) to Nextcloud internal share types (integer)
*
* @param string $shareType
* @return int
*/
private function mapShareTypeToNextcloud($shareType) {
$result = Share::SHARE_TYPE_USER;
if ($shareType === 'group') {
$result = Share::SHARE_TYPE_GROUP;
}

return $result;
}

/**
* notify user about new federated share
*
* @param $shareWith
* @param $shareId
* @param $ownerFederatedId
* @param $sharedByFederatedId
* @param $name
*/
private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name) {
$notification = $this->notificationManager->createNotification();
$notification->setApp('files_sharing')
->setUser($shareWith)
->setDateTime(new \DateTime())
->setObject('remote_share', $shareId)
->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);

$declineAction = $notification->createAction();
$declineAction->setLabel('decline')
->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
$notification->addAction($declineAction);

$acceptAction = $notification->createAction();
$acceptAction->setLabel('accept')
->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
$notification->addAction($acceptAction);

$this->notificationManager->notify($notification);
}

/**
* process notification that the recipient accepted a share
*
Expand Down
11 changes: 11 additions & 0 deletions apps/files_sharing/appinfo/database.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
<autoincrement>1</autoincrement>
<length>4</length>
</field>
<field>
<name>parent</name>
<type>integer</type>
<default>-1</default>
<length>4</length>
</field>
<field>
<name>share_type</name>
<type>integer</type>
<length>4</length>
</field>
<field>
<name>remote</name>
<type>text</type>
Expand Down
2 changes: 1 addition & 1 deletion apps/files_sharing/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Turning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation.

</description>
<version>1.6.1</version>
<version>1.6.2</version>
<licence>agpl</licence>
<author>Michael Gapczynski</author>
<author>Bjoern Schiessle</author>
Expand Down
20 changes: 14 additions & 6 deletions apps/files_sharing/lib/External/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,15 @@ public function __construct(IDBConnection $connection,
* @param string $password
* @param string $name
* @param string $owner
* @param int $shareType
* @param boolean $accepted
* @param string $user
* @param int $remoteId
* @param int $parent
* @return Mount|null
* @throws \Doctrine\DBAL\DBALException
*/
public function addShare($remote, $token, $password, $name, $owner, $accepted=false, $user = null, $remoteId = -1) {
public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted=false, $user = null, $remoteId = -1, $parent = -1) {

$user = $user ? $user : $this->uid;
$accepted = $accepted ? 1 : 0;
Expand All @@ -156,6 +159,7 @@ public function addShare($remote, $token, $password, $name, $owner, $accepted=fa
'mountpoint_hash' => $hash,
'accepted' => $accepted,
'remote_id' => $remoteId,
'share_type' => $shareType,
];

$i = 1;
Expand All @@ -174,10 +178,10 @@ public function addShare($remote, $token, $password, $name, $owner, $accepted=fa

$query = $this->connection->prepare('
INSERT INTO `*PREFIX*share_external`
(`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
(`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`, `parent`, `share_type`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
');
$query->execute(array($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId));
$query->execute(array($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType));

$options = array(
'remote' => $remote,
Expand Down Expand Up @@ -223,13 +227,17 @@ public function acceptShare($id) {
$mountPoint = Filesystem::normalizePath($mountPoint);
$hash = md5($mountPoint);

$acceptShare = $this->connection->prepare('
if($share['share_type'] === \OCP\Share::SHARE_TYPE_USER) {
$acceptShare = $this->connection->prepare('
UPDATE `*PREFIX*share_external`
SET `accepted` = ?,
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
$updated = $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
$updated = $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
} else {
// TODO group share, add additional row for the user who accepted it
}
if ($updated === true) {
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
\OC_Hook::emit(Share::class, 'federated_share_added', ['server' => $share['remote']]);
Expand Down
1 change: 1 addition & 0 deletions lib/private/Share/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Constants {
const SHARE_TYPE_REMOTE = 6;
const SHARE_TYPE_CIRCLE = 7;
const SHARE_TYPE_GUEST = 8;
const SHARE_TYPE_REMOTE_GROUP = 9;

const FORMAT_NONE = -1;
const FORMAT_STATUSES = -2;
Expand Down

0 comments on commit 037a610

Please sign in to comment.