From e9b35dc5dab193c4f2e80978cb86c049c432f38c Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 10 Feb 2020 10:14:16 -0100 Subject: [PATCH 1/2] (GlobalScale) lock federation to internal Signed-off-by: Maxence Lange --- .../Controller/RequestHandlerController.php | 5 +- apps/dav/appinfo/v1/publicwebdav.php | 5 +- apps/dav/lib/Connector/PublicAuth.php | 21 ++- .../lib/AppInfo/Application.php | 8 +- .../Controller/MountPublicLinkController.php | 4 + .../Controller/RequestHandlerController.php | 4 +- .../lib/FederatedShareProvider.php | 100 +++++++++--- .../lib/Notifications.php | 10 +- .../lib/Settings/Admin.php | 11 +- .../lib/ocm/CloudFederationProviderFiles.php | 12 ++ .../templates/settings-admin.php | 14 +- .../files_sharing/lib/AppInfo/Application.php | 3 +- apps/files_sharing/lib/External/Manager.php | 27 ++++ .../lib/External/MountProvider.php | 3 +- .../Federation/CloudFederationFactory.php | 6 +- .../Federation/CloudFederationShare.php | 29 +++- lib/private/GlobalScale/Config.php | 146 +++++++++++++++++- .../Federation/ICloudFederationFactory.php | 4 +- .../Federation/ICloudFederationShare.php | 11 ++ lib/public/GlobalScale/IConfig.php | 60 ++++++- 20 files changed, 428 insertions(+), 55 deletions(-) diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index c31ff10e1d733..bdd617f3bb6f9 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -122,7 +122,6 @@ public function __construct($appName, * Example: curl -H "Content-Type: application/json" -X POST -d '{"shareWith":"admin1@serve1","name":"welcome server2.txt","description":"desc","providerId":"2","owner":"admin2@http://localhost/server2","ownerDisplayName":"admin2 display","shareType":"user","resourceType":"file","protocol":{"name":"webdav","options":{"sharedSecret":"secret","permissions":"webdav-property"}}}' http://localhost/server/index.php/ocm/shares */ public function addShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $protocol, $shareType, $resourceType) { - // check if all required parameters are set if ($shareWith === null || $name === null || @@ -177,6 +176,8 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $ $ownerDisplayName = $ownerDisplayName === null ? $owner : $ownerDisplayName; $sharedByDisplayName = $sharedByDisplayName === null ? $sharedBy : $sharedByDisplayName; + $password = ''; + // sharedBy* parameter is optional, if nothing is set we assume that it is the same user as the owner if ($sharedBy === null) { $sharedBy = $owner; @@ -185,7 +186,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $ try { $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType); - $share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType); + $share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType, $password); $share->setProtocol($protocol); $provider->shareReceived($share); } catch (ProviderDoesNotExistsException $e) { diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php index f0e3cc6438dd4..8279aea0a5e5b 100644 --- a/apps/dav/appinfo/v1/publicwebdav.php +++ b/apps/dav/appinfo/v1/publicwebdav.php @@ -29,6 +29,8 @@ * */ +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; + // load needed apps $RUNTIME_APPTYPES = ['filesystem', 'authentication', 'logging']; @@ -41,7 +43,8 @@ $authBackend = new OCA\DAV\Connector\PublicAuth( \OC::$server->getRequest(), \OC::$server->getShareManager(), - \OC::$server->getSession() + \OC::$server->getSession(), + \OC::$server->query(IGlobalScaleConfig::class) ); $authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend); diff --git a/apps/dav/lib/Connector/PublicAuth.php b/apps/dav/lib/Connector/PublicAuth.php index d92fbc68df3b7..39a267fb72856 100644 --- a/apps/dav/lib/Connector/PublicAuth.php +++ b/apps/dav/lib/Connector/PublicAuth.php @@ -30,6 +30,8 @@ namespace OCA\DAV\Connector; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; +use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; @@ -56,17 +58,24 @@ class PublicAuth extends AbstractBasic { /** @var IRequest */ private $request; + /** @var IGlobalScaleConfig */ + private $gsConfig; + + /** * @param IRequest $request * @param IManager $shareManager * @param ISession $session + * @param IGlobalScaleConfig $gsConfig */ public function __construct(IRequest $request, IManager $shareManager, - ISession $session) { + ISession $session, + IGlobalScaleConfig $gsConfig) { $this->request = $request; $this->shareManager = $shareManager; $this->session = $session; + $this->gsConfig = $gsConfig; // setup realm $defaults = new \OCP\Defaults(); @@ -117,11 +126,21 @@ protected function validateUserPass($username, $password) { return false; } } else if ($share->getShareType() === IShare::TYPE_REMOTE) { + // there is no password in federation share ? return true; } else { return false; } } else { + + if ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) { + if ($this->gsConfig->allowedOutgoingFederation('', $share->getToken(), $password)) { + return true; + } + + return false; + } + return true; } } diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index dc1b8541f07ee..641f2835c7fc2 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -37,7 +37,8 @@ use OCA\FederatedFileSharing\Notifier; use OCA\FederatedFileSharing\OCM\CloudFederationProviderFiles; use OCP\AppFramework\App; -use OCP\GlobalScale\IConfig; + +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; class Application extends App { @@ -68,6 +69,7 @@ function() use ($container) { $server->getURLGenerator(), $server->getCloudFederationFactory(), $server->getCloudFederationProviderManager(), + $server->query(IGlobalScaleConfig::class), $server->getDatabaseConnection(), $server->getGroupManager() ); @@ -109,7 +111,7 @@ function() use ($container) { $federatedShareProvider = $this->getFederatedShareProvider(); $manager->registerNotifierService(Notifier::class); - + $eventDispatcher->addListener( 'OCA\Files::loadAdditionalScripts', function() use ($federatedShareProvider) { @@ -166,7 +168,7 @@ protected function initFederatedShareProvider() { \OC::$server->getConfig(), \OC::$server->getUserManager(), \OC::$server->getCloudIdManager(), - $c->query(IConfig::class), + $c->query(IGlobalScaleConfig::class), \OC::$server->getCloudFederationProviderManager() ); diff --git a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php index c0c9ce9bc8c94..9a9c75bf34ca4 100644 --- a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php +++ b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php @@ -196,6 +196,10 @@ public function askForFederatedShare($token, $remote, $password = '', $owner = ' $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL()); + if (!$this->federatedShareProvider->allowedIncomingFederation($cloudId->getRemote(), $token, $password)) { + return new JSONResponse(['message' => $this->l->t('Server to server sharing is only available internally')], Http::STATUS_BAD_REQUEST); + } + $httpClient = $this->clientService->newClient(); try { diff --git a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php index e772d088f24b4..18b899afa774d 100644 --- a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php +++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php @@ -146,6 +146,7 @@ public function createShare() { $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; + $password = isset($_POST['password']) ? $_POST['password'] : ''; if ($ownerFederatedId === null) { $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); @@ -167,7 +168,8 @@ public function createShare() { $sharedBy, $token, 'user', - 'file' + 'file', + $password ); try { diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index cf5ee133ea629..e6957c43b5107 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -40,6 +40,7 @@ use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; @@ -92,7 +93,7 @@ class FederatedShareProvider implements IShareProvider { /** @var ICloudIdManager */ private $cloudIdManager; - /** @var \OCP\GlobalScale\IConfig */ + /** @var IGlobalScaleConfig */ private $gsConfig; /** @var ICloudFederationProviderManager */ @@ -114,22 +115,22 @@ class FederatedShareProvider implements IShareProvider { * @param IConfig $config * @param IUserManager $userManager * @param ICloudIdManager $cloudIdManager - * @param \OCP\GlobalScale\IConfig $globalScaleConfig + * @param IGlobalScaleConfig $globalScaleConfig * @param ICloudFederationProviderManager $cloudFederationProviderManager */ public function __construct( - IDBConnection $connection, - AddressHandler $addressHandler, - Notifications $notifications, - TokenHandler $tokenHandler, - IL10N $l10n, - ILogger $logger, - IRootFolder $rootFolder, - IConfig $config, - IUserManager $userManager, - ICloudIdManager $cloudIdManager, - \OCP\GlobalScale\IConfig $globalScaleConfig, - ICloudFederationProviderManager $cloudFederationProviderManager + IDBConnection $connection, + AddressHandler $addressHandler, + Notifications $notifications, + TokenHandler $tokenHandler, + IL10N $l10n, + ILogger $logger, + IRootFolder $rootFolder, + IConfig $config, + IUserManager $userManager, + ICloudIdManager $cloudIdManager, + IGlobalScaleConfig $globalScaleConfig, + ICloudFederationProviderManager $cloudFederationProviderManager ) { $this->dbConnection = $connection; $this->addressHandler = $addressHandler; @@ -193,8 +194,16 @@ public function create(IShare $share) { } - // don't allow federated shares if source and target server are the same $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); + // check that cloudId is an allowed recipient + if (!$this->allowedOutgoingFederation($cloudId->getRemote())) { + $message = 'Not allowed to create a federated share outside of the internal GlobalScale.'; + $message_t = $this->l->t($message); + $this->logger->debug($message, ['app' => 'Federated File Sharing']); + throw new \Exception($message_t); + } + + // don't allow federated shares if source and target server are the same $currentServer = $this->addressHandler->generateRemoteURL(); $currentUser = $sharedBy; if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { @@ -274,6 +283,14 @@ protected function createFederatedShare(IShare $share) { $sharedByFederatedId = $cloudId->getId(); } $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); + // if in GS and recipient is internal, generate password + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); + $password = ''; + if ($this->gsConfig->remoteIsInternal($remote)) { + $password = $this->gsConfig->generateInternalKey($token); + } + $share->setPassword($password); + $send = $this->notifications->sendRemoteShare( $token, $share->getSharedWith(), @@ -283,7 +300,8 @@ protected function createFederatedShare(IShare $share) { $ownerCloudId->getId(), $share->getSharedBy(), $sharedByFederatedId, - $share->getShareType() + $share->getShareType(), + $share->getPassword() ); if ($send === false) { @@ -977,13 +995,16 @@ public function userDeletedFromGroup($uid, $gid) { /** * check if users from other Nextcloud instances are allowed to mount public links share by this instance * + * @param string $remote + * * @return bool */ public function isOutgoingServer2serverShareEnabled() { - if ($this->gsConfig->onlyInternalFederation()) { - return false; + if ($this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::OUTGOING)) { + return true; } $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); + return ($result === 'yes'); } @@ -993,10 +1014,11 @@ public function isOutgoingServer2serverShareEnabled() { * @return bool */ public function isIncomingServer2serverShareEnabled() { - if ($this->gsConfig->onlyInternalFederation()) { - return false; + if ($this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::INCOMING)) { + return true; } $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); + return ($result === 'yes'); } @@ -1007,10 +1029,11 @@ public function isIncomingServer2serverShareEnabled() { * @return bool */ public function isOutgoingServer2serverGroupShareEnabled() { - if ($this->gsConfig->onlyInternalFederation()) { - return false; + if ($this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::OUTGOING)) { + return true; } $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no'); + return ($result === 'yes'); } @@ -1020,13 +1043,39 @@ public function isOutgoingServer2serverGroupShareEnabled() { * @return bool */ public function isIncomingServer2serverGroupShareEnabled() { - if ($this->gsConfig->onlyInternalFederation()) { - return false; + if ($this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::INCOMING)) { + return true; } $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no'); + return ($result === 'yes'); } + + /** + * @param string $remote + * + * @return bool + * @since 19.0.0 + */ + public function allowedOutgoingFederation(string $remote) { + return $this->gsConfig->allowedOutgoingFederation($remote); + } + + + /** + * @param string $token + * @param string $key + * @param string $remote + * + * @return bool + * @since 19.0.0 + */ + public function allowedIncomingFederation(string $remote, string $token, string $key): bool { + return $this->gsConfig->allowedIncomingFederation($remote, $token, $key); + } + + /** * check if federated group sharing is supported, therefore the OCM API need to be enabled * @@ -1047,6 +1096,7 @@ public function isLookupServerQueriesEnabled() { return true; } $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); + return ($result === 'yes'); } @@ -1062,6 +1112,7 @@ public function isLookupServerUploadEnabled() { return false; } $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); + return ($result === 'yes'); } @@ -1130,4 +1181,5 @@ public function getAllShares(): iterable { } $cursor->closeCursor(); } + } diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index 5df2173263ca9..269de6576b75f 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -88,11 +88,13 @@ public function __construct( * @param string $sharedBy * @param string $sharedByFederatedId * @param int $shareType (can be a remote user or group share) + * @param string $password (used to confirm that share is internal to GS) + * * @return bool * @throws \OC\HintException * @throws \OC\ServerNotAvailableException */ - public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId, $shareType) { + public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId, $shareType, string $password = '') { list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); @@ -109,7 +111,8 @@ public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ 'sharedBy' => $sharedBy, 'sharedByFederatedId' => $sharedByFederatedId, 'remote' => $local, - 'shareType' => $shareType + 'shareType' => $shareType, + 'password' => $password ); $result = $this->tryHttpPostToShareEndpoint($remote, '', $fields); @@ -394,7 +397,8 @@ protected function tryOCMEndPoint($remoteDomain, $fields, $action) { $fields['sharedBy'], $fields['token'], $fields['shareType'], - 'file' + 'file', + '' ); return $this->federationProviderManager->sendShare($share); case 'reshare': diff --git a/apps/federatedfilesharing/lib/Settings/Admin.php b/apps/federatedfilesharing/lib/Settings/Admin.php index 821421294e562..b7fbd23c3dd17 100644 --- a/apps/federatedfilesharing/lib/Settings/Admin.php +++ b/apps/federatedfilesharing/lib/Settings/Admin.php @@ -27,7 +27,7 @@ use OCA\FederatedFileSharing\FederatedShareProvider; use OCP\AppFramework\Http\TemplateResponse; -use OCP\GlobalScale\IConfig; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\Settings\ISettings; class Admin implements ISettings { @@ -35,16 +35,16 @@ class Admin implements ISettings { /** @var FederatedShareProvider */ private $fedShareProvider; - /** @var IConfig */ + /** @var IGlobalScaleConfig */ private $gsConfig; /** * Admin constructor. * * @param FederatedShareProvider $fedShareProvider - * @param IConfig $globalScaleConfig + * @param IGlobalScaleConfig $globalScaleConfig */ - public function __construct(FederatedShareProvider $fedShareProvider, IConfig $globalScaleConfig) { + public function __construct(FederatedShareProvider $fedShareProvider, IGlobalScaleConfig $globalScaleConfig) { $this->fedShareProvider = $fedShareProvider; $this->gsConfig = $globalScaleConfig; } @@ -55,7 +55,8 @@ public function __construct(FederatedShareProvider $fedShareProvider, IConfig $g public function getForm() { $parameters = [ - 'internalOnly' => $this->gsConfig->onlyInternalFederation(), + 'incomingInternalOnly' => $this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::INCOMING), + 'outgoingInternalOnly' => $this->gsConfig->onlyInternalFederation(IGlobalScaleConfig::OUTGOING), 'outgoingServer2serverShareEnabled' => $this->fedShareProvider->isOutgoingServer2serverShareEnabled(), 'incomingServer2serverShareEnabled' => $this->fedShareProvider->isIncomingServer2serverShareEnabled(), 'federatedGroupSharingSupported' => $this->fedShareProvider->isFederatedGroupSharingSupported(), diff --git a/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php index e2e2f26ad3a83..b4d2cbc2f6dfd 100644 --- a/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/ocm/CloudFederationProviderFiles.php @@ -43,6 +43,7 @@ use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; use OCP\Files\NotFoundException; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\ILogger; @@ -93,6 +94,9 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { /** @var ICloudFederationProviderManager */ private $cloudFederationProviderManager; + /** @var IGlobalScaleConfig */ + private $gsConfig; + /** @var IDBConnection */ private $connection; @@ -114,6 +118,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { * @param IURLGenerator $urlGenerator * @param ICloudFederationFactory $cloudFederationFactory * @param ICloudFederationProviderManager $cloudFederationProviderManager + * @param IGlobalScaleConfig $gsConfig * @param IDBConnection $connection * @param IGroupManager $groupManager */ @@ -129,6 +134,7 @@ public function __construct(IAppManager $appManager, IURLGenerator $urlGenerator, ICloudFederationFactory $cloudFederationFactory, ICloudFederationProviderManager $cloudFederationProviderManager, + IGlobalScaleConfig $gsConfig, IDBConnection $connection, IGroupManager $groupManager ) { @@ -144,6 +150,7 @@ public function __construct(IAppManager $appManager, $this->urlGenerator = $urlGenerator; $this->cloudFederationFactory = $cloudFederationFactory; $this->cloudFederationProviderManager = $cloudFederationProviderManager; + $this->gsConfig = $gsConfig; $this->connection = $connection; $this->groupManager = $groupManager; } @@ -187,6 +194,10 @@ public function shareReceived(ICloudFederationShare $share) { } $token = $share->getShareSecret(); + if (!$this->federatedShareProvider->allowedIncomingFederation($remote, $token, $share->getPassword())) { + throw new ProviderCouldNotAddShareException('Server only support internal federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE); + } + $name = $share->getResourceName(); $owner = $share->getOwnerDisplayName(); $sharedBy = $share->getSharedByDisplayName(); @@ -239,6 +250,7 @@ public function shareReceived(ICloudFederationShare $share) { \OC::$server->query(\OCP\OCS\IDiscoveryService::class), \OC::$server->getCloudFederationProviderManager(), \OC::$server->getCloudFederationFactory(), + $this->gsConfig, \OC::$server->getGroupManager(), \OC::$server->getUserManager(), $shareWith diff --git a/apps/federatedfilesharing/templates/settings-admin.php b/apps/federatedfilesharing/templates/settings-admin.php index fbbeecd88bf05..f79daeedb2066 100644 --- a/apps/federatedfilesharing/templates/settings-admin.php +++ b/apps/federatedfilesharing/templates/settings-admin.php @@ -5,7 +5,7 @@ style('federatedfilesharing', 'settings-admin'); ?> - +

@@ -17,6 +17,7 @@

t('Adjust how people can share between servers.')); ?>

+

/> @@ -24,6 +25,9 @@ t('Allow users on this server to send shares to other servers'));?>

+ + +

/> @@ -31,7 +35,10 @@ t('Allow users on this server to receive shares from other servers'));?>

+ + +

/> @@ -39,6 +46,8 @@ t('Allow users on this server to send shares to groups on other servers'));?>

+ +

/> @@ -47,6 +56,7 @@

+

/> @@ -54,6 +64,7 @@ t('Search global and public address book for users'));?>

+

/> @@ -61,6 +72,7 @@ t('Allow users to publish their data to a global and public address book'));?>

+

diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index d8019cba24075..45dd3864dc659 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -36,7 +36,6 @@ use OCA\Files_Sharing\Controller\ExternalSharesController; use OCA\Files_Sharing\Controller\ShareController; use OCA\Files_Sharing\External\Manager; -use OCA\Files_Sharing\Listener\GlobalShareAcceptanceListener; use OCA\Files_Sharing\Listener\LoadAdditionalListener; use OCA\Files_Sharing\Listener\LoadSidebarListener; use OCA\Files_Sharing\Listener\UserShareAcceptanceListener; @@ -54,6 +53,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudIdManager; use OCP\Files\Config\IMountProviderCollection; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\IContainer; use OCP\IGroup; use OCP\IServerContainer; @@ -131,6 +131,7 @@ public function __construct(array $urlParams = []) { $server->query(\OCP\OCS\IDiscoveryService::class), $server->getCloudFederationProviderManager(), $server->getCloudFederationFactory(), + $server->query(IGlobalScaleConfig::class), $server->getGroupManager(), $server->getUserManager(), $uid diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php index c58e6e7b98a78..e612068ea515d 100644 --- a/apps/files_sharing/lib/External/Manager.php +++ b/apps/files_sharing/lib/External/Manager.php @@ -37,6 +37,7 @@ use OCP\Federation\ICloudFederationProviderManager; use OCP\Files; use OCP\Files\Storage\IStorageFactory; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\Http\Client\IClientService; use OCP\IDBConnection; use OCP\IGroupManager; @@ -90,6 +91,9 @@ class Manager { /** @var ICloudFederationFactory */ private $cloudFederationFactory; + /** @var IGlobalScaleConfig */ + private $gsConfig; + /** @var IGroupManager */ private $groupManager; @@ -105,6 +109,7 @@ class Manager { * @param IDiscoveryService $discoveryService * @param ICloudFederationProviderManager $cloudFederationProviderManager * @param ICloudFederationFactory $cloudFederationFactory + * @param IGlobalScaleConfig $globalScaleConfig * @param IGroupManager $groupManager * @param IUserManager $userManager * @param string $uid @@ -117,6 +122,7 @@ public function __construct(IDBConnection $connection, IDiscoveryService $discoveryService, ICloudFederationProviderManager $cloudFederationProviderManager, ICloudFederationFactory $cloudFederationFactory, + IGlobalScaleConfig $globalScaleConfig, IGroupManager $groupManager, IUserManager $userManager, $uid) { @@ -129,6 +135,7 @@ public function __construct(IDBConnection $connection, $this->discoveryService = $discoveryService; $this->cloudFederationProviderManager = $cloudFederationProviderManager; $this->cloudFederationFactory = $cloudFederationFactory; + $this->gsConfig = $globalScaleConfig; $this->groupManager = $groupManager; $this->userManager = $userManager; } @@ -465,6 +472,8 @@ public function getMount($data) { $mountPoint = '/' . $this->uid . '/files' . $data['mountpoint']; $data['mountpoint'] = $mountPoint; $data['certificateManager'] = \OC::$server->getCertificateManager($this->uid); + $data['password'] = $this->generateGSPassword($data['token'], ''); + return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader); } @@ -650,4 +659,22 @@ private function getShares($accepted) { return $result ? $shares->fetchAll() : []; } + + + /** + * Generate a password based on jwt key on GS setup. + * The condition is that it will be generated only to reach an internal GlobalScale share. + * + * @param string $token + * @param string $remote + * + * @return string + */ + public function generateGSPassword(string $token, string $remote): string { + if (!$this->gsConfig->remoteIsInternal($remote)) { + return ''; + } + + return $this->gsConfig->generateInternalKey($token); + } } diff --git a/apps/files_sharing/lib/External/MountProvider.php b/apps/files_sharing/lib/External/MountProvider.php index 992aba48c6671..1b8a2a1feddd2 100644 --- a/apps/files_sharing/lib/External/MountProvider.php +++ b/apps/files_sharing/lib/External/MountProvider.php @@ -38,7 +38,7 @@ class MountProvider implements IMountProvider { private $connection; /** - * @var callable + * @var Manager */ private $managerProvider; @@ -67,6 +67,7 @@ public function getMount(IUser $user, $data, IStorageFactory $storageFactory) { $data['cloudId'] = $this->cloudIdManager->getCloudId($data['owner'], $data['remote']); $data['certificateManager'] = \OC::$server->getCertificateManager($user->getUID()); $data['HttpClientService'] = \OC::$server->getHTTPClientService(); + $data['password'] = $manager->generateGSPassword($data['token'], $data['remote']); return new Mount(self::STORAGE, $mountPoint, $data, $manager, $storageFactory); } diff --git a/lib/private/Federation/CloudFederationFactory.php b/lib/private/Federation/CloudFederationFactory.php index f1bae28523464..8d983462ed3a9 100644 --- a/lib/private/Federation/CloudFederationFactory.php +++ b/lib/private/Federation/CloudFederationFactory.php @@ -43,12 +43,14 @@ class CloudFederationFactory implements ICloudFederationFactory { * @param string $sharedSecret used to authenticate requests across servers * @param string $shareType ('group' or 'user' share) * @param $resourceType ('file', 'calendar',...) + * @param $password + * * @return ICloudFederationShare * * @since 14.0.0 */ - public function getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $sharedSecret, $shareType, $resourceType) { - return new CloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $shareType, $resourceType, $sharedSecret); + public function getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $sharedSecret, $shareType, $resourceType, $password) { + return new CloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $shareType, $resourceType, $sharedSecret, $password); } /** diff --git a/lib/private/Federation/CloudFederationShare.php b/lib/private/Federation/CloudFederationShare.php index 50a01d46b3f49..81a243fa61c9c 100644 --- a/lib/private/Federation/CloudFederationShare.php +++ b/lib/private/Federation/CloudFederationShare.php @@ -38,6 +38,7 @@ class CloudFederationShare implements ICloudFederationShare { 'ownerDisplayName' => '', 'sharedBy' => '', 'sharedByDisplayName' => '', + 'password' => '', 'protocol' => [] ]; @@ -55,6 +56,7 @@ class CloudFederationShare implements ICloudFederationShare { * @param string $shareType ('group' or 'user' share) * @param string $resourceType ('file', 'calendar',...) * @param string $sharedSecret + * @param string $password */ public function __construct($shareWith = '', $name = '', @@ -66,7 +68,8 @@ public function __construct($shareWith = '', $sharedByDisplayName = '', $shareType = '', $resourceType = '', - $sharedSecret = '' + $sharedSecret = '', + $password = '' ) { $this->setShareWith($shareWith); $this->setResourceName($name); @@ -85,7 +88,7 @@ public function __construct($shareWith = '', ]); $this->setShareType($shareType); $this->setResourceType($resourceType); - + $this->setPassword($password); } /** @@ -213,6 +216,17 @@ public function setShareType($shareType) { } } + + /** + * @param $password + * + * @since 19.0.0 + */ + public function setPassword($password) { + $this->share['password'] = $password; + } + + /** * get the whole share, ready to send out * @@ -355,4 +369,15 @@ public function getShareSecret() { public function getProtocol() { return $this->share['protocol']; } + + + /** + * get password + * + * @return string + */ + public function getPassword(): string { + return $this->share['password']; + } + } diff --git a/lib/private/GlobalScale/Config.php b/lib/private/GlobalScale/Config.php index c1a70c946478f..7d2c388886690 100644 --- a/lib/private/GlobalScale/Config.php +++ b/lib/private/GlobalScale/Config.php @@ -24,9 +24,22 @@ namespace OC\GlobalScale; +use daita\NcSmallPhpTools\Exceptions\RequestContentException; +use daita\NcSmallPhpTools\Exceptions\RequestNetworkException; +use daita\NcSmallPhpTools\Exceptions\RequestResultNotJsonException; +use daita\NcSmallPhpTools\Exceptions\RequestResultSizeException; +use daita\NcSmallPhpTools\Exceptions\RequestServerException; +use daita\NcSmallPhpTools\Model\Request; +use daita\NcSmallPhpTools\Traits\TRequest; +use OCP\GlobalScale\IConfig as IGlobalScaleConfig; use OCP\IConfig; -class Config implements \OCP\GlobalScale\IConfig { + +class Config implements IGlobalScaleConfig { + + + use TRequest; + /** @var IConfig */ private $config; @@ -43,21 +56,24 @@ public function __construct(IConfig $config) { /** * check if global scale is enabled * - * @since 12.0.1 * @return bool + * @since 12.0.1 */ public function isGlobalScaleEnabled() { $enabled = $this->config->getSystemValue('gs.enabled', false); + return $enabled !== false; } /** * check if federation should only be used internally in a global scale setup * - * @since 12.0.1 + * @param string $type since 19.0.0 + * * @return bool + * @since 12.0.1 */ - public function onlyInternalFederation() { + public function onlyInternalFederation(string $type) { // if global scale is disabled federation works always globally $gsEnabled = $this->isGlobalScaleEnabled(); if ($gsEnabled === false) { @@ -66,7 +82,127 @@ public function onlyInternalFederation() { $enabled = $this->config->getSystemValue('gs.federation', 'internal'); - return $enabled === 'internal'; + $type = strtolower($type); + $typeEnabled = 'internal'; + if (in_array($type, [IGlobalScaleConfig::INCOMING, IGlobalScaleConfig::OUTGOING])) { + $typeEnabled = $this->config->getSystemValue('gs.federation.' . $type, 'internal'); + } + + return $enabled === 'internal' && $typeEnabled === 'internal'; + } + + + /** + * @param string $remote + * @param string $token + * @param string $key + * + * @return bool + * @since 19.0.0 + */ + public function allowedOutgoingFederation(string $remote, string $token = '', string $key = ''): bool { + if (!$this->onlyInternalFederation(self::OUTGOING)) { + return true; + } + + if ($key !== '' && $token !== '') { + return $this->keyIsInternal($token, $key); + } + + return $this->remoteIsInternal($remote); + } + + + /** + * @param string $remote + * @param string $token + * @param string $key + * + * @return bool + * @since 19.0.0 + */ + public function allowedIncomingFederation(string $remote, string $token, string $key): bool { + if (!$this->onlyInternalFederation(self::INCOMING)) { + return true; + } + + if (!$this->remoteIsInternal($remote)) { + return false; + } + + return $this->keyIsInternal($token, $key); + } + + + /** + * @param string $token + * + * @return string + */ + public function generateInternalKey(string $token): string { + $jwt = $this->config->getSystemValue('gss.jwt.key', ''); + if ($jwt === '' || $token === '') { + return ''; + } + + return md5($token . '-' . $jwt); + } + + + /** + * @param string $remote + * + * @return bool + */ + public function remoteIsInternal(string $remote): bool { + if (!$this->isGlobalScaleEnabled()) { + return false; + } + + $tmp = parse_url($remote, PHP_URL_HOST); + $remote = ($tmp === null) ? $remote : $tmp; + + if (in_array($remote, $this->getGSInstances())) { + return true; + } + + return false; + } + + + /** + * @param string $token + * @param string $key + * + * @return bool + */ + private function keyIsInternal(string $token, string $key): bool { + if ($key === $this->generateInternalKey($token)) { + return true; + } + + return false; + } + + + /** + * @return array + */ + public function getGSInstances(): array { + /** @var string $lookup */ + $lookup = $this->config->getSystemValue('lookup_server', ''); + + $request = new Request('/instances', Request::TYPE_GET); + $request->setAddressFromUrl($lookup); + + try { + return $instances = $this->retrieveJson($request); + } catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException | RequestResultNotJsonException $e) { + \OC::$server->getLogger() + ->log(2, 'Issue while retrieving instances from lookup: ' . $e->getMessage()); + + return []; + } } } diff --git a/lib/public/Federation/ICloudFederationFactory.php b/lib/public/Federation/ICloudFederationFactory.php index 4c2a874743bdc..bb8fd39027585 100644 --- a/lib/public/Federation/ICloudFederationFactory.php +++ b/lib/public/Federation/ICloudFederationFactory.php @@ -46,11 +46,13 @@ interface ICloudFederationFactory { * @param string $sharedSecret used to authenticate requests across servers * @param string $shareType ('group' or 'user' share) * @param $resourceType ('file', 'calendar',...) + * @param $password + * * @return ICloudFederationShare * * @since 14.0.0 */ - public function getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $sharedSecret, $shareType, $resourceType); + public function getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $sharedSecret, $shareType, $resourceType, $password); /** * get a Cloud FederationNotification object to prepare a notification you diff --git a/lib/public/Federation/ICloudFederationShare.php b/lib/public/Federation/ICloudFederationShare.php index 41ed440529ce4..934df28fc8907 100644 --- a/lib/public/Federation/ICloudFederationShare.php +++ b/lib/public/Federation/ICloudFederationShare.php @@ -249,4 +249,15 @@ public function getShareSecret(); */ public function getProtocol(); + + /** + * return password + * + * @return string + * + * @since 19.0.0 + */ + public function getPassword(): string; + } + diff --git a/lib/public/GlobalScale/IConfig.php b/lib/public/GlobalScale/IConfig.php index 943680c44d20d..42bc947dd3e5f 100644 --- a/lib/public/GlobalScale/IConfig.php +++ b/lib/public/GlobalScale/IConfig.php @@ -33,20 +33,76 @@ */ interface IConfig { + + const INCOMING = 'incoming'; + const OUTGOING = 'outgoing'; + + /** * check if global scale is enabled * - * @since 12.0.1 * @return bool + * @since 12.0.1 */ public function isGlobalScaleEnabled(); /** * check if federation should only be used internally in a global scale setup * + * @param string $type since 19.0.0 + * + * @return bool * @since 12.0.1 + */ + public function onlyInternalFederation(string $type); + + + /** + * check if the outgoing federation is allowed, based on the $remote address + * If $token+$key is provided, only check the $token+$key (mainly for reading file purpose) + * + * @param string $remote + * @param string $token + * @param string $key + * + * @return bool + * @since 19.0.0 + */ + public function allowedOutgoingFederation(string $remote, string $token = '', string $key = ''): bool; + + + /** + * check if the incoming federation is allowed, based on the $token+$key. + * ~! If $remote is provided, only check the $remote (mainly for re-share purposing) !~ + * + * @param string $remote + * @param string $token + * @param string $key + * * @return bool + * @since 19.0.0 + */ + public function allowedIncomingFederation(string $remote, string $token, string $key): bool; + + + /** + * generate the key to confirm internal federation. + * + * @param $token + * + * @return string */ - public function onlyInternalFederation(); + public function generateInternalKey(string $token): string; + + + /** + * check that remote instance is internal. + * + * @param string $remote + * + * @return bool + */ + public function remoteIsInternal(string $remote): bool; } + From a2a0e63498ea31c002c2bf40e07859d4ca9cc67c Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 11 Feb 2020 13:15:27 -0100 Subject: [PATCH 2/2] switching to Client Signed-off-by: Maxence Lange cleaning Signed-off-by: Maxence Lange --- lib/private/GlobalScale/Config.php | 34 +++++++++++++++++------------- lib/private/Server.php | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/private/GlobalScale/Config.php b/lib/private/GlobalScale/Config.php index 7d2c388886690..5c2d58a6ce31f 100644 --- a/lib/private/GlobalScale/Config.php +++ b/lib/private/GlobalScale/Config.php @@ -24,32 +24,29 @@ namespace OC\GlobalScale; -use daita\NcSmallPhpTools\Exceptions\RequestContentException; -use daita\NcSmallPhpTools\Exceptions\RequestNetworkException; -use daita\NcSmallPhpTools\Exceptions\RequestResultNotJsonException; -use daita\NcSmallPhpTools\Exceptions\RequestResultSizeException; -use daita\NcSmallPhpTools\Exceptions\RequestServerException; -use daita\NcSmallPhpTools\Model\Request; -use daita\NcSmallPhpTools\Traits\TRequest; use OCP\GlobalScale\IConfig as IGlobalScaleConfig; +use OCP\Http\Client\IClientService; use OCP\IConfig; class Config implements IGlobalScaleConfig { - use TRequest; - + /** @var IClientService */ + private $clientService; /** @var IConfig */ private $config; + /** * Config constructor. * + * @param IClientService $clientService * @param IConfig $config */ - public function __construct(IConfig $config) { + public function __construct(IClientService $clientService, IConfig $config) { + $this->clientService = $clientService; $this->config = $config; } @@ -192,17 +189,24 @@ public function getGSInstances(): array { /** @var string $lookup */ $lookup = $this->config->getSystemValue('lookup_server', ''); - $request = new Request('/instances', Request::TYPE_GET); - $request->setAddressFromUrl($lookup); + if ($lookup === '') { + return []; + } + + $client = $this->clientService->newClient(); + $url = rtrim($lookup, '/') . '/instances'; try { - return $instances = $this->retrieveJson($request); - } catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException | RequestResultNotJsonException $e) { + $response = $client->get($url, ['connect_timeout' => 10]); + } catch (\Exception $e) { \OC::$server->getLogger() - ->log(2, 'Issue while retrieving instances from lookup: ' . $e->getMessage()); + ->warning('Issue while retrieving instances from lookup: ' . $e->getMessage()); return []; } + + return json_decode($response->getBody(), true); } } + diff --git a/lib/private/Server.php b/lib/private/Server.php index caaacab0a0a1e..3631be88e8612 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1232,7 +1232,7 @@ public function __construct($webRoot, \OC\Config $config) { }); $this->registerService(IConfig::class, function (Server $c) { - return new GlobalScale\Config($c->getConfig()); + return new GlobalScale\Config($c->getHTTPClientService(), $c->getConfig()); }); $this->registerService(ICloudFederationProviderManager::class, function (Server $c) {