diff options
author | Lukas Reschke <lukas@owncloud.com> | 2016-02-25 20:46:01 +0100 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2016-02-26 09:26:55 +0100 |
commit | 59028cced0a4f7377de9c22012c7624440b14a56 (patch) | |
tree | dc209a35e1626309032640459bec268323e51bac /apps/federatedfilesharing/lib | |
parent | cb41b1a86397579d55e0ce96e67b80a83037d1d2 (diff) | |
download | nextcloud-server-59028cced0a4f7377de9c22012c7624440b14a56.tar.gz nextcloud-server-59028cced0a4f7377de9c22012c7624440b14a56.zip |
Add autodiscovery support to server-to-server sharing
Adds autodiscovery support to server-to-server sharing as specified in the specification. If no discovery data is found it is using the fallback ownCloud endpoints for legacy support.
Diffstat (limited to 'apps/federatedfilesharing/lib')
-rw-r--r-- | apps/federatedfilesharing/lib/discoverymanager.php | 134 | ||||
-rw-r--r-- | apps/federatedfilesharing/lib/notifications.php | 36 |
2 files changed, 153 insertions, 17 deletions
diff --git a/apps/federatedfilesharing/lib/discoverymanager.php b/apps/federatedfilesharing/lib/discoverymanager.php new file mode 100644 index 00000000000..1df510438d7 --- /dev/null +++ b/apps/federatedfilesharing/lib/discoverymanager.php @@ -0,0 +1,134 @@ +<?php +/** + * @author Lukas Reschke <lukas@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 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) { + } catch (ConnectException $e) { + } + + // 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 d778ac87828..4ec21e81cc7 100644 --- a/apps/federatedfilesharing/lib/notifications.php +++ b/apps/federatedfilesharing/lib/notifications.php @@ -1,6 +1,7 @@ <?php /** * @author Björn Schießle <schiessle@owncloud.com> + * @author Lukas Reschke <lukas@owncloud.com> * * @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; } - } |