diff --git a/apps/federatedfilesharing/lib/discoverymanager.php b/apps/federatedfilesharing/lib/discoverymanager.php new file mode 100644 index 0000000000..51ea71195f --- /dev/null +++ b/apps/federatedfilesharing/lib/discoverymanager.php @@ -0,0 +1,136 @@ + + * + * @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 + * + */ + +namespace OCA\FederatedFileSharing; + +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\ConnectException; +use OCP\Http\Client\IClient; +use OCP\Http\Client\IClientService; +use OCP\ICache; +use OCP\ICacheFactory; + +/** + * Class DiscoveryManager handles the discovery of endpoints used by Federated + * Cloud Sharing. + * + * @package OCA\FederatedFileSharing + */ +class DiscoveryManager { + /** @var ICache */ + private $cache; + /** @var IClient */ + private $client; + + /** + * @param ICacheFactory $cacheFactory + * @param IClientService $clientService + */ + public function __construct(ICacheFactory $cacheFactory, + IClientService $clientService) { + $this->cache = $cacheFactory->create('ocs-discovery'); + $this->client = $clientService->newClient(); + } + + /** + * Returns whether the specified URL includes only safe characters, if not + * returns false + * + * @param string $url + * @return bool + */ + private function isSafeUrl($url) { + return (bool)preg_match('/^[\/\.A-Za-z0-9]+$/', $url); + } + + /** + * Discover the actual data and do some naive caching to ensure that the data + * is not requested multiple times. + * + * If no valid discovery data is found the ownCloud defaults are returned. + * + * @param string $remote + * @return array + */ + private function discover($remote) { + // Check if something is in the cache + if($cacheData = $this->cache->get($remote)) { + return json_decode($cacheData, true); + } + + // Default response body + $discoveredServices = [ + 'webdav' => '/public.php/webdav', + 'share' => '/ocs/v1.php/cloud/shares', + ]; + + // Read the data from the response body + try { + $response = $this->client->get($remote . '/ocs-provider/'); + if($response->getStatusCode() === 200) { + $decodedService = json_decode($response->getBody(), true); + if(is_array($decodedService)) { + $endpoints = [ + 'webdav', + 'share', + ]; + + foreach($endpoints as $endpoint) { + if(isset($decodedService['services']['FEDERATED_SHARING']['endpoints'][$endpoint])) { + $endpointUrl = (string)$decodedService['services']['FEDERATED_SHARING']['endpoints'][$endpoint]; + if($this->isSafeUrl($endpointUrl)) { + $discoveredServices[$endpoint] = $endpointUrl; + } + } + } + } + } + } catch (ClientException $e) { + // Don't throw any exception since exceptions are handled before + } catch (ConnectException $e) { + // Don't throw any exception since exceptions are handled before + } + + // Write into cache + $this->cache->set($remote, json_encode($discoveredServices)); + return $discoveredServices; + } + + /** + * Return the public WebDAV endpoint used by the specified remote + * + * @param string $host + * @return string + */ + public function getWebDavEndpoint($host) { + return $this->discover($host)['webdav']; + } + + /** + * Return the sharing endpoint used by the specified remote + * + * @param string $host + * @return string + */ + public function getShareEndpoint($host) { + return $this->discover($host)['share']; + } +} diff --git a/apps/federatedfilesharing/lib/notifications.php b/apps/federatedfilesharing/lib/notifications.php index d778ac8782..4ec21e81cc 100644 --- a/apps/federatedfilesharing/lib/notifications.php +++ b/apps/federatedfilesharing/lib/notifications.php @@ -1,6 +1,7 @@ + * @author Lukas Reschke * * @copyright Copyright (c) 2016, ownCloud, Inc. * @license AGPL-3.0 @@ -22,32 +23,31 @@ namespace OCA\FederatedFileSharing; - use OCP\Http\Client\IClientService; class Notifications { - - const BASE_PATH_TO_SHARE_API = '/ocs/v1.php/cloud/shares'; const RESPONSE_FORMAT = 'json'; // default response format for ocs calls /** @var AddressHandler */ private $addressHandler; - /** @var IClientService */ private $httpClientService; + /** @var DiscoveryManager */ + private $discoveryManager; /** - * Notifications constructor. - * * @param AddressHandler $addressHandler * @param IClientService $httpClientService + * @param DiscoveryManager $discoveryManager */ public function __construct( AddressHandler $addressHandler, - IClientService $httpClientService + IClientService $httpClientService, + DiscoveryManager $discoveryManager ) { $this->addressHandler = $addressHandler; $this->httpClientService = $httpClientService; + $this->discoveryManager = $discoveryManager; } /** @@ -65,7 +65,7 @@ class Notifications { list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); if ($user && $remote) { - $url = $remote . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT; + $url = $remote; $local = $this->addressHandler->generateRemoteURL(); $fields = array( @@ -78,10 +78,10 @@ class Notifications { ); $url = $this->addressHandler->removeProtocolFromUrl($url); - $result = $this->tryHttpPost($url, $fields); + $result = $this->tryHttpPostToShareEndpoint($url, '', $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)) { \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); return true; } @@ -100,23 +100,24 @@ class Notifications { * @return bool */ public function sendRemoteUnShare($remote, $id, $token) { - $url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT; + $url = rtrim($remote, '/'); $fields = array('token' => $token, 'format' => 'json'); $url = $this->addressHandler->removeProtocolFromUrl($url); - $result = $this->tryHttpPost($url, $fields); + $result = $this->tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields); $status = json_decode($result['result'], true); - return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)); } /** * try http post first with https and then with http as a fallback * - * @param string $url + * @param string $remoteDomain + * @param string $urlSuffix * @param array $fields post parameters * @return array */ - private function tryHttpPost($url, array $fields) { + private function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) { $client = $this->httpClientService->newClient(); $protocol = 'https://'; $result = [ @@ -124,9 +125,11 @@ class Notifications { 'result' => '', ]; $try = 0; + while ($result['success'] === false && $try < 2) { + $endpoint = $this->discoveryManager->getShareEndpoint($protocol . $remoteDomain); try { - $response = $client->post($protocol . $url, [ + $response = $client->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, [ 'body' => $fields ]); $result['result'] = $response->getBody(); @@ -140,5 +143,4 @@ class Notifications { return $result; } - } diff --git a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php new file mode 100644 index 0000000000..9ae62b1ae4 --- /dev/null +++ b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php @@ -0,0 +1,194 @@ + + * + * @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 + * + */ + +namespace OCA\FederatedFileSharing\Tests; + +use OCA\FederatedFileSharing\DiscoveryManager; +use OCP\Http\Client\IClient; +use OCP\Http\Client\IClientService; +use OCP\ICache; +use OCP\ICacheFactory; +use Test\TestCase; + +class DiscoveryManagerTest extends TestCase { + /** @var ICache */ + private $cache; + /** @var IClient */ + private $client; + /** @var DiscoveryManager */ + private $discoveryManager; + + public function setUp() { + parent::setUp(); + $this->cache = $this->getMock('\OCP\ICache'); + /** @var ICacheFactory $cacheFactory */ + $cacheFactory = $this->getMockBuilder('\OCP\ICacheFactory') + ->disableOriginalConstructor()->getMock(); + $cacheFactory + ->expects($this->once()) + ->method('create') + ->with('ocs-discovery') + ->willReturn($this->cache); + + $this->client = $this->getMockBuilder('\OCP\Http\Client\IClient') + ->disableOriginalConstructor()->getMock(); + /** @var IClientService $clientService */ + $clientService = $this->getMockBuilder('\OCP\Http\Client\IClientService') + ->disableOriginalConstructor()->getMock(); + $clientService + ->expects($this->once()) + ->method('newClient') + ->willReturn($this->client); + + $this->discoveryManager = new DiscoveryManager( + $cacheFactory, + $clientService + ); + } + + public function testWithMalformedFormattedEndpointCached() { + $response = $this->getMock('\OCP\Http\Client\IResponse'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('CertainlyNotJson'); + $this->client + ->expects($this->once()) + ->method('get') + ->with('https://myhost.com/ocs-provider/', []) + ->willReturn($response); + $this->cache + ->expects($this->at(0)) + ->method('get') + ->with('https://myhost.com') + ->willReturn(null); + $this->cache + ->expects($this->at(1)) + ->method('set') + ->with('https://myhost.com', '{"webdav":"\/public.php\/webdav","share":"\/ocs\/v1.php\/cloud\/shares"}'); + $this->cache + ->expects($this->at(2)) + ->method('get') + ->with('https://myhost.com') + ->willReturn('{"webdav":"\/public.php\/webdav","share":"\/ocs\/v1.php\/cloud\/shares"}'); + + $this->assertSame('/public.php/webdav', $this->discoveryManager->getWebDavEndpoint('https://myhost.com')); + $this->assertSame('/ocs/v1.php/cloud/shares', $this->discoveryManager->getShareEndpoint('https://myhost.com')); + } + + public function testGetWebDavEndpointWithValidFormattedEndpointAndNotCached() { + $response = $this->getMock('\OCP\Http\Client\IResponse'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('{"version":2,"services":{"PRIVATE_DATA":{"version":1,"endpoints":{"store":"\/ocs\/v2.php\/privatedata\/setattribute","read":"\/ocs\/v2.php\/privatedata\/getattribute","delete":"\/ocs\/v2.php\/privatedata\/deleteattribute"}},"SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/apps\/files_sharing\/api\/v1\/shares"}},"FEDERATED_SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/cloud\/shares","webdav":"\/public.php\/MyCustomEndpoint\/"}},"ACTIVITY":{"version":1,"endpoints":{"list":"\/ocs\/v2.php\/cloud\/activity"}},"PROVISIONING":{"version":1,"endpoints":{"user":"\/ocs\/v2.php\/cloud\/users","groups":"\/ocs\/v2.php\/cloud\/groups","apps":"\/ocs\/v2.php\/cloud\/apps"}}}}'); + $this->client + ->expects($this->once()) + ->method('get') + ->with('https://myhost.com/ocs-provider/', []) + ->willReturn($response); + + $expectedResult = '/public.php/MyCustomEndpoint/'; + $this->assertSame($expectedResult, $this->discoveryManager->getWebDavEndpoint('https://myhost.com')); + } + + public function testGetWebDavEndpointWithValidFormattedEndpointWithoutDataAndNotCached() { + $response = $this->getMock('\OCP\Http\Client\IResponse'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('{"version":2,"PRIVATE_DATA":{"version":1,"endpoints":{"store":"\/ocs\/v2.php\/privatedata\/setattribute","read":"\/ocs\/v2.php\/privatedata\/getattribute","delete":"\/ocs\/v2.php\/privatedata\/deleteattribute"}},"SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/apps\/files_sharing\/api\/v1\/shares"}},"FEDERATED_SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/cloud\/shares","webdav":"\/public.php\/MyCustomEndpoint\/"}},"ACTIVITY":{"version":1,"endpoints":{"list":"\/ocs\/v2.php\/cloud\/activity"}},"PROVISIONING":{"version":1,"endpoints":{"user":"\/ocs\/v2.php\/cloud\/users","groups":"\/ocs\/v2.php\/cloud\/groups","apps":"\/ocs\/v2.php\/cloud\/apps"}}}'); + $this->client + ->expects($this->once()) + ->method('get') + ->with('https://myhost.com/ocs-provider/', []) + ->willReturn($response); + + $expectedResult = '/public.php/webdav'; + $this->assertSame($expectedResult, $this->discoveryManager->getWebDavEndpoint('https://myhost.com')); + } + + public function testGetShareEndpointWithValidFormattedEndpointAndNotCached() { + $response = $this->getMock('\OCP\Http\Client\IResponse'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('{"version":2,"services":{"PRIVATE_DATA":{"version":1,"endpoints":{"store":"\/ocs\/v2.php\/privatedata\/setattribute","read":"\/ocs\/v2.php\/privatedata\/getattribute","delete":"\/ocs\/v2.php\/privatedata\/deleteattribute"}},"SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/apps\/files_sharing\/api\/v1\/shares"}},"FEDERATED_SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/cloud\/MyCustomShareEndpoint","webdav":"\/public.php\/MyCustomEndpoint\/"}},"ACTIVITY":{"version":1,"endpoints":{"list":"\/ocs\/v2.php\/cloud\/activity"}},"PROVISIONING":{"version":1,"endpoints":{"user":"\/ocs\/v2.php\/cloud\/users","groups":"\/ocs\/v2.php\/cloud\/groups","apps":"\/ocs\/v2.php\/cloud\/apps"}}}}'); + $this->client + ->expects($this->once()) + ->method('get') + ->with('https://myhost.com/ocs-provider/', []) + ->willReturn($response); + + $expectedResult = '/ocs/v2.php/cloud/MyCustomShareEndpoint'; + $this->assertSame($expectedResult, $this->discoveryManager->getShareEndpoint('https://myhost.com')); + } + + public function testWithMaliciousEndpointCached() { + $response = $this->getMock('\OCP\Http\Client\IResponse'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('{"version":2,"services":{"PRIVATE_DATA":{"version":1,"endpoints":{"store":"\/ocs\/v2.php\/privatedata\/setattribute","read":"\/ocs\/v2.php\/privatedata\/getattribute","delete":"\/ocs\/v2.php\/privatedata\/deleteattribute"}},"SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/apps\/files_sharing\/api\/v1\/shares"}},"FEDERATED_SHARING":{"version":1,"endpoints":{"share":"\/ocs\/v2.php\/cl@oud\/MyCustomShareEndpoint","webdav":"\/public.php\/MyC:ustomEndpoint\/"}},"ACTIVITY":{"version":1,"endpoints":{"list":"\/ocs\/v2.php\/cloud\/activity"}},"PROVISIONING":{"version":1,"endpoints":{"user":"\/ocs\/v2.php\/cloud\/users","groups":"\/ocs\/v2.php\/cloud\/groups","apps":"\/ocs\/v2.php\/cloud\/apps"}}}}'); + $this->client + ->expects($this->once()) + ->method('get') + ->with('https://myhost.com/ocs-provider/', []) + ->willReturn($response); + $this->cache + ->expects($this->at(0)) + ->method('get') + ->with('https://myhost.com') + ->willReturn(null); + $this->cache + ->expects($this->at(1)) + ->method('set') + ->with('https://myhost.com', '{"webdav":"\/public.php\/webdav","share":"\/ocs\/v1.php\/cloud\/shares"}'); + $this->cache + ->expects($this->at(2)) + ->method('get') + ->with('https://myhost.com') + ->willReturn('{"webdav":"\/public.php\/webdav","share":"\/ocs\/v1.php\/cloud\/shares"}'); + + $this->assertSame('/public.php/webdav', $this->discoveryManager->getWebDavEndpoint('https://myhost.com')); + $this->assertSame('/ocs/v1.php/cloud/shares', $this->discoveryManager->getShareEndpoint('https://myhost.com')); + } +} diff --git a/apps/files_sharing/ajax/external.php b/apps/files_sharing/ajax/external.php index 1efe4356b4..76f9d5d766 100644 --- a/apps/files_sharing/ajax/external.php +++ b/apps/files_sharing/ajax/external.php @@ -57,13 +57,17 @@ if (\OC\Share\Helper::isSameUserOnSameServer($owner, $remote, $currentUser, $cur exit(); } - +$discoveryManager = new \OCA\FederatedFileSharing\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, \OC::$server->getUserSession()->getUser()->getUID() ); diff --git a/apps/files_sharing/api/remote.php b/apps/files_sharing/api/remote.php index 8b47955b51..1b5eb28aa8 100644 --- a/apps/files_sharing/api/remote.php +++ b/apps/files_sharing/api/remote.php @@ -24,6 +24,7 @@ namespace OCA\Files_Sharing\API; use OC\Files\Filesystem; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\Files_Sharing\External\Manager; class Remote { @@ -35,12 +36,17 @@ class Remote { * @return \OC_OCS_Result */ public static function getOpenShares($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); @@ -54,12 +60,17 @@ class Remote { * @return \OC_OCS_Result */ public static function acceptShare($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); @@ -80,12 +91,17 @@ class Remote { * @return \OC_OCS_Result */ public static function declineShare($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); @@ -123,12 +139,17 @@ class Remote { * @return \OC_OCS_Result */ public static function getShares($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); @@ -146,12 +167,17 @@ class Remote { * @return \OC_OCS_Result */ public static function getShare($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); @@ -172,12 +198,17 @@ class Remote { * @return \OC_OCS_Result */ public static function unshare($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $externalManager = new Manager( \OC::$server->getDatabaseConnection(), Filesystem::getMountManager(), Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, \OC_User::getUser() ); diff --git a/apps/files_sharing/api/server2server.php b/apps/files_sharing/api/server2server.php index f04ddc81b8..6da95ed654 100644 --- a/apps/files_sharing/api/server2server.php +++ b/apps/files_sharing/api/server2server.php @@ -25,6 +25,7 @@ namespace OCA\Files_Sharing\API; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\Files_Sharing\Activity; use OCP\Files\NotFoundException; @@ -70,12 +71,17 @@ class Server2Server { \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 ); diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php index e4a2262fec..64c7517456 100644 --- a/apps/files_sharing/appinfo/application.php +++ b/apps/files_sharing/appinfo/application.php @@ -24,6 +24,7 @@ namespace OCA\Files_Sharing\AppInfo; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\Files_Sharing\MountProvider; use OCP\AppFramework\App; use OC\AppFramework\Utility\SimpleContainer; @@ -76,12 +77,17 @@ class Application extends App { $container->registerService('ExternalManager', function (SimpleContainer $c) use ($server) { $user = $server->getUserSession()->getUser(); $uid = $user ? $user->getUID() : null; + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); return new \OCA\Files_Sharing\External\Manager( $server->getDatabaseConnection(), \OC\Files\Filesystem::getMountManager(), \OC\Files\Filesystem::getLoader(), $server->getHTTPHelper(), $server->getNotificationManager(), + $discoveryManager, $uid ); }); diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php index 84de1da69f..ec48744962 100644 --- a/apps/files_sharing/lib/external/manager.php +++ b/apps/files_sharing/lib/external/manager.php @@ -27,6 +27,7 @@ namespace OCA\Files_Sharing\External; use OC\Files\Filesystem; +use OCA\FederatedFileSharing\DiscoveryManager; use OCP\Files; use OCP\Notification\IManager; @@ -62,6 +63,8 @@ class Manager { * @var IManager */ private $notificationManager; + /** @var DiscoveryManager */ + private $discoveryManager; /** * @param \OCP\IDBConnection $connection @@ -69,16 +72,23 @@ class Manager { * @param \OCP\Files\Storage\IStorageFactory $storageLoader * @param \OC\HTTPHelper $httpHelper * @param IManager $notificationManager + * @param DiscoveryManager $discoveryManager * @param string $uid */ - public function __construct(\OCP\IDBConnection $connection, \OC\Files\Mount\Manager $mountManager, - \OCP\Files\Storage\IStorageFactory $storageLoader, \OC\HTTPHelper $httpHelper, IManager $notificationManager, $uid) { + public function __construct(\OCP\IDBConnection $connection, + \OC\Files\Mount\Manager $mountManager, + \OCP\Files\Storage\IStorageFactory $storageLoader, + \OC\HTTPHelper $httpHelper, + IManager $notificationManager, + DiscoveryManager $discoveryManager, + $uid) { $this->connection = $connection; $this->mountManager = $mountManager; $this->storageLoader = $storageLoader; $this->httpHelper = $httpHelper; $this->uid = $uid; $this->notificationManager = $notificationManager; + $this->discoveryManager = $discoveryManager; } /** @@ -246,13 +256,13 @@ class Manager { */ private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) { - $url = rtrim($remote, '/') . \OCP\Share::BASE_PATH_TO_SHARE_API . '/' . $remoteId . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT; + $url = rtrim($remote, '/') . $this->discoveryManager->getShareEndpoint($remote) . '/' . $remoteId . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT; $fields = array('token' => $token); $result = $this->httpHelper->post($url, $fields); $status = json_decode($result['result'], true); - return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)); } /** diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php index ed391f331a..41f7bef589 100644 --- a/apps/files_sharing/lib/external/storage.php +++ b/apps/files_sharing/lib/external/storage.php @@ -67,6 +67,11 @@ class Storage extends DAV implements ISharedStorage { private $manager; public function __construct($options) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); + $this->manager = $options['manager']; $this->certificateManager = $options['certificateManager']; $this->remote = $options['remote']; @@ -79,7 +84,7 @@ class Storage extends DAV implements ISharedStorage { $root = ''; } $secure = $protocol === 'https'; - $root = rtrim($root, '/') . '/public.php/webdav'; + $root = rtrim($root, '/') . $discoveryManager->getWebDavEndpoint($this->remote); $this->mountPoint = $options['mountpoint']; $this->token = $options['token']; parent::__construct(array( diff --git a/apps/files_sharing/lib/hooks.php b/apps/files_sharing/lib/hooks.php index 166905b9aa..e3f24d0226 100644 --- a/apps/files_sharing/lib/hooks.php +++ b/apps/files_sharing/lib/hooks.php @@ -25,16 +25,22 @@ namespace OCA\Files_Sharing; use OC\Files\Filesystem; +use OCA\FederatedFileSharing\DiscoveryManager; class Hooks { public static function deleteUser($params) { + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $manager = new External\Manager( \OC::$server->getDatabaseConnection(), \OC\Files\Filesystem::getMountManager(), \OC\Files\Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, $params['uid']); $manager->removeUserShares($params['uid']); diff --git a/apps/files_sharing/tests/external/managertest.php b/apps/files_sharing/tests/external/managertest.php index f73fedaf05..2836a3dc02 100644 --- a/apps/files_sharing/tests/external/managertest.php +++ b/apps/files_sharing/tests/external/managertest.php @@ -24,6 +24,7 @@ namespace OCA\Files_Sharing\Tests\External; use OC\Files\Storage\StorageFactory; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\Files_Sharing\External\Manager; use OCA\Files_Sharing\External\MountProvider; use OCA\Files_Sharing\Tests\TestCase; @@ -64,6 +65,10 @@ class ManagerTest extends TestCase { $this->user = \OC::$server->getUserManager()->get($this->uid); $this->mountManager = new \OC\Files\Mount\Manager(); $this->httpHelper = $httpHelper = $this->getMockBuilder('\OC\HTTPHelper')->disableOriginalConstructor()->getMock(); + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); /** @var \OC\HTTPHelper $httpHelper */ $this->manager = new Manager( \OC::$server->getDatabaseConnection(), @@ -71,6 +76,7 @@ class ManagerTest extends TestCase { new StorageFactory(), $httpHelper, \OC::$server->getNotificationManager(), + $discoveryManager, $this->uid ); $this->mountProvider = new MountProvider(\OC::$server->getDatabaseConnection(), function() { diff --git a/apps/files_sharing/tests/server2server.php b/apps/files_sharing/tests/server2server.php index a282f92ecb..298f1008f7 100644 --- a/apps/files_sharing/tests/server2server.php +++ b/apps/files_sharing/tests/server2server.php @@ -153,14 +153,19 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase { function testDeleteUser($toDelete, $expected, $remainingUsers) { $this->createDummyS2SShares(); + $discoveryManager = new \OCA\FederatedFileSharing\DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); $manager = new OCA\Files_Sharing\External\Manager( \OC::$server->getDatabaseConnection(), \OC\Files\Filesystem::getMountManager(), \OC\Files\Filesystem::getLoader(), \OC::$server->getHTTPHelper(), \OC::$server->getNotificationManager(), + $discoveryManager, $toDelete - ); + ); $manager->removeUserShares($toDelete); diff --git a/lib/private/share/constants.php b/lib/private/share/constants.php index e2b87d7247..e60eb98832 100644 --- a/lib/private/share/constants.php +++ b/lib/private/share/constants.php @@ -40,8 +40,6 @@ class Constants { const TOKEN_LENGTH = 15; // old (oc7) length is 32, keep token length in db at least that for compatibility - const BASE_PATH_TO_SHARE_API = '/ocs/v1.php/cloud/shares'; - protected static $shareTypeUserAndGroups = -1; protected static $shareTypeGroupUserUnique = 2; protected static $backends = array(); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 149dd082bb..4453e3758b 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -38,6 +38,7 @@ namespace OC\Share; use OC\Files\Filesystem; +use OCA\FederatedFileSharing\DiscoveryManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IUserSession; use OCP\IDBConnection; @@ -2620,19 +2621,25 @@ class Share extends Constants { /** * try http post first with https and then with http as a fallback * - * @param string $url + * @param string $remoteDomain + * @param string $urlSuffix * @param array $fields post parameters * @return array */ - private static function tryHttpPost($url, $fields) { + private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) { $protocol = 'https://'; $result = [ 'success' => false, 'result' => '', ]; $try = 0; + $discoveryManager = new DiscoveryManager( + \OC::$server->getMemCacheFactory(), + \OC::$server->getHTTPClientService() + ); while ($result['success'] === false && $try < 2) { - $result = \OC::$server->getHTTPHelper()->post($protocol . $url, $fields); + $endpoint = $discoveryManager->getShareEndpoint($protocol . $remoteDomain); + $result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields); $try++; $protocol = 'http://'; } @@ -2655,7 +2662,7 @@ class Share extends Constants { list($user, $remote) = Helper::splitUserRemote($shareWith); if ($user && $remote) { - $url = $remote . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT; + $url = $remote; $local = \OC::$server->getURLGenerator()->getAbsoluteURL('/'); @@ -2669,10 +2676,10 @@ class Share extends Constants { ); $url = self::removeProtocolFromUrl($url); - $result = self::tryHttpPost($url, $fields); + $result = self::tryHttpPostToShareEndpoint($url, '', $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)) { \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); return true; } @@ -2691,13 +2698,13 @@ class Share extends Constants { * @return bool */ private static function sendRemoteUnshare($remote, $id, $token) { - $url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT; + $url = rtrim($remote, '/'); $fields = array('token' => $token, 'format' => 'json'); $url = self::removeProtocolFromUrl($url); - $result = self::tryHttpPost($url, $fields); + $result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields); $status = json_decode($result['result'], true); - return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)); } /** diff --git a/lib/private/share20/providerfactory.php b/lib/private/share20/providerfactory.php index bb440d2e01..c2980b2251 100644 --- a/lib/private/share20/providerfactory.php +++ b/lib/private/share20/providerfactory.php @@ -21,6 +21,7 @@ namespace OC\Share20; use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\Notifications; use OCA\FederatedFileSharing\TokenHandler; @@ -91,9 +92,14 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->getURLGenerator(), $l ); + $discoveryManager = new DiscoveryManager( + $this->serverContainer->getMemCacheFactory(), + $this->serverContainer->getHTTPClientService() + ); $notifications = new Notifications( $addressHandler, - $this->serverContainer->getHTTPClientService() + $this->serverContainer->getHTTPClientService(), + $discoveryManager ); $tokenHandler = new TokenHandler( $this->serverContainer->getSecureRandom()