summaryrefslogtreecommitdiffstats
path: root/apps/federatedfilesharing
diff options
context:
space:
mode:
authorVincent Petry <pvince81@owncloud.com>2016-05-23 09:52:13 +0200
committerVincent Petry <pvince81@owncloud.com>2016-05-23 09:52:13 +0200
commit5ba1add03ccd3c4f72a72e5e31f986af42acc8de (patch)
treea4dd71aa2822367ee584055613e1922c327345b0 /apps/federatedfilesharing
parentc78bb2ab340472c682add8bbe86c25379ef5740c (diff)
parent20852fd8c071b949962406d7ffb3b6e173eb171b (diff)
downloadnextcloud-server-5ba1add03ccd3c4f72a72e5e31f986af42acc8de.tar.gz
nextcloud-server-5ba1add03ccd3c4f72a72e5e31f986af42acc8de.zip
Merge pull request #24603 from owncloud/federated_reshare
flat federated re-share
Diffstat (limited to 'apps/federatedfilesharing')
-rw-r--r--apps/federatedfilesharing/appinfo/app.php17
-rw-r--r--apps/federatedfilesharing/appinfo/database.xml41
-rw-r--r--apps/federatedfilesharing/appinfo/info.xml2
-rw-r--r--apps/federatedfilesharing/lib/AppInfo/Application.php3
-rw-r--r--apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php (renamed from apps/federatedfilesharing/lib/BackgroundJob/UnShare.php)32
-rw-r--r--apps/federatedfilesharing/lib/FederatedShareProvider.php300
-rw-r--r--apps/federatedfilesharing/lib/Notifications.php147
-rw-r--r--apps/federatedfilesharing/lib/RequestHandler.php580
-rw-r--r--apps/federatedfilesharing/lib/notifier.php93
-rw-r--r--apps/federatedfilesharing/tests/AddressHandlerTest.php3
-rw-r--r--apps/federatedfilesharing/tests/DiscoveryManagerTest.php3
-rw-r--r--apps/federatedfilesharing/tests/FederatedShareProviderTest.php116
-rw-r--r--apps/federatedfilesharing/tests/NotificationsTest.php29
-rw-r--r--apps/federatedfilesharing/tests/RequestHandlerTest.php381
-rw-r--r--apps/federatedfilesharing/tests/TestCase.php132
-rw-r--r--apps/federatedfilesharing/tests/TokenHandlerTest.php3
16 files changed, 1798 insertions, 84 deletions
diff --git a/apps/federatedfilesharing/appinfo/app.php b/apps/federatedfilesharing/appinfo/app.php
index 4666d343f7e..c33a887c6d6 100644
--- a/apps/federatedfilesharing/appinfo/app.php
+++ b/apps/federatedfilesharing/appinfo/app.php
@@ -20,4 +20,21 @@
*/
$app = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing');
+
+use OCA\FederatedFileSharing\Notifier;
+
+$l = \OC::$server->getL10N('files_sharing');
+
$app->registerSettings();
+
+$manager = \OC::$server->getNotificationManager();
+$manager->registerNotifier(function() {
+ return new Notifier(
+ \OC::$server->getL10NFactory()
+ );
+}, function() use ($l) {
+ return [
+ 'id' => 'files_sharing',
+ 'name' => $l->t('Federated sharing'),
+ ];
+});
diff --git a/apps/federatedfilesharing/appinfo/database.xml b/apps/federatedfilesharing/appinfo/database.xml
new file mode 100644
index 00000000000..1dbe8ee2ec9
--- /dev/null
+++ b/apps/federatedfilesharing/appinfo/database.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Keep a mapping of the share ID stored in the local oc_share table
+and the share ID stored in the remote servers oc_share table.
+This is needed in order to send updates in both directions between
+the servers (e.g. permissions change, unshare,...)
+-->
+
+<database>
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+ <charset>utf8</charset>
+ <table>
+ <name>*dbprefix*federated_reshares</name>
+ <declaration>
+ <field>
+ <name>share_id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <length>4</length>
+ </field>
+ <field>
+ <name>remote_id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <length>4</length>
+ <comments>share ID at the remote server</comments>
+ </field>
+ <index>
+ <name>share_id_index</name>
+ <unique>true</unique>
+ <field>
+ <name>share_id</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+ </declaration>
+ </table>
+</database>
diff --git a/apps/federatedfilesharing/appinfo/info.xml b/apps/federatedfilesharing/appinfo/info.xml
index 643281bd145..5cf4039f196 100644
--- a/apps/federatedfilesharing/appinfo/info.xml
+++ b/apps/federatedfilesharing/appinfo/info.xml
@@ -5,7 +5,7 @@
<description>Provide federated file sharing across ownCloud servers</description>
<licence>AGPL</licence>
<author>Bjoern Schiessle, Roeland Jago Douma</author>
- <version>0.2.0</version>
+ <version>0.3.0</version>
<namespace>FederatedFileSharing</namespace>
<category>other</category>
<dependencies>
diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php
index 5a213aec8e2..d1b0646ba5b 100644
--- a/apps/federatedfilesharing/lib/AppInfo/Application.php
+++ b/apps/federatedfilesharing/lib/AppInfo/Application.php
@@ -81,7 +81,8 @@ class Application extends App {
\OC::$server->getL10N('federatedfilesharing'),
\OC::$server->getLogger(),
\OC::$server->getLazyRootFolder(),
- \OC::$server->getConfig()
+ \OC::$server->getConfig(),
+ \OC::$server->getUserManager()
);
}
diff --git a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php
index b056db4eac7..109a607bff0 100644
--- a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php
+++ b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php
@@ -32,26 +32,26 @@ use OCP\BackgroundJob\IJobList;
use OCP\ILogger;
/**
- * Class UnShare
+ * Class RetryJob
*
- * Background job to re-send the un-share notification to the remote server in
+ * Background job to re-send update of federated re-shares to the remote server in
* case the server was not available on the first try
*
* @package OCA\FederatedFileSharing\BackgroundJob
*/
-class UnShare extends Job {
+class RetryJob extends Job {
/** @var bool */
private $retainJob = true;
-
+
/** @var Notifications */
private $notifications;
- /** @var int max number of attempts to send the un-share request */
- private $maxTry = 10;
+ /** @var int max number of attempts to send the request */
+ private $maxTry = 20;
- /** @var int how much time should be between two tries (12 hours) */
- private $interval = 43200;
+ /** @var int how much time should be between two tries (10 minutes) */
+ private $interval = 600;
/**
* UnShare constructor.
@@ -77,7 +77,7 @@ class UnShare extends Job {
\OC::$server->getJobList()
);
}
-
+
}
/**
@@ -99,12 +99,14 @@ class UnShare extends Job {
protected function run($argument) {
$remote = $argument['remote'];
- $id = (int)$argument['id'];
+ $remoteId = $argument['remoteId'];
$token = $argument['token'];
+ $action = $argument['action'];
+ $data = json_decode($argument['data'], true);
$try = (int)$argument['try'] + 1;
- $result = $this->notifications->sendRemoteUnShare($remote, $id, $token, $try);
-
+ $result = $this->notifications->sendUpdateToRemote($remote, $remoteId, $token, $action, $data, $try);
+
if ($result === true || $try > $this->maxTry) {
$this->retainJob = false;
}
@@ -117,11 +119,13 @@ class UnShare extends Job {
* @param array $argument
*/
protected function reAddJob(IJobList $jobList, array $argument) {
- $jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare',
+ $jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
[
'remote' => $argument['remote'],
- 'id' => $argument['id'],
+ 'remoteId' => $argument['remoteId'],
'token' => $argument['token'],
+ 'data' => $argument['data'],
+ 'action' => $argument['action'],
'try' => (int)$argument['try'] + 1,
'lastRun' => time()
]
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php
index d014a6219a3..76fed01c308 100644
--- a/apps/federatedfilesharing/lib/FederatedShareProvider.php
+++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php
@@ -25,10 +25,10 @@ namespace OCA\FederatedFileSharing;
use OC\Share20\Share;
use OCP\Files\IRootFolder;
-use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
+use OCP\IUserManager;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
use OC\Share20\Exception\InvalidShare;
@@ -70,6 +70,12 @@ class FederatedShareProvider implements IShareProvider {
/** @var IConfig */
private $config;
+ /** @var string */
+ private $externalShareTable = 'share_external';
+
+ /** @var IUserManager */
+ private $userManager;
+
/**
* DefaultShareProvider constructor.
*
@@ -81,6 +87,7 @@ class FederatedShareProvider implements IShareProvider {
* @param ILogger $logger
* @param IRootFolder $rootFolder
* @param IConfig $config
+ * @param IUserManager $userManager
*/
public function __construct(
IDBConnection $connection,
@@ -90,7 +97,8 @@ class FederatedShareProvider implements IShareProvider {
IL10N $l10n,
ILogger $logger,
IRootFolder $rootFolder,
- IConfig $config
+ IConfig $config,
+ IUserManager $userManager
) {
$this->dbConnection = $connection;
$this->addressHandler = $addressHandler;
@@ -100,6 +108,7 @@ class FederatedShareProvider implements IShareProvider {
$this->logger = $logger;
$this->rootFolder = $rootFolder;
$this->config = $config;
+ $this->userManager = $userManager;
}
/**
@@ -124,10 +133,9 @@ class FederatedShareProvider implements IShareProvider {
$shareWith = $share->getSharedWith();
$itemSource = $share->getNodeId();
$itemType = $share->getNodeType();
- $uidOwner = $share->getShareOwner();
$permissions = $share->getPermissions();
$sharedBy = $share->getSharedBy();
-
+
/*
* Check if file is not already shared with the remote user
*/
@@ -151,31 +159,136 @@ class FederatedShareProvider implements IShareProvider {
throw new \Exception($message_t);
}
- $token = $this->tokenHandler->generateToken();
+ $share->setSharedWith($user . '@' . $remote);
+
+ try {
+ $remoteShare = $this->getShareFromExternalShareTable($share);
+ } catch (ShareNotFound $e) {
+ $remoteShare = null;
+ }
- $shareWith = $user . '@' . $remote;
+ if ($remoteShare) {
+ try {
+ $uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote'];
+ $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time());
+ $share->setId($shareId);
+ list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
+ // remote share was create successfully if we get a valid token as return
+ $send = is_string($token) && $token !== '';
+ } catch (\Exception $e) {
+ // fall back to old re-share behavior if the remote server
+ // doesn't support flat re-shares (was introduced with ownCloud 9.1)
+ $this->removeShareFromTable($share);
+ $shareId = $this->createFederatedShare($share);
+ }
+ if ($send) {
+ $this->updateSuccessfulReshare($shareId, $token);
+ $this->storeRemoteId($shareId, $remoteId);
+ } else {
+ $this->removeShareFromTable($share);
+ $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
+ throw new \Exception($message_t);
+ }
- $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+ } else {
+ $shareId = $this->createFederatedShare($share);
+ }
+ $data = $this->getRawShare($shareId);
+ return $this->createShareObject($data);
+ }
+
+ /**
+ * create federated share and inform the recipient
+ *
+ * @param IShare $share
+ * @return int
+ * @throws ShareNotFound
+ * @throws \Exception
+ */
+ protected function createFederatedShare(IShare $share) {
+ $token = $this->tokenHandler->generateToken();
+ $shareId = $this->addShareToDB(
+ $share->getNodeId(),
+ $share->getNodeType(),
+ $share->getSharedWith(),
+ $share->getSharedBy(),
+ $share->getShareOwner(),
+ $share->getPermissions(),
+ $token
+ );
+ $sharedByFederatedId = $share->getSharedBy();
+ if ($this->userManager->userExists($sharedByFederatedId)) {
+ $sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
+ }
$send = $this->notifications->sendRemoteShare(
$token,
- $shareWith,
+ $share->getSharedWith(),
$share->getNode()->getName(),
$shareId,
- $share->getSharedBy()
+ $share->getShareOwner(),
+ $share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
+ $share->getSharedBy(),
+ $sharedByFederatedId
);
- $data = $this->getRawShare($shareId);
- $share = $this->createShare($data);
-
if ($send === false) {
- $this->delete($share);
+ $data = $this->getRawShare($shareId);
+ $share = $this->createShareObject($data);
+ $this->removeShareFromTable($share);
$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
- [$share->getNode()->getName(), $shareWith]);
+ [$share->getNode()->getName(), $share->getSharedWith()]);
throw new \Exception($message_t);
}
- return $share;
+ return $shareId;
+ }
+
+ /**
+ * @param string $shareWith
+ * @param IShare $share
+ * @param string $shareId internal share Id
+ * @return array
+ * @throws \Exception
+ */
+ protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
+
+ $remoteShare = $this->getShareFromExternalShareTable($share);
+ $token = $remoteShare['share_token'];
+ $remoteId = $remoteShare['remote_id'];
+ $remote = $remoteShare['remote'];
+
+ list($token, $remoteId) = $this->notifications->requestReShare(
+ $token,
+ $remoteId,
+ $shareId,
+ $remote,
+ $shareWith,
+ $share->getPermissions()
+ );
+
+ return [$token, $remoteId];
+ }
+
+ /**
+ * get federated share from the share_external table but exclude mounted link shares
+ *
+ * @param IShare $share
+ * @return array
+ * @throws ShareNotFound
+ */
+ protected function getShareFromExternalShareTable(IShare $share) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $query->select('*')->from($this->externalShareTable)
+ ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
+ ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
+ $result = $query->execute()->fetchAll();
+
+ if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
+ return $result[0];
+ }
+
+ throw new ShareNotFound('share not found in share_external table');
}
/**
@@ -234,10 +347,86 @@ class FederatedShareProvider implements IShareProvider {
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->execute();
+ // send the updated permission to the owner/initiator, if they are not the same
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ $this->sendPermissionUpdate($share);
+ }
+
return $share;
}
/**
+ * send the updated permission to the owner/initiator, if they are not the same
+ *
+ * @param IShare $share
+ * @throws ShareNotFound
+ * @throws \OC\HintException
+ */
+ protected function sendPermissionUpdate(IShare $share) {
+ $remoteId = $this->getRemoteId($share);
+ // if the local user is the owner we send the permission change to the initiator
+ if ($this->userManager->userExists($share->getShareOwner())) {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+ } else { // ... if not we send the permission change to the owner
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+ }
+ $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
+ }
+
+
+ /**
+ * update successful reShare with the correct token
+ *
+ * @param int $shareId
+ * @param string $token
+ */
+ protected function updateSuccessfulReShare($shareId, $token) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $query->update('share')
+ ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
+ ->set('token', $query->createNamedParameter($token))
+ ->execute();
+ }
+
+ /**
+ * store remote ID in federated reShare table
+ *
+ * @param $shareId
+ * @param $remoteId
+ */
+ public function storeRemoteId($shareId, $remoteId) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $query->insert('federated_reshares')
+ ->values(
+ [
+ 'share_id' => $query->createNamedParameter($shareId),
+ 'remote_id' => $query->createNamedParameter($remoteId),
+ ]
+ );
+ $query->execute();
+ }
+
+ /**
+ * get share ID on remote server for federated re-shares
+ *
+ * @param IShare $share
+ * @return int
+ * @throws ShareNotFound
+ */
+ public function getRemoteId(IShare $share) {
+ $query = $this->dbConnection->getQueryBuilder();
+ $query->select('remote_id')->from('federated_reshares')
+ ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
+ $data = $query->execute()->fetch();
+
+ if (!is_array($data) || !isset($data['remote_id'])) {
+ throw new ShareNotFound();
+ }
+
+ return (int)$data['remote_id'];
+ }
+
+ /**
* @inheritdoc
*/
public function move(IShare $share, $recipient) {
@@ -266,7 +455,7 @@ class FederatedShareProvider implements IShareProvider {
$cursor = $qb->execute();
while($data = $cursor->fetch()) {
- $children[] = $this->createShare($data);
+ $children[] = $this->createShareObject($data);
}
$cursor->closeCursor();
@@ -274,18 +463,77 @@ class FederatedShareProvider implements IShareProvider {
}
/**
- * Delete a share
+ * Delete a share (owner unShares the file)
*
* @param IShare $share
*/
public function delete(IShare $share) {
+
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
+
+ $isOwner = false;
+
+ // if the local user is the owner we can send the unShare request directly...
+ if ($this->userManager->userExists($share->getShareOwner())) {
+ $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
+ $this->revokeShare($share, true);
+ $isOwner = true;
+ } else { // ... if not we need to correct ID for the unShare request
+ $remoteId = $this->getRemoteId($share);
+ $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
+ $this->revokeShare($share, false);
+ }
+
+ // send revoke notification to the other user, if initiator and owner are not the same user
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ $remoteId = $this->getRemoteId($share);
+ if ($isOwner) {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+ } else {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+ }
+ $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
+ }
+
+ $this->removeShareFromTable($share);
+ }
+
+ /**
+ * in case of a re-share we need to send the other use (initiator or owner)
+ * a message that the file was unshared
+ *
+ * @param IShare $share
+ * @param bool $isOwner the user can either be the owner or the user who re-sahred it
+ * @throws ShareNotFound
+ * @throws \OC\HintException
+ */
+ protected function revokeShare($share, $isOwner) {
+ // also send a unShare request to the initiator, if this is a different user than the owner
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ if ($isOwner) {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+ } else {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+ }
+ $remoteId = $this->getRemoteId($share);
+ $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
+ }
+ }
+
+ /**
+ * remove share from table
+ *
+ * @param IShare $share
+ */
+ public function removeShareFromTable(IShare $share) {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
$qb->execute();
- list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
- $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
+ $qb->delete('federated_reshares')
+ ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($share->getId())));
+ $qb->execute();
}
/**
@@ -348,7 +596,7 @@ class FederatedShareProvider implements IShareProvider {
$cursor = $qb->execute();
$shares = [];
while($data = $cursor->fetch()) {
- $shares[] = $this->createShare($data);
+ $shares[] = $this->createShareObject($data);
}
$cursor->closeCursor();
@@ -375,7 +623,7 @@ class FederatedShareProvider implements IShareProvider {
}
try {
- $share = $this->createShare($data);
+ $share = $this->createShareObject($data);
} catch (InvalidShare $e) {
throw new ShareNotFound();
}
@@ -400,7 +648,7 @@ class FederatedShareProvider implements IShareProvider {
$shares = [];
while($data = $cursor->fetch()) {
- $shares[] = $this->createShare($data);
+ $shares[] = $this->createShareObject($data);
}
$cursor->closeCursor();
@@ -439,7 +687,7 @@ class FederatedShareProvider implements IShareProvider {
$cursor = $qb->execute();
while($data = $cursor->fetch()) {
- $shares[] = $this->createShare($data);
+ $shares[] = $this->createShareObject($data);
}
$cursor->closeCursor();
@@ -470,7 +718,7 @@ class FederatedShareProvider implements IShareProvider {
}
try {
- $share = $this->createShare($data);
+ $share = $this->createShareObject($data);
} catch (InvalidShare $e) {
throw new ShareNotFound();
}
@@ -512,9 +760,9 @@ class FederatedShareProvider implements IShareProvider {
* @throws InvalidShare
* @throws ShareNotFound
*/
- private function createShare($data) {
+ private function createShareObject($data) {
- $share = new Share($this->rootFolder);
+ $share = new Share($this->rootFolder, $this->userManager);
$share->setId((int)$data['id'])
->setShareType((int)$data['share_type'])
->setPermissions((int)$data['permissions'])
diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php
index 9cdc7760361..bf9e0fc9634 100644
--- a/apps/federatedfilesharing/lib/Notifications.php
+++ b/apps/federatedfilesharing/lib/Notifications.php
@@ -23,6 +23,7 @@
namespace OCA\FederatedFileSharing;
+use OCP\AppFramework\Http;
use OCP\BackgroundJob\IJobList;
use OCP\Http\Client\IClientService;
@@ -67,9 +68,14 @@ class Notifications {
* @param string $name
* @param int $remote_id
* @param string $owner
+ * @param string $ownerFederatedId
+ * @param string $sharedBy
+ * @param string $sharedByFederatedId
* @return bool
+ * @throws \OC\HintException
+ * @throws \OC\ServerNotAvailableException
*/
- public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
+ public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
@@ -83,6 +89,9 @@ class Notifications {
'name' => $name,
'remoteId' => $remote_id,
'owner' => $owner,
+ 'ownerFederatedId' => $ownerFederatedId,
+ 'sharedBy' => $sharedBy,
+ 'sharedByFederatedId' => $sharedByFederatedId,
'remote' => $local,
);
@@ -101,34 +110,142 @@ class Notifications {
}
/**
+ * ask owner to re-share the file with the given user
+ *
+ * @param string $token
+ * @param int $id remote Id
+ * @param int $shareId internal share Id
+ * @param string $remote remote address of the owner
+ * @param string $shareWith
+ * @param int $permission
+ * @return bool
+ * @throws \OC\HintException
+ * @throws \OC\ServerNotAvailableException
+ */
+ public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission) {
+
+ $fields = array(
+ 'shareWith' => $shareWith,
+ 'token' => $token,
+ 'permission' => $permission,
+ 'remoteId' => $shareId
+ );
+
+ $url = $this->addressHandler->removeProtocolFromUrl($remote);
+ $result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $id . '/reshare', $fields);
+ $status = json_decode($result['result'], true);
+
+ $httpRequestSuccessful = $result['success'];
+ $ocsCallSuccessful = $status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200;
+ $validToken = isset($status['ocs']['data']['token']) && is_string($status['ocs']['data']['token']);
+ $validRemoteId = isset($status['ocs']['data']['remoteId']);
+
+ if ($httpRequestSuccessful && $ocsCallSuccessful && $validToken && $validRemoteId) {
+ return [
+ $status['ocs']['data']['token'],
+ (int)$status['ocs']['data']['remoteId']
+ ];
+ }
+
+ return false;
+ }
+
+ /**
* send server-to-server unshare to remote server
*
* @param string $remote url
* @param int $id share id
* @param string $token
- * @param int $try how often did we already tried to send the un-share request
* @return bool
*/
- public function sendRemoteUnShare($remote, $id, $token, $try = 0) {
- $url = rtrim($remote, '/');
- $fields = array('token' => $token, 'format' => 'json');
- $url = $this->addressHandler->removeProtocolFromUrl($url);
- $result = $this->tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
+ public function sendRemoteUnShare($remote, $id, $token) {
+ $this->sendUpdateToRemote($remote, $id, $token, 'unshare');
+ }
+
+ /**
+ * send server-to-server unshare to remote server
+ *
+ * @param string $remote url
+ * @param int $id share id
+ * @param string $token
+ * @return bool
+ */
+ public function sendRevokeShare($remote, $id, $token) {
+ $this->sendUpdateToRemote($remote, $id, $token, 'revoke');
+ }
+
+ /**
+ * send notification to remote server if the permissions was changed
+ *
+ * @param string $remote
+ * @param int $remoteId
+ * @param string $token
+ * @param int $permissions
+ * @return bool
+ */
+ public function sendPermissionChange($remote, $remoteId, $token, $permissions) {
+ $this->sendUpdateToRemote($remote, $remoteId, $token, 'permissions', ['permissions' => $permissions]);
+ }
+
+ /**
+ * forward accept reShare to remote server
+ *
+ * @param string $remote
+ * @param int $remoteId
+ * @param string $token
+ */
+ public function sendAcceptShare($remote, $remoteId, $token) {
+ $this->sendUpdateToRemote($remote, $remoteId, $token, 'accept');
+ }
+
+ /**
+ * forward decline reShare to remote server
+ *
+ * @param string $remote
+ * @param int $remoteId
+ * @param string $token
+ */
+ public function sendDeclineShare($remote, $remoteId, $token) {
+ $this->sendUpdateToRemote($remote, $remoteId, $token, 'decline');
+ }
+
+ /**
+ * inform remote server whether server-to-server share was accepted/declined
+ *
+ * @param string $remote
+ * @param string $token
+ * @param int $remoteId Share id on the remote host
+ * @param string $action possible actions: accept, decline, unshare, revoke, permissions
+ * @param array $data
+ * @param int $try
+ * @return boolean
+ */
+ public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) {
+
+ $fields = array('token' => $token);
+ foreach ($data as $key => $value) {
+ $fields[$key] = $value;
+ }
+
+ $url = $this->addressHandler->removeProtocolFromUrl($remote);
+ $result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $remoteId . '/' . $action, $fields);
$status = json_decode($result['result'], true);
- if ($result['success'] &&
- ($status['ocs']['meta']['statuscode'] === 100 ||
+ if ($result['success'] &&
+ ($status['ocs']['meta']['statuscode'] === 100 ||
$status['ocs']['meta']['statuscode'] === 200
)
) {
return true;
} elseif ($try === 0) {
// only add new job on first try
- $this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare',
+ $this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
[
'remote' => $remote,
- 'id' => $id,
+ 'remoteId' => $remoteId,
'token' => $token,
+ 'action' => $action,
+ 'data' => json_encode($data),
'try' => $try,
'lastRun' => $this->getTimestamp()
]
@@ -138,6 +255,7 @@ class Notifications {
return false;
}
+
/**
* return current timestamp
*
@@ -154,6 +272,7 @@ class Notifications {
* @param string $urlSuffix
* @param array $fields post parameters
* @return array
+ * @throws \Exception
*/
protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
$client = $this->httpClientService->newClient();
@@ -174,6 +293,12 @@ class Notifications {
$result['success'] = true;
break;
} catch (\Exception $e) {
+ // if flat re-sharing is not supported by the remote server
+ // we re-throw the exception and fall back to the old behaviour.
+ // (flat re-shares has been introduced in ownCloud 9.1)
+ if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
+ throw $e;
+ }
$try++;
$protocol = 'http://';
}
diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php
new file mode 100644
index 00000000000..01ab96822d8
--- /dev/null
+++ b/apps/federatedfilesharing/lib/RequestHandler.php
@@ -0,0 +1,580 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing;
+
+use OCA\Files_Sharing\Activity;
+use OCP\AppFramework\Http;
+use OCP\Constants;
+use OCP\Files\NotFoundException;
+use OCP\IDBConnection;
+use OCP\IRequest;
+use OCP\IUserManager;
+use OCP\Share;
+
+/**
+ * Class RequestHandler
+ *
+ * handles OCS Request to the federated share API
+ *
+ * @package OCA\FederatedFileSharing\API
+ */
+class RequestHandler {
+
+ /** @var FederatedShareProvider */
+ private $federatedShareProvider;
+
+ /** @var IDBConnection */
+ private $connection;
+
+ /** @var Share\IManager */
+ private $shareManager;
+
+ /** @var IRequest */
+ private $request;
+
+ /** @var Notifications */
+ private $notifications;
+
+ /** @var AddressHandler */
+ private $addressHandler;
+
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var string */
+ private $shareTable = 'share';
+
+ /**
+ * Server2Server constructor.
+ *
+ * @param FederatedShareProvider $federatedShareProvider
+ * @param IDBConnection $connection
+ * @param Share\IManager $shareManager
+ * @param IRequest $request
+ * @param Notifications $notifications
+ * @param AddressHandler $addressHandler
+ * @param IUserManager $userManager
+ */
+ public function __construct(FederatedShareProvider $federatedShareProvider,
+ IDBConnection $connection,
+ Share\IManager $shareManager,
+ IRequest $request,
+ Notifications $notifications,
+ AddressHandler $addressHandler,
+ IUserManager $userManager
+ ) {
+ $this->federatedShareProvider = $federatedShareProvider;
+ $this->connection = $connection;
+ $this->shareManager = $shareManager;
+ $this->request = $request;
+ $this->notifications = $notifications;
+ $this->addressHandler = $addressHandler;
+ $this->userManager = $userManager;
+ }
+
+ /**
+ * create a new share
+ *
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public function createShare($params) {
+
+ if (!$this->isS2SEnabled(true)) {
+ return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+ }
+
+ $remote = isset($_POST['remote']) ? $_POST['remote'] : null;
+ $token = isset($_POST['token']) ? $_POST['token'] : null;
+ $name = isset($_POST['name']) ? $_POST['name'] : null;
+ $owner = isset($_POST['owner']) ? $_POST['owner'] : null;
+ $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
+ $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
+ $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
+ $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
+ $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
+
+ if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
+
+ if(!\OCP\Util::isValidFileName($name)) {
+ return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.');
+ }
+
+ // FIXME this should be a method in the user management instead
+ \OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
+ \OCP\Util::emitHook(
+ '\OCA\Files_Sharing\API\Server2Server',
+ 'preLoginNameUsedAsUserName',
+ array('uid' => &$shareWith)
+ );
+ \OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG);
+
+ if (!\OCP\User::userExists($shareWith)) {
+ return new \OC_OCS_Result(null, 400, 'User does not exists');
+ }
+
+ \OC_Util::setupFS($shareWith);
+
+ $discoveryManager = new DiscoveryManager(
+ \OC::$server->getMemCacheFactory(),
+ \OC::$server->getHTTPClientService()
+ );
+ $externalManager = new \OCA\Files_Sharing\External\Manager(
+ \OC::$server->getDatabaseConnection(),
+ \OC\Files\Filesystem::getMountManager(),
+ \OC\Files\Filesystem::getLoader(),
+ \OC::$server->getHTTPHelper(),
+ \OC::$server->getNotificationManager(),
+ $discoveryManager,
+ $shareWith
+ );
+
+ try {
+ $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
+ $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
+
+ if ($ownerFederatedId === null) {
+ $ownerFederatedId = $owner . '@' . $this->cleanupRemote($remote);
+ }
+ // if the owner of the share and the initiator are the same user
+ // we also complete the federated share ID for the initiator
+ if ($sharedByFederatedId === null && $owner === $sharedBy) {
+ $sharedByFederatedId = $ownerFederatedId;
+ }
+
+ \OC::$server->getActivityManager()->publishActivity(
+ Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_RECEIVED, array($ownerFederatedId, trim($name, '/')), '', array(),
+ '', '', $shareWith, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_LOW);
+
+ $urlGenerator = \OC::$server->getURLGenerator();
+
+ $notificationManager = \OC::$server->getNotificationManager();
+ $notification = $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($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
+ $notification->addAction($declineAction);
+
+ $acceptAction = $notification->createAction();
+ $acceptAction->setLabel('accept')
+ ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
+ $notification->addAction($acceptAction);
+
+ $notificationManager->notify($notification);
+
+ return new \OC_OCS_Result();
+ } catch (\Exception $e) {
+ \OCP\Util::writeLog('files_sharing', 'server can not add remote share, ' . $e->getMessage(), \OCP\Util::ERROR);
+ return new \OC_OCS_Result(null, 500, 'internal server error, was not able to add share from ' . $remote);
+ }
+ }
+
+ return new \OC_OCS_Result(null, 400, 'server can not add remote share, missing parameter');
+ }
+
+ /**
+ * create re-share on behalf of another user
+ *
+ * @param $params
+ * @return \OC_OCS_Result
+ */
+ public function reShare($params) {
+
+ $id = isset($params['id']) ? (int)$params['id'] : null;
+ $token = $this->request->getParam('token', null);
+ $shareWith = $this->request->getParam('shareWith', null);
+ $permission = (int)$this->request->getParam('permission', null);
+ $remoteId = (int)$this->request->getParam('remoteId', null);
+
+ if ($id === null ||
+ $token === null ||
+ $shareWith === null ||
+ $permission === null ||
+ $remoteId === null
+ ) {
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+ }
+
+ try {
+ $share = $this->federatedShareProvider->getShareById($id);
+ } catch (Share\Exceptions\ShareNotFound $e) {
+ return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND);
+ }
+
+ // don't allow to share a file back to the owner
+ list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
+ $owner = $share->getShareOwner();
+ $currentServer = $this->addressHandler->generateRemoteURL();
+ if ($this->addressHandler->compareAddresses($user, $remote,$owner , $currentServer)) {
+ return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
+ }
+
+ if ($this->verifyShare($share, $token)) {
+
+ // check if re-sharing is allowed
+ if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) {
+ $share->setPermissions($share->getPermissions() & $permission);
+ // the recipient of the initial share is now the initiator for the re-share
+ $share->setSharedBy($share->getSharedWith());
+ $share->setSharedWith($shareWith);
+ try {
+ $result = $this->federatedShareProvider->create($share);
+ $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId);
+ return new \OC_OCS_Result(['token' => $result->getToken(), 'remoteId' => $result->getId()]);
+ } catch (\Exception $e) {
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+ }
+ } else {
+ return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
+ }
+ }
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+
+ }
+
+ /**
+ * accept server-to-server share
+ *
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public function acceptShare($params) {
+
+ if (!$this->isS2SEnabled()) {
+ return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+ }
+
+ $id = $params['id'];
+ $token = isset($_POST['token']) ? $_POST['token'] : null;
+
+ try {
+ $share = $this->federatedShareProvider->getShareById($id);
+ } catch (Share\Exceptions\ShareNotFound $e) {
+ return new \OC_OCS_Result();
+ }
+
+ if ($this->verifyShare($share, $token)) {
+ $this->executeAcceptShare($share);
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+ $remoteId = $this->federatedShareProvider->getRemoteId($share);
+ $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken());
+ }
+ }
+
+ return new \OC_OCS_Result();
+ }
+
+ protected function executeAcceptShare(Share\IShare $share) {
+ list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId());
+
+ $event = \OC::$server->getActivityManager()->generateEvent();
+ $event->setApp(Activity::FILES_SHARING_APP)
+ ->setType(Activity::TYPE_REMOTE_SHARE)
+ ->setAffectedUser($this->getCorrectUid($share))
+ ->setSubject(Activity::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), basename($file)])
+ ->setObject('files', $share->getNode()->getId(), $file)
+ ->setLink($link);
+ \OC::$server->getActivityManager()->publish($event);
+ }
+
+ /**
+ * decline server-to-server share
+ *
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public function declineShare($params) {
+
+ if (!$this->isS2SEnabled()) {
+ return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+ }
+
+ $id = (int)$params['id'];
+ $token = isset($_POST['token']) ? $_POST['token'] : null;
+
+ try {
+ $share = $this->federatedShareProvider->getShareById($id);
+ } catch (Share\Exceptions\ShareNotFound $e) {
+ return new \OC_OCS_Result();
+ }
+
+ if($this->verifyShare($share, $token)) {
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+ $remoteId = $this->federatedShareProvider->getRemoteId($share);
+ $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken());
+ }
+ $this->executeDeclineShare($share);
+ }
+
+ return new \OC_OCS_Result();
+ }
+
+ /**
+ * delete declined share and create a activity
+ *
+ * @param Share\IShare $share
+ */
+ protected function executeDeclineShare(Share\IShare $share) {
+ $this->federatedShareProvider->removeShareFromTable($share);
+ list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId());
+
+ $event = \OC::$server->getActivityManager()->generateEvent();
+ $event->setApp(Activity::FILES_SHARING_APP)
+ ->setType(Activity::TYPE_REMOTE_SHARE)
+ ->setAffectedUser($this->getCorrectUid($share))
+ ->setSubject(Activity::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), basename($file)])
+ ->setObject('files', $share->getNode()->getId(), $file)
+ ->setLink($link);
+ \OC::$server->getActivityManager()->publish($event);
+
+ }
+
+ /**
+ * check if we are the initiator or the owner of a re-share and return the correct UID
+ *
+ * @param Share\IShare $share
+ * @return string
+ */
+ protected function getCorrectUid(Share\IShare $share) {
+ if($this->userManager->userExists($share->getShareOwner())) {
+ return $share->getShareOwner();
+ }
+
+ return $share->getSharedBy();
+ }
+
+ /**
+ * remove server-to-server share if it was unshared by the owner
+ *
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public function unshare($params) {
+
+ if (!$this->isS2SEnabled()) {
+ return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+ }
+
+ $id = $params['id'];
+ $token = isset($_POST['token']) ? $_POST['token'] : null;
+
+ $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
+ $query->execute(array($id, $token));
+ $share = $query->fetchRow();
+
+ if ($token && $id && !empty($share)) {
+
+ $remote = $this->cleanupRemote($share['remote']);
+
+ $owner = $share['owner'] . '@' . $remote;
+ $mountpoint = $share['mountpoint'];
+ $user = $share['user'];
+
+ $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
+ $query->execute(array($id, $token));
+
+ if ($share['accepted']) {
+ $path = trim($mountpoint, '/');
+ } else {
+ $path = trim($share['name'], '/');
+ }
+
+ $notificationManager = \OC::$server->getNotificationManager();
+ $notification = $notificationManager->createNotification();
+ $notification->setApp('files_sharing')
+ ->setUser($share['user'])
+ ->setObject('remote_share', (int) $share['id']);
+ $notificationManager->markProcessed($notification);
+
+ \OC::$server->getActivityManager()->publishActivity(
+ Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $path), '', array(),
+ '', '', $user, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_MEDIUM);
+ }
+
+ return new \OC_OCS_Result();
+ }
+
+ private function cleanupRemote($remote) {
+ $remote = substr($remote, strpos($remote, '://') + 3);
+
+ return rtrim($remote, '/');
+ }
+
+
+ /**
+ * federated share was revoked, either by the owner or the re-sharer
+ *
+ * @param $params
+ * @return \OC_OCS_Result
+ */
+ public function revoke($params) {
+ $id = (int)$params['id'];
+ $token = $this->request->getParam('token');
+
+ $share = $this->federatedShareProvider->getShareById($id);
+
+ if ($this->verifyShare($share, $token)) {
+ $this->federatedShareProvider->removeShareFromTable($share);
+ return new \OC_OCS_Result();
+ }
+
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+
+ }
+
+ /**
+ * get share
+ *
+ * @param int $id
+ * @param string $token
+ * @return array|bool
+ */
+ protected function getShare($id, $token) {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')->from($this->shareTable)
+ ->where($query->expr()->eq('token', $query->createNamedParameter($token)))
+ ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE)))
+ ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id)));
+
+ $result = $query->execute()->fetchAll();
+
+ if (!empty($result) && isset($result[0])) {
+ return $result[0];
+ }
+
+ return false;
+ }
+
+ /**
+ * get file
+ *
+ * @param string $user
+ * @param int $fileSource
+ * @return array with internal path of the file and a absolute link to it
+ */
+ private function getFile($user, $fileSource) {
+ \OC_Util::setupFS($user);
+
+ try {
+ $file = \OC\Files\Filesystem::getPath($fileSource);
+ } catch (NotFoundException $e) {
+ $file = null;
+ }
+ $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
+ $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
+
+ return array($file, $link);
+
+ }
+
+ /**
+ * check if server-to-server sharing is enabled
+ *
+ * @param bool $incoming
+ * @return bool
+ */
+ private function isS2SEnabled($incoming = false) {
+
+ $result = \OCP\App::isEnabled('files_sharing');
+
+ if ($incoming) {
+ $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
+ } else {
+ $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
+ }
+
+ return $result;
+ }
+
+ /**
+ * check if we got the right share
+ *
+ * @param Share\IShare $share
+ * @param string $token
+ * @return bool
+ */
+ protected function verifyShare(Share\IShare $share, $token) {
+ if (
+ $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE &&
+ $share->getToken() === $token
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * update share information to keep federated re-shares in sync
+ *
+ * @param array $params
+ * @return \OC_OCS_Result
+ */
+ public function updatePermissions($params) {
+ $id = (int)$params['id'];
+ $token = $this->request->getParam('token', null);
+ $permissions = $this->request->getParam('permissions', null);
+
+ try {
+ $share = $this->federatedShareProvider->getShareById($id);
+ } catch (Share\Exceptions\ShareNotFound $e) {
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+ }
+
+ $validPermission = ctype_digit($permissions);
+ $validToken = $this->verifyShare($share, $token);
+ if ($validPermission && $validToken) {
+ $this->updatePermissionsInDatabase($share, (int)$permissions);
+ } else {
+ return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+ }
+
+ return new \OC_OCS_Result();
+ }
+
+ /**
+ * update permissions in database
+ *
+ * @param IShare $share
+ * @param int $permissions
+ */
+ protected function updatePermissionsInDatabase(IShare $share, $permissions) {
+ $query = $this->connection->getQueryBuilder();
+ $query->update('share')
+ ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
+ ->set('permissions', $query->createNamedParameter($permissions))
+ ->execute();
+ }
+
+}
diff --git a/apps/federatedfilesharing/lib/notifier.php b/apps/federatedfilesharing/lib/notifier.php
new file mode 100644
index 00000000000..1c8d92c7eca
--- /dev/null
+++ b/apps/federatedfilesharing/lib/notifier.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing;
+
+
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+
+class Notifier implements INotifier {
+ /** @var \OCP\L10N\IFactory */
+ protected $factory;
+
+ /**
+ * @param \OCP\L10N\IFactory $factory
+ */
+ public function __construct(\OCP\L10N\IFactory $factory) {
+ $this->factory = $factory;
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ * @return INotification
+ */
+ public function prepare(INotification $notification, $languageCode) {
+ if ($notification->getApp() !== 'files_sharing') {
+ // Not my app => throw
+ throw new \InvalidArgumentException();
+ }
+
+ // Read the language from the notification
+ $l = $this->factory->get('files_sharing', $languageCode);
+
+ switch ($notification->getSubject()) {
+ // Deal with known subjects
+ case 'remote_share':
+ $params = $notification->getSubjectParameters();
+ if ($params[0] !== $params[1] && $params[1] !== null) {
+ $notification->setParsedSubject(
+ (string) $l->t('You received "/%3$s" as a remote share from %1$s (on behalf of %2$s)', $params)
+ );
+ } else {
+ $notification->setParsedSubject(
+ (string)$l->t('You received "/%3$s" as a remote share from %1$s', $params)
+ );
+ }
+
+ // Deal with the actions for a known subject
+ foreach ($notification->getActions() as $action) {
+ switch ($action->getLabel()) {
+ case 'accept':
+ $action->setParsedLabel(
+ (string) $l->t('Accept')
+ )
+ ->setPrimary(true);
+ break;
+
+ case 'decline':
+ $action->setParsedLabel(
+ (string) $l->t('Decline')
+ );
+ break;
+ }
+
+ $notification->addParsedAction($action);
+ }
+ return $notification;
+
+ default:
+ // Unknown subject => Unknown notification => throw
+ throw new \InvalidArgumentException();
+ }
+ }
+}
diff --git a/apps/federatedfilesharing/tests/AddressHandlerTest.php b/apps/federatedfilesharing/tests/AddressHandlerTest.php
index 9f7d8c49b4d..bb1c2c5a25a 100644
--- a/apps/federatedfilesharing/tests/AddressHandlerTest.php
+++ b/apps/federatedfilesharing/tests/AddressHandlerTest.php
@@ -27,9 +27,8 @@ namespace OCA\FederatedFileSharing\Tests;
use OCA\FederatedFileSharing\AddressHandler;
use OCP\IL10N;
use OCP\IURLGenerator;
-use Test\TestCase;
-class AddressHandlerTest extends TestCase {
+class AddressHandlerTest extends \Test\TestCase {
/** @var AddressHandler */
private $addressHandler;
diff --git a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
index 9ae62b1ae4d..5af6b394dd2 100644
--- a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
+++ b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
@@ -26,9 +26,8 @@ use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\ICache;
use OCP\ICacheFactory;
-use Test\TestCase;
-class DiscoveryManagerTest extends TestCase {
+class DiscoveryManagerTest extends \Test\TestCase {
/** @var ICache */
private $cache;
/** @var IClient */
diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
index 1fbae90a46f..9b3edf0398d 100644
--- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
+++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
@@ -30,8 +30,8 @@ use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\ILogger;
+use OCP\IUserManager;
use OCP\Share\IManager;
-use Test\TestCase;
/**
* Class FederatedShareProviderTest
@@ -39,7 +39,7 @@ use Test\TestCase;
* @package OCA\FederatedFileSharing\Tests
* @group DB
*/
-class FederatedShareProviderTest extends TestCase {
+class FederatedShareProviderTest extends \Test\TestCase {
/** @var IDBConnection */
protected $connection;
@@ -57,6 +57,8 @@ class FederatedShareProviderTest extends TestCase {
protected $rootFolder;
/** @var IConfig | \PHPUnit_Framework_MockObject_MockObject */
protected $config;
+ /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+ protected $userManager;
/** @var IManager */
protected $shareManager;
@@ -82,7 +84,11 @@ class FederatedShareProviderTest extends TestCase {
$this->logger = $this->getMock('OCP\ILogger');
$this->rootFolder = $this->getMock('OCP\Files\IRootFolder');
$this->config = $this->getMock('OCP\IConfig');
- $this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l);
+ $this->userManager = $this->getMock('OCP\IUserManager');
+ //$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l);
+ $this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler')->disableOriginalConstructor()->getMock();
+
+ $this->userManager->expects($this->any())->method('userExists')->willReturn(true);
$this->provider = new FederatedShareProvider(
$this->connection,
@@ -92,7 +98,8 @@ class FederatedShareProviderTest extends TestCase {
$this->l,
$this->logger,
$this->rootFolder,
- $this->config
+ $this->config,
+ $this->userManager
);
$this->shareManager = \OC::$server->getShareManager();
@@ -119,6 +126,11 @@ class FederatedShareProviderTest extends TestCase {
$this->tokenHandler->method('generateToken')->willReturn('token');
+ $this->addressHandler->expects($this->any())->method('generateRemoteURL')
+ ->willReturn('http://localhost/');
+ $this->addressHandler->expects($this->any())->method('splitUserRemote')
+ ->willReturn(['user', 'server.com']);
+
$this->notifications->expects($this->once())
->method('sendRemoteShare')
->with(
@@ -126,7 +138,10 @@ class FederatedShareProviderTest extends TestCase {
$this->equalTo('user@server.com'),
$this->equalTo('myFile'),
$this->anything(),
- 'sharedBy'
+ 'shareOwner',
+ 'shareOwner@http://localhost/',
+ 'sharedBy',
+ 'sharedBy@http://localhost/'
)->willReturn(true);
$this->rootFolder->expects($this->never())->method($this->anything());
@@ -182,6 +197,11 @@ class FederatedShareProviderTest extends TestCase {
$this->tokenHandler->method('generateToken')->willReturn('token');
+ $this->addressHandler->expects($this->any())->method('generateRemoteURL')
+ ->willReturn('http://localhost/');
+ $this->addressHandler->expects($this->any())->method('splitUserRemote')
+ ->willReturn(['user', 'server.com']);
+
$this->notifications->expects($this->once())
->method('sendRemoteShare')
->with(
@@ -189,7 +209,10 @@ class FederatedShareProviderTest extends TestCase {
$this->equalTo('user@server.com'),
$this->equalTo('myFile'),
$this->anything(),
- 'sharedBy'
+ 'shareOwner',
+ 'shareOwner@http://localhost/',
+ 'sharedBy',
+ 'sharedBy@http://localhost/'
)->willReturn(false);
$this->rootFolder->expects($this->once())
@@ -226,7 +249,10 @@ class FederatedShareProviderTest extends TestCase {
$node->method('getId')->willReturn(42);
$node->method('getName')->willReturn('myFile');
- $shareWith = 'sharedBy@' . $this->addressHandler->generateRemoteURL();
+ $this->addressHandler->expects($this->any())->method('compareAddresses')
+ ->willReturn(true);
+
+ $shareWith = 'sharedBy@localhost';
$share->setSharedWith($shareWith)
->setSharedBy('sharedBy')
@@ -262,6 +288,10 @@ class FederatedShareProviderTest extends TestCase {
$node->method('getId')->willReturn(42);
$node->method('getName')->willReturn('myFile');
+
+ $this->addressHandler->expects($this->any())->method('splitUserRemote')
+ ->willReturn(['user', 'server.com']);
+
$share->setSharedWith('user@server.com')
->setSharedBy('sharedBy')
->setShareOwner('shareOwner')
@@ -270,6 +300,9 @@ class FederatedShareProviderTest extends TestCase {
$this->tokenHandler->method('generateToken')->willReturn('token');
+ $this->addressHandler->expects($this->any())->method('generateRemoteURL')
+ ->willReturn('http://localhost/');
+
$this->notifications->expects($this->once())
->method('sendRemoteShare')
->with(
@@ -277,7 +310,10 @@ class FederatedShareProviderTest extends TestCase {
$this->equalTo('user@server.com'),
$this->equalTo('myFile'),
$this->anything(),
- 'sharedBy'
+ 'shareOwner',
+ 'shareOwner@http://localhost/',
+ 'sharedBy',
+ 'sharedBy@http://localhost/'
)->willReturn(true);
$this->rootFolder->expects($this->never())->method($this->anything());
@@ -291,20 +327,46 @@ class FederatedShareProviderTest extends TestCase {
}
}
- public function testUpdate() {
+ /**
+ * @dataProvider datatTestUpdate
+ *
+ */
+ public function testUpdate($owner, $sharedBy) {
+
+ $this->provider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider')
+ ->setConstructorArgs(
+ [
+ $this->connection,
+ $this->addressHandler,
+ $this->notifications,
+ $this->tokenHandler,
+ $this->l,
+ $this->logger,
+ $this->rootFolder,
+ $this->config,
+ $this->userManager
+ ]
+ )->setMethods(['sendPermissionUpdate'])->getMock();
+
$share = $this->shareManager->newShare();
$node = $this->getMock('\OCP\Files\File');
$node->method('getId')->willReturn(42);
$node->method('getName')->willReturn('myFile');
+
+ $this->addressHandler->expects($this->any())->method('splitUserRemote')
+ ->willReturn(['user', 'server.com']);
+
$share->setSharedWith('user@server.com')
- ->setSharedBy('sharedBy')
- ->setShareOwner('shareOwner')
+ ->setSharedBy($sharedBy)
+ ->setShareOwner($owner)
->setPermissions(19)
->setNode($node);
$this->tokenHandler->method('generateToken')->willReturn('token');
+ $this->addressHandler->expects($this->any())->method('generateRemoteURL')
+ ->willReturn('http://localhost/');
$this->notifications->expects($this->once())
->method('sendRemoteShare')
@@ -313,9 +375,18 @@ class FederatedShareProviderTest extends TestCase {
$this->equalTo('user@server.com'),
$this->equalTo('myFile'),
$this->anything(),
- 'sharedBy'
+ $owner,
+ $owner . '@http://localhost/',
+ $sharedBy,
+ $sharedBy . '@http://localhost/'
)->willReturn(true);
+ if($owner === $sharedBy) {
+ $this->provider->expects($this->never())->method('sendPermissionUpdate');
+ } else {
+ $this->provider->expects($this->once())->method('sendPermissionUpdate');
+ }
+
$this->rootFolder->expects($this->never())->method($this->anything());
$share = $this->provider->create($share);
@@ -328,11 +399,24 @@ class FederatedShareProviderTest extends TestCase {
$this->assertEquals(1, $share->getPermissions());
}
+ public function datatTestUpdate() {
+ return [
+ ['sharedBy', 'shareOwner'],
+ ['shareOwner', 'shareOwner']
+ ];
+ }
+
public function testGetSharedBy() {
$node = $this->getMock('\OCP\Files\File');
$node->method('getId')->willReturn(42);
$node->method('getName')->willReturn('myFile');
+ $this->addressHandler->expects($this->at(0))->method('splitUserRemote')
+ ->willReturn(['user', 'server.com']);
+
+ $this->addressHandler->expects($this->at(1))->method('splitUserRemote')
+ ->willReturn(['user2', 'server.com']);
+
$this->tokenHandler->method('generateToken')->willReturn('token');
$this->notifications
->method('sendRemoteShare')
@@ -439,6 +523,14 @@ class FederatedShareProviderTest extends TestCase {
$node->method('getId')->willReturn(42);
$node->method('getName')->willReturn('myFile');
+ $this->addressHandler->expects($this->any())->method('splitUserRemote')
+ ->willReturnCallback(function ($uid) {
+ if ($uid === 'user@server.com') {
+ return ['user', 'server.com'];
+ }
+ return ['user2', 'server.com'];
+ });
+
$this->tokenHandler->method('generateToken')->willReturn('token');
$this->notifications
->method('sendRemoteShare')
diff --git a/apps/federatedfilesharing/tests/NotificationsTest.php b/apps/federatedfilesharing/tests/NotificationsTest.php
index bde69a82bad..50a62e9829e 100644
--- a/apps/federatedfilesharing/tests/NotificationsTest.php
+++ b/apps/federatedfilesharing/tests/NotificationsTest.php
@@ -28,9 +28,8 @@ use OCA\FederatedFileSharing\DiscoveryManager;
use OCA\FederatedFileSharing\Notifications;
use OCP\BackgroundJob\IJobList;
use OCP\Http\Client\IClientService;
-use Test\TestCase;
-class NotificationsTest extends TestCase {
+class NotificationsTest extends \Test\TestCase {
/** @var AddressHandler | \PHPUnit_Framework_MockObject_MockObject */
private $addressHandler;
@@ -85,14 +84,15 @@ class NotificationsTest extends TestCase {
return $instance;
}
+
/**
- * @dataProvider dataTestSendRemoteUnShare
+ * @dataProvider dataTestSendUpdateToRemote
*
* @param int $try
* @param array $httpRequestResult
* @param bool $expected
*/
- public function testSendRemoteUnShare($try, $httpRequestResult, $expected) {
+ public function testSendUpdateToRemote($try, $httpRequestResult, $expected) {
$remote = 'remote';
$id = 42;
$timestamp = 63576;
@@ -102,20 +102,22 @@ class NotificationsTest extends TestCase {
$instance->expects($this->any())->method('getTimestamp')->willReturn($timestamp);
$instance->expects($this->once())->method('tryHttpPostToShareEndpoint')
- ->with($remote, '/'.$id.'/unshare', ['token' => $token, 'format' => 'json'])
+ ->with($remote, '/'.$id.'/unshare', ['token' => $token, 'data1Key' => 'data1Value'])
->willReturn($httpRequestResult);
$this->addressHandler->expects($this->once())->method('removeProtocolFromUrl')
->with($remote)->willReturn($remote);
-
+
// only add background job on first try
if ($try === 0 && $expected === false) {
$this->jobList->expects($this->once())->method('add')
->with(
- 'OCA\FederatedFileSharing\BackgroundJob\UnShare',
+ 'OCA\FederatedFileSharing\BackgroundJob\RetryJob',
[
'remote' => $remote,
- 'id' => $id,
+ 'remoteId' => $id,
+ 'action' => 'unshare',
+ 'data' => json_encode(['data1Key' => 'data1Value']),
'token' => $token,
'try' => $try,
'lastRun' => $timestamp
@@ -124,14 +126,15 @@ class NotificationsTest extends TestCase {
} else {
$this->jobList->expects($this->never())->method('add');
}
-
+
$this->assertSame($expected,
- $instance->sendRemoteUnShare($remote, $id, $token, $try)
+ $instance->sendUpdateToRemote($remote, $id, $token, 'unshare', ['data1Key' => 'data1Value'], $try)
);
-
+
}
-
- public function dataTestSendRemoteUnshare() {
+
+
+ public function dataTestSendUpdateToRemote() {
return [
// test if background job is added correctly
[0, ['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 200]]])], true],
diff --git a/apps/federatedfilesharing/tests/RequestHandlerTest.php b/apps/federatedfilesharing/tests/RequestHandlerTest.php
new file mode 100644
index 00000000000..14da756a0ef
--- /dev/null
+++ b/apps/federatedfilesharing/tests/RequestHandlerTest.php
@@ -0,0 +1,381 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing\Tests;
+
+use OC\Files\Filesystem;
+use OCA\FederatedFileSharing\DiscoveryManager;
+use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\FederatedFileSharing\RequestHandler;
+use OCP\IUserManager;
+use OCP\Share\IShare;
+
+/**
+ * Class RequestHandlerTest
+ *
+ * @package OCA\FederatedFileSharing\Tests
+ * @group DB
+ */
+class RequestHandlerTest extends TestCase {
+
+ const TEST_FOLDER_NAME = '/folder_share_api_test';
+
+ /**
+ * @var \OCP\IDBConnection
+ */
+ private $connection;
+
+ /**
+ * @var RequestHandler
+ */
+ private $s2s;
+
+ /** @var \OCA\FederatedFileSharing\FederatedShareProvider | PHPUnit_Framework_MockObject_MockObject */
+ private $federatedShareProvider;
+
+ /** @var \OCA\FederatedFileSharing\Notifications | PHPUnit_Framework_MockObject_MockObject */
+ private $notifications;
+
+ /** @var \OCA\FederatedFileSharing\AddressHandler | PHPUnit_Framework_MockObject_MockObject */
+ private $addressHandler;
+
+ /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+ private $userManager;
+
+ /** @var IShare | \PHPUnit_Framework_MockObject_MockObject */
+ private $share;
+
+ protected function setUp() {
+ parent::setUp();
+
+ self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
+ \OCP\Share::registerBackend('test', 'Test\Share\Backend');
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()->getMock();
+ $clientService = $this->getMock('\OCP\Http\Client\IClientService');
+ $httpHelperMock = $this->getMockBuilder('\OC\HTTPHelper')
+ ->setConstructorArgs([$config, $clientService])
+ ->getMock();
+ $httpHelperMock->expects($this->any())->method('post')->with($this->anything())->will($this->returnValue(true));
+ $this->share = $this->getMock('\OCP\Share\IShare');
+ $this->federatedShareProvider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider')
+ ->disableOriginalConstructor()->getMock();
+ $this->federatedShareProvider->expects($this->any())
+ ->method('isOutgoingServer2serverShareEnabled')->willReturn(true);
+ $this->federatedShareProvider->expects($this->any())
+ ->method('isIncomingServer2serverShareEnabled')->willReturn(true);
+ $this->federatedShareProvider->expects($this->any())->method('getShareById')
+ ->willReturn($this->share);
+
+ $this->notifications = $this->getMockBuilder('OCA\FederatedFileSharing\Notifications')
+ ->disableOriginalConstructor()->getMock();
+ $this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler')
+ ->disableOriginalConstructor()->getMock();
+ $this->userManager = $this->getMock('OCP\IUserManager');
+
+ $this->registerHttpHelper($httpHelperMock);
+
+ $this->s2s = new RequestHandler(
+ $this->federatedShareProvider,
+ \OC::$server->getDatabaseConnection(),
+ \OC::$server->getShareManager(),
+ \OC::$server->getRequest(),
+ $this->notifications,
+ $this->addressHandler,
+ $this->userManager
+ );
+
+ $this->connection = \OC::$server->getDatabaseConnection();
+ }
+
+ protected function tearDown() {
+ $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external`');
+ $query->execute();
+
+ $query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share`');
+ $query->execute();
+
+ $this->restoreHttpHelper();
+
+ parent::tearDown();
+ }
+
+ /**
+ * Register an http helper mock for testing purposes.
+ * @param $httpHelper http helper mock
+ */
+ private function registerHttpHelper($httpHelper) {
+ $this->oldHttpHelper = \OC::$server->query('HTTPHelper');
+ \OC::$server->registerService('HTTPHelper', function ($c) use ($httpHelper) {
+ return $httpHelper;
+ });
+ }
+
+ /**
+ * Restore the original http helper
+ */
+ private function restoreHttpHelper() {
+ $oldHttpHelper = $this->oldHttpHelper;
+ \OC::$server->registerService('HTTPHelper', function ($c) use ($oldHttpHelper) {
+ return $oldHttpHelper;
+ });
+ }
+
+ /**
+ * @medium
+ */
+ function testCreateShare() {
+ // simulate a post request
+ $_POST['remote'] = 'localhost';
+ $_POST['token'] = 'token';
+ $_POST['name'] = 'name';
+ $_POST['owner'] = 'owner';
+ $_POST['shareWith'] = self::TEST_FILES_SHARING_API_USER2;
+ $_POST['remoteId'] = 1;
+
+ $result = $this->s2s->createShare(null);
+
+ $this->assertTrue($result->succeeded());
+
+ $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ?');
+ $result = $query->execute(array('1'));
+ $data = $result->fetchRow();
+
+ $this->assertSame('localhost', $data['remote']);
+ $this->assertSame('token', $data['share_token']);
+ $this->assertSame('/name', $data['name']);
+ $this->assertSame('owner', $data['owner']);
+ $this->assertSame(self::TEST_FILES_SHARING_API_USER2, $data['user']);
+ $this->assertSame(1, (int)$data['remote_id']);
+ $this->assertSame(0, (int)$data['accepted']);
+ }
+
+
+ function testDeclineShare() {
+
+ $this->s2s = $this->getMockBuilder('\OCA\FederatedFileSharing\RequestHandler')
+ ->setConstructorArgs(
+ [
+ $this->federatedShareProvider,
+ \OC::$server->getDatabaseConnection(),
+ \OC::$server->getShareManager(),
+ \OC::$server->getRequest(),
+ $this->notifications,
+ $this->addressHandler,
+ $this->userManager
+ ]
+ )->setMethods(['executeDeclineShare', 'verifyShare'])->getMock();
+
+ $this->s2s->expects($this->once())->method('executeDeclineShare');
+
+ $this->s2s->expects($this->any())->method('verifyShare')->willReturn(true);
+
+ $_POST['token'] = 'token';
+
+ $this->s2s->declineShare(array('id' => 42));
+
+ }
+
+ function XtestDeclineShareMultiple() {
+
+ $this->share->expects($this->any())->method('verifyShare')->willReturn(true);
+
+ $dummy = \OCP\DB::prepare('
+ INSERT INTO `*PREFIX*share`
+ (`share_type`, `uid_owner`, `item_type`, `item_source`, `item_target`, `file_source`, `file_target`, `permissions`, `stime`, `token`, `share_with`)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ');
+ $dummy->execute(array(\OCP\Share::SHARE_TYPE_REMOTE, self::TEST_FILES_SHARING_API_USER1, 'test', '1', '/1', '1', '/test.txt', '1', time(), 'token1', 'foo@bar'));
+ $dummy->execute(array(\OCP\Share::SHARE_TYPE_REMOTE, self::TEST_FILES_SHARING_API_USER1, 'test', '1', '/1', '1', '/test.txt', '1', time(), 'token2', 'bar@bar'));
+
+ $verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`');
+ $result = $verify->execute();
+ $data = $result->fetchAll();
+ $this->assertCount(2, $data);
+
+ $_POST['token'] = 'token1';
+ $this->s2s->declineShare(array('id' => $data[0]['id']));
+
+ $verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`');
+ $result = $verify->execute();
+ $data = $result->fetchAll();
+ $this->assertCount(1, $data);
+ $this->assertEquals('bar@bar', $data[0]['share_with']);
+
+ $_POST['token'] = 'token2';
+ $this->s2s->declineShare(array('id' => $data[0]['id']));
+
+ $verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`');
+ $result = $verify->execute();
+ $data = $result->fetchAll();
+ $this->assertEmpty($data);
+ }
+
+ /**
+ * @dataProvider dataTestDeleteUser
+ */
+ function testDeleteUser($toDelete, $expected, $remainingUsers) {
+ $this->createDummyS2SShares();
+
+ $discoveryManager = new DiscoveryManager(
+ \OC::$server->getMemCacheFactory(),
+ \OC::$server->getHTTPClientService()
+ );
+ $manager = new \OCA\Files_Sharing\External\Manager(
+ \OC::$server->getDatabaseConnection(),
+ Filesystem::getMountManager(),
+ Filesystem::getLoader(),
+ \OC::$server->getHTTPHelper(),
+ \OC::$server->getNotificationManager(),
+ $discoveryManager,
+ $toDelete
+ );
+
+ $manager->removeUserShares($toDelete);
+
+ $query = $this->connection->prepare('SELECT `user` FROM `*PREFIX*share_external`');
+ $query->execute();
+ $result = $query->fetchAll();
+
+ foreach ($result as $r) {
+ $remainingShares[$r['user']] = isset($remainingShares[$r['user']]) ? $remainingShares[$r['user']] + 1 : 1;
+ }
+
+ $this->assertSame($remainingUsers, count($remainingShares));
+
+ foreach ($expected as $key => $value) {
+ if ($key === $toDelete) {
+ $this->assertArrayNotHasKey($key, $remainingShares);
+ } else {
+ $this->assertSame($value, $remainingShares[$key]);
+ }
+ }
+
+ }
+
+ function dataTestDeleteUser() {
+ return array(
+ array('user1', array('user1' => 0, 'user2' => 3, 'user3' => 3), 2),
+ array('user2', array('user1' => 4, 'user2' => 0, 'user3' => 3), 2),
+ array('user3', array('user1' => 4, 'user2' => 3, 'user3' => 0), 2),
+ array('user4', array('user1' => 4, 'user2' => 3, 'user3' => 3), 3),
+ );
+ }
+
+ private function createDummyS2SShares() {
+ $query = $this->connection->prepare('
+ INSERT INTO `*PREFIX*share_external`
+ (`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `remote_id`, `accepted`)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ');
+
+ $users = array('user1', 'user2', 'user3');
+
+ for ($i = 0; $i < 10; $i++) {
+ $user = $users[$i%3];
+ $query->execute(array('remote', 'token', 'password', 'name', 'owner', $user, 'mount point', $i, $i, 0));
+ }
+
+ $query = $this->connection->prepare('SELECT `id` FROM `*PREFIX*share_external`');
+ $query->execute();
+ $dummyEntries = $query->fetchAll();
+
+ $this->assertSame(10, count($dummyEntries));
+ }
+
+ /**
+ * @dataProvider dataTestGetShare
+ *
+ * @param bool $found
+ * @param bool $correctId
+ * @param bool $correctToken
+ */
+ public function testGetShare($found, $correctId, $correctToken) {
+
+ $connection = \OC::$server->getDatabaseConnection();
+ $query = $connection->getQueryBuilder();
+ $stime = time();
+ $query->insert('share')
+ ->values(
+ [
+ 'share_type' => $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE),
+ 'uid_owner' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER1),
+ 'uid_initiator' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER2),
+ 'item_type' => $query->createNamedParameter('test'),
+ 'item_source' => $query->createNamedParameter('1'),
+ 'item_target' => $query->createNamedParameter('/1'),
+ 'file_source' => $query->createNamedParameter('1'),
+ 'file_target' => $query->createNamedParameter('/test.txt'),
+ 'permissions' => $query->createNamedParameter('1'),
+ 'stime' => $query->createNamedParameter($stime),
+ 'token' => $query->createNamedParameter('token'),
+ 'share_with' => $query->createNamedParameter('foo@bar'),
+ ]
+ )->execute();
+ $id = $query->getLastInsertId();
+
+ $expected = [
+ 'share_type' => (string)FederatedShareProvider::SHARE_TYPE_REMOTE,
+ 'uid_owner' => self::TEST_FILES_SHARING_API_USER1,
+ 'item_type' => 'test',
+ 'item_source' => '1',
+ 'item_target' => '/1',
+ 'file_source' => '1',
+ 'file_target' => '/test.txt',
+ 'permissions' => '1',
+ 'stime' => (string)$stime,
+ 'token' => 'token',
+ 'share_with' => 'foo@bar',
+ 'id' => (string)$id,
+ 'uid_initiator' => self::TEST_FILES_SHARING_API_USER2,
+ 'parent' => null,
+ 'accepted' => '0',
+ 'expiration' => null,
+ 'mail_send' => '0'
+ ];
+
+ $searchToken = $correctToken ? 'token' : 'wrongToken';
+ $searchId = $correctId ? $id : -1;
+
+ $result = $this->invokePrivate($this->s2s, 'getShare', [$searchId, $searchToken]);
+
+ if ($found) {
+ $this->assertEquals($expected, $result);
+ } else {
+ $this->assertSame(false, $result);
+ }
+ }
+
+ public function dataTestGetShare() {
+ return [
+ [true, true, true],
+ [false, false, true],
+ [false, true, false],
+ [false, false, false],
+ ];
+ }
+
+}
diff --git a/apps/federatedfilesharing/tests/TestCase.php b/apps/federatedfilesharing/tests/TestCase.php
new file mode 100644
index 00000000000..64c6d045598
--- /dev/null
+++ b/apps/federatedfilesharing/tests/TestCase.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jörn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing\Tests;
+
+use OC\Files\Filesystem;
+use OCA\Files\Share;
+
+/**
+ * Class Test_Files_Sharing_Base
+ *
+ * @group DB
+ *
+ * Base class for sharing tests.
+ */
+abstract class TestCase extends \Test\TestCase {
+
+ const TEST_FILES_SHARING_API_USER1 = "test-share-user1";
+ const TEST_FILES_SHARING_API_USER2 = "test-share-user2";
+
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+
+ // reset backend
+ \OC_User::clearBackends();
+ \OC_Group::clearBackends();
+
+ // create users
+ $backend = new \Test\Util\User\Dummy();
+ \OC_User::useBackend($backend);
+ $backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1);
+ $backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2);
+ }
+
+ protected function setUp() {
+ parent::setUp();
+
+ //login as user1
+ self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
+ }
+
+ public static function tearDownAfterClass() {
+ // cleanup users
+ $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER1);
+ if ($user !== null) {
+ $user->delete();
+ }
+ $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER2);
+ if ($user !== null) {
+ $user->delete();
+ }
+
+ \OC_Util::tearDownFS();
+ \OC_User::setUserId('');
+ Filesystem::tearDown();
+
+ // reset backend
+ \OC_User::clearBackends();
+ \OC_User::useBackend('database');
+ \OC_Group::clearBackends();
+ \OC_Group::useBackend(new \OC_Group_Database());
+
+ parent::tearDownAfterClass();
+ }
+
+ /**
+ * @param string $user
+ * @param bool $create
+ * @param bool $password
+ */
+ protected static function loginHelper($user, $create = false, $password = false) {
+
+ if ($password === false) {
+ $password = $user;
+ }
+
+ if ($create) {
+ \OC::$server->getUserManager()->createUser($user, $password);
+ \OC_Group::createGroup('group');
+ \OC_Group::addToGroup($user, 'group');
+ }
+
+ self::resetStorage();
+
+ \OC_Util::tearDownFS();
+ \OC::$server->getUserSession()->setUser(null);
+ \OC\Files\Filesystem::tearDown();
+ \OC::$server->getUserSession()->login($user, $password);
+ \OC::$server->getUserFolder($user);
+
+ \OC_Util::setupFS($user);
+ }
+
+ /**
+ * reset init status for the share storage
+ */
+ protected static function resetStorage() {
+ $storage = new \ReflectionClass('\OC\Files\Storage\Shared');
+ $isInitialized = $storage->getProperty('initialized');
+ $isInitialized->setAccessible(true);
+ $isInitialized->setValue($storage, false);
+ $isInitialized->setAccessible(false);
+ }
+
+}
diff --git a/apps/federatedfilesharing/tests/TokenHandlerTest.php b/apps/federatedfilesharing/tests/TokenHandlerTest.php
index 490c0d95d7b..004a3a61933 100644
--- a/apps/federatedfilesharing/tests/TokenHandlerTest.php
+++ b/apps/federatedfilesharing/tests/TokenHandlerTest.php
@@ -26,9 +26,8 @@ namespace OCA\FederatedFileSharing\Tests;
use OCA\FederatedFileSharing\TokenHandler;
use OCP\Security\ISecureRandom;
-use Test\TestCase;
-class TokenHandlerTest extends TestCase {
+class TokenHandlerTest extends \Test\TestCase {
/** @var TokenHandler */
private $tokenHandler;