Signed-off-by: Maxence Lange <maxence@artificial-owl.com>tags/v28.0.0beta1
@@ -6,6 +6,7 @@ declare(strict_types=1); | |||
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> | |||
* | |||
* @author Joas Schilling <coding@schilljs.com> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
@@ -27,15 +28,21 @@ return [ | |||
'routes' => [ | |||
[ | |||
'name' => 'RequestHandler#addShare', | |||
'url' => '/ocm/shares', | |||
'url' => '/shares', | |||
'verb' => 'POST', | |||
'root' => '', | |||
'root' => '/ocm', | |||
], | |||
[ | |||
'name' => 'RequestHandler#receiveNotification', | |||
'url' => '/ocm/notifications', | |||
'url' => '/notifications', | |||
'verb' => 'POST', | |||
'root' => '', | |||
'root' => '/ocm', | |||
], | |||
// [ | |||
// 'name' => 'RequestHandler#inviteAccepted', | |||
// 'url' => '/invite-accepted', | |||
// 'verb' => 'POST', | |||
// 'root' => '/ocm', | |||
// ] | |||
], | |||
]; |
@@ -1,9 +1,13 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> | |||
* | |||
* @author Bjoern Schiessle <bjoern@schiessle.org> | |||
* @author Kate Döen <kate.doeen@nextcloud.com> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
@@ -21,18 +25,22 @@ | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCA\CloudFederationAPI; | |||
use OC\OCM\Model\OCMProvider; | |||
use OC\OCM\Model\OCMResource; | |||
use OCP\Capabilities\ICapability; | |||
use OCP\IURLGenerator; | |||
use OCP\OCM\Exceptions\OCMArgumentException; | |||
class Capabilities implements ICapability { | |||
/** @var IURLGenerator */ | |||
private $urlGenerator; | |||
public const API_VERSION = '1.0-proposal1'; | |||
public function __construct(IURLGenerator $urlGenerator) { | |||
$this->urlGenerator = $urlGenerator; | |||
public function __construct( | |||
private IURLGenerator $urlGenerator, | |||
) { | |||
} | |||
/** | |||
@@ -46,32 +54,33 @@ class Capabilities implements ICapability { | |||
* resourceTypes: array{ | |||
* name: string, | |||
* shareTypes: string[], | |||
* protocols: array{ | |||
* webdav: string, | |||
* }, | |||
* }[], | |||
* }, | |||
* protocols: array<string, string> | |||
* }[], | |||
* }, | |||
* } | |||
* @throws OCMArgumentException | |||
*/ | |||
public function getCapabilities() { | |||
$url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare'); | |||
$capabilities = ['ocm' => | |||
[ | |||
'enabled' => true, | |||
'apiVersion' => '1.0-proposal1', | |||
'endPoint' => substr($url, 0, strrpos($url, '/')), | |||
'resourceTypes' => [ | |||
[ | |||
'name' => 'file', | |||
'shareTypes' => ['user', 'group'], | |||
'protocols' => [ | |||
'webdav' => '/public.php/webdav/', | |||
] | |||
], | |||
] | |||
] | |||
]; | |||
return $capabilities; | |||
$provider = new OCMProvider(); | |||
$provider->setEnabled(true); | |||
$provider->setApiVersion(self::API_VERSION); | |||
$pos = strrpos($url, '/'); | |||
if (false === $pos) { | |||
throw new OCMArgumentException('generated route should contains a slash character'); | |||
} | |||
$provider->setEndPoint(substr($url, 0, $pos)); | |||
$resource = new OCMResource(); | |||
$resource->setName('file') | |||
->setShareTypes(['user', 'group']) | |||
->setProtocols(['webdav' => '/public.php/webdav/']); | |||
$provider->setResourceTypes([$resource]); | |||
return ['ocm' => $provider->jsonSerialize()]; | |||
} | |||
} |
@@ -4,6 +4,7 @@ | |||
* | |||
* @author Bjoern Schiessle <bjoern@schiessle.org> | |||
* @author Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* @author Kate Döen <kate.doeen@nextcloud.com> | |||
* | |||
@@ -55,52 +56,19 @@ use Psr\Log\LoggerInterface; | |||
* @psalm-import-type CloudFederationApiError from ResponseDefinitions | |||
*/ | |||
class RequestHandlerController extends Controller { | |||
/** @var LoggerInterface */ | |||
private $logger; | |||
/** @var IUserManager */ | |||
private $userManager; | |||
/** @var IGroupManager */ | |||
private $groupManager; | |||
/** @var IURLGenerator */ | |||
private $urlGenerator; | |||
/** @var ICloudFederationProviderManager */ | |||
private $cloudFederationProviderManager; | |||
/** @var Config */ | |||
private $config; | |||
/** @var ICloudFederationFactory */ | |||
private $factory; | |||
/** @var ICloudIdManager */ | |||
private $cloudIdManager; | |||
public function __construct($appName, | |||
IRequest $request, | |||
LoggerInterface $logger, | |||
IUserManager $userManager, | |||
IGroupManager $groupManager, | |||
IURLGenerator $urlGenerator, | |||
ICloudFederationProviderManager $cloudFederationProviderManager, | |||
Config $config, | |||
ICloudFederationFactory $factory, | |||
ICloudIdManager $cloudIdManager | |||
public function __construct( | |||
string $appName, | |||
IRequest $request, | |||
private LoggerInterface $logger, | |||
private IUserManager $userManager, | |||
private IGroupManager $groupManager, | |||
private IURLGenerator $urlGenerator, | |||
private ICloudFederationProviderManager $cloudFederationProviderManager, | |||
private Config $config, | |||
private ICloudFederationFactory $factory, | |||
private ICloudIdManager $cloudIdManager | |||
) { | |||
parent::__construct($appName, $request); | |||
$this->logger = $logger; | |||
$this->userManager = $userManager; | |||
$this->groupManager = $groupManager; | |||
$this->urlGenerator = $urlGenerator; | |||
$this->cloudFederationProviderManager = $cloudFederationProviderManager; | |||
$this->config = $config; | |||
$this->factory = $factory; | |||
$this->cloudIdManager = $cloudIdManager; | |||
} | |||
/** | |||
@@ -128,7 +96,6 @@ class RequestHandlerController extends Controller { | |||
* 501: Share type or the resource type is not supported | |||
*/ | |||
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 || | |||
@@ -253,7 +220,6 @@ class RequestHandlerController extends Controller { | |||
* 501: The resource type is not supported | |||
*/ | |||
public function receiveNotification($notificationType, $resourceType, $providerId, ?array $notification) { | |||
// check if all required parameters are set | |||
if ($notificationType === null || | |||
$resourceType === null || | |||
@@ -311,7 +277,7 @@ class RequestHandlerController extends Controller { | |||
); | |||
} | |||
return new JSONResponse($result,Http::STATUS_CREATED); | |||
return new JSONResponse($result, Http::STATUS_CREATED); | |||
} | |||
/** |
@@ -76,13 +76,8 @@ | |||
}, | |||
"protocols": { | |||
"type": "object", | |||
"required": [ | |||
"webdav" | |||
], | |||
"properties": { | |||
"webdav": { | |||
"type": "string" | |||
} | |||
"additionalProperties": { | |||
"type": "string" | |||
} | |||
} | |||
} |
@@ -6,6 +6,7 @@ declare(strict_types=1); | |||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @author Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
@@ -29,21 +30,19 @@ namespace OCA\Files_Sharing\BackgroundJob; | |||
use OCP\AppFramework\Utility\ITimeFactory; | |||
use OCP\BackgroundJob\TimedJob; | |||
use OCP\IDBConnection; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
use OCP\OCM\IOCMDiscoveryService; | |||
use OCP\OCS\IDiscoveryService; | |||
class FederatedSharesDiscoverJob extends TimedJob { | |||
/** @var IDBConnection */ | |||
private $connection; | |||
/** @var IDiscoveryService */ | |||
private $discoveryService; | |||
public function __construct(ITimeFactory $time, | |||
IDBConnection $connection, | |||
IDiscoveryService $discoveryService) { | |||
parent::__construct($time); | |||
$this->connection = $connection; | |||
$this->discoveryService = $discoveryService; | |||
public function __construct( | |||
ITimeFactory $time, | |||
private IDBConnection $connection, | |||
private IDiscoveryService $discoveryService, | |||
private IOCMDiscoveryService $ocmDiscoveryService | |||
) { | |||
parent::__construct($time); | |||
$this->setInterval(86400); | |||
} | |||
@@ -56,6 +55,10 @@ class FederatedSharesDiscoverJob extends TimedJob { | |||
$result = $qb->execute(); | |||
while ($row = $result->fetch()) { | |||
$this->discoveryService->discover($row['remote'], 'FEDERATED_SHARING', true); | |||
try { | |||
$this->ocmDiscoveryService->discover($row['remote'], true); | |||
} catch (OCMProviderException $e) { | |||
} | |||
} | |||
$result->closeCursor(); | |||
} |
@@ -134,14 +134,14 @@ class ExternalSharesController extends Controller { | |||
} | |||
if ( | |||
$this->testUrl('https://' . $remote . '/ocs-provider/') || | |||
$this->testUrl('https://' . $remote . '/ocs-provider/index.php') || | |||
$this->testUrl('https://' . $remote . '/ocm-provider/') || | |||
$this->testUrl('https://' . $remote . '/ocm-provider/index.php') || | |||
$this->testUrl('https://' . $remote . '/status.php', true) | |||
) { | |||
return new DataResponse('https'); | |||
} elseif ( | |||
$this->testUrl('http://' . $remote . '/ocs-provider/') || | |||
$this->testUrl('http://' . $remote . '/ocs-provider/index.php') || | |||
$this->testUrl('http://' . $remote . '/ocm-provider/') || | |||
$this->testUrl('http://' . $remote . '/ocm-provider/index.php') || | |||
$this->testUrl('http://' . $remote . '/status.php', true) | |||
) { | |||
return new DataResponse('http'); |
@@ -1,4 +1,7 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2016, ownCloud, Inc. | |||
* | |||
@@ -8,6 +11,7 @@ | |||
* @author Daniel Kesselberg <mail@danielkesselberg.de> | |||
* @author Joas Schilling <coding@schilljs.com> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* @author Morris Jobke <hey@morrisjobke.de> | |||
* @author Robin Appelman <robin@icewind.nl> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
@@ -36,8 +40,8 @@ use GuzzleHttp\Exception\ConnectException; | |||
use GuzzleHttp\Exception\RequestException; | |||
use OC\Files\Storage\DAV; | |||
use OC\ForbiddenException; | |||
use OCA\Files_Sharing\ISharedStorage; | |||
use OCA\Files_Sharing\External\Manager as ExternalShareManager; | |||
use OCA\Files_Sharing\ISharedStorage; | |||
use OCP\AppFramework\Http; | |||
use OCP\Constants; | |||
use OCP\Federation\ICloudId; | |||
@@ -46,25 +50,23 @@ use OCP\Files\Storage\IDisableEncryptionStorage; | |||
use OCP\Files\Storage\IReliableEtagStorage; | |||
use OCP\Files\StorageInvalidException; | |||
use OCP\Files\StorageNotAvailableException; | |||
use OCP\Http\Client\LocalServerException; | |||
use OCP\Http\Client\IClientService; | |||
use OCP\Http\Client\LocalServerException; | |||
use OCP\ICacheFactory; | |||
use OCP\OCM\Exceptions\OCMArgumentException; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
use OCP\OCM\IOCMDiscoveryService; | |||
use OCP\Server; | |||
use Psr\Log\LoggerInterface; | |||
class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage, IReliableEtagStorage { | |||
/** @var ICloudId */ | |||
private $cloudId; | |||
/** @var string */ | |||
private $mountPoint; | |||
/** @var string */ | |||
private $token; | |||
/** @var \OCP\ICacheFactory */ | |||
private $memcacheFactory; | |||
/** @var \OCP\Http\Client\IClientService */ | |||
private $httpClient; | |||
/** @var bool */ | |||
private $updateChecked = false; | |||
/** @var ExternalShareManager */ | |||
private $manager; | |||
private ICloudId $cloudId; | |||
private string $mountPoint; | |||
private string $token; | |||
private ICacheFactory $memcacheFactory; | |||
private IClientService $httpClient; | |||
private bool $updateChecked = false; | |||
private ExternalShareManager $manager; | |||
/** | |||
* @param array{HttpClientService: IClientService, manager: ExternalShareManager, cloudId: ICloudId, mountpoint: string, token: string, password: ?string}|array $options | |||
@@ -72,32 +74,44 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage, | |||
public function __construct($options) { | |||
$this->memcacheFactory = \OC::$server->getMemCacheFactory(); | |||
$this->httpClient = $options['HttpClientService']; | |||
$this->manager = $options['manager']; | |||
$this->cloudId = $options['cloudId']; | |||
$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class); | |||
$this->logger = Server::get(LoggerInterface::class); | |||
$discoveryService = Server::get(IOCMDiscoveryService::class); | |||
[$protocol, $remote] = explode('://', $this->cloudId->getRemote()); | |||
if (str_contains($remote, '/')) { | |||
[$host, $root] = explode('/', $remote, 2); | |||
} else { | |||
$host = $remote; | |||
$root = ''; | |||
// use default path to webdav if not found on discovery | |||
try { | |||
$ocmProvider = $discoveryService->discover($this->cloudId->getRemote()); | |||
$webDavEndpoint = $ocmProvider->extractProtocolEntry('file', 'webdav'); | |||
$remote = $ocmProvider->getEndPoint(); | |||
} catch (OCMProviderException|OCMArgumentException $e) { | |||
$this->logger->notice('exception while retrieving webdav endpoint', ['exception' => $e]); | |||
$webDavEndpoint = '/public.php/webdav'; | |||
$remote = $this->cloudId->getRemote(); | |||
} | |||
$host = parse_url($remote, PHP_URL_HOST); | |||
$port = parse_url($remote, PHP_URL_PORT); | |||
$host .= (null === $port) ? '' : ':' . $port; // we add port if available | |||
// in case remote NC is on a sub folder and using deprecated ocm provider | |||
$tmpPath = rtrim(parse_url($this->cloudId->getRemote(), PHP_URL_PATH) ?? '', '/'); | |||
if (!str_starts_with($webDavEndpoint, $tmpPath)) { | |||
$webDavEndpoint = $tmpPath . $webDavEndpoint; | |||
} | |||
$secure = $protocol === 'https'; | |||
$federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING'); | |||
$webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav'; | |||
$root = rtrim($root, '/') . $webDavEndpoint; | |||
$this->mountPoint = $options['mountpoint']; | |||
$this->token = $options['token']; | |||
parent::__construct([ | |||
'secure' => $secure, | |||
'host' => $host, | |||
'root' => $root, | |||
'user' => $options['token'], | |||
'password' => (string)$options['password'] | |||
]); | |||
parent::__construct( | |||
[ | |||
'secure' => ((parse_url($remote, PHP_URL_SCHEME) ?? 'https') === 'https'), | |||
'host' => $host, | |||
'root' => $webDavEndpoint, | |||
'user' => $options['token'], | |||
'password' => (string)$options['password'] | |||
] | |||
); | |||
} | |||
public function getWatcher($path = '', $storage = null) { | |||
@@ -255,9 +269,9 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage, | |||
*/ | |||
protected function testRemote(): bool { | |||
try { | |||
return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php') | |||
|| $this->testRemoteUrl($this->getRemote() . '/ocs-provider/') | |||
|| $this->testRemoteUrl($this->getRemote() . '/status.php'); | |||
return $this->testRemoteUrl($this->getRemote() . '/ocm-provider/index.php') | |||
|| $this->testRemoteUrl($this->getRemote() . '/ocm-provider/') | |||
|| $this->testRemoteUrl($this->getRemote() . '/status.php'); | |||
} catch (\Exception $e) { | |||
return false; | |||
} |
@@ -28,6 +28,7 @@ | |||
namespace OCA\Files_Sharing\Tests; | |||
use OC\Federation\CloudId; | |||
use OCA\Files_Sharing\External\Manager as ExternalShareManager; | |||
use OCP\Http\Client\IClient; | |||
use OCP\Http\Client\IClientService; | |||
use OCP\Http\Client\IResponse; | |||
@@ -75,6 +76,7 @@ class ExternalStorageTest extends \Test\TestCase { | |||
private function getTestStorage($uri) { | |||
$certificateManager = \OC::$server->getCertificateManager(); | |||
$httpClientService = $this->createMock(IClientService::class); | |||
$manager = $this->createMock(ExternalShareManager::class); | |||
$client = $this->createMock(IClient::class); | |||
$response = $this->createMock(IResponse::class); | |||
$client | |||
@@ -98,7 +100,7 @@ class ExternalStorageTest extends \Test\TestCase { | |||
'mountpoint' => 'remoteshare', | |||
'token' => 'abcdef', | |||
'password' => '', | |||
'manager' => null, | |||
'manager' => $manager, | |||
'certificateManager' => $certificateManager, | |||
'HttpClientService' => $httpClientService, | |||
] |
@@ -79,7 +79,6 @@ $expectedFiles = [ | |||
'jest.config.ts', | |||
'lib', | |||
'occ', | |||
'ocm-provider', | |||
'ocs', | |||
'ocs-provider', | |||
'package-lock.json', |
@@ -43,6 +43,11 @@ | |||
<code>IEventListener</code> | |||
</MissingTemplateParam> | |||
</file> | |||
<file src="apps/cloud_federation_api/lib/Capabilities.php"> | |||
<LessSpecificImplementedReturnType> | |||
<code>array</code> | |||
</LessSpecificImplementedReturnType> | |||
</file> | |||
<file src="apps/comments/lib/Listener/CommentsEntityEventListener.php"> | |||
<MissingTemplateParam> | |||
<code>IEventListener</code> |
@@ -0,0 +1,92 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\Core\Controller; | |||
use Exception; | |||
use OCP\AppFramework\Controller; | |||
use OCP\AppFramework\Http; | |||
use OCP\AppFramework\Http\Attribute\IgnoreOpenAPI; | |||
use OCP\AppFramework\Http\DataResponse; | |||
use OCP\AppFramework\Http\Response; | |||
use OCP\IConfig; | |||
use OCP\IRequest; | |||
use OCP\Server; | |||
use Psr\Container\ContainerExceptionInterface; | |||
use Psr\Log\LoggerInterface; | |||
/** | |||
* Controller about the endpoint /ocm-provider/ | |||
* | |||
* @since 28.0.0 | |||
*/ | |||
class OCMController extends Controller { | |||
public function __construct( | |||
IRequest $request, | |||
private IConfig $config, | |||
private LoggerInterface $logger | |||
) { | |||
parent::__construct('core', $request); | |||
} | |||
/** | |||
* generate a OCMProvider with local data and send it as DataResponse. | |||
* This replaces the old PHP file ocm-provider/index.php | |||
* | |||
* @PublicPage | |||
* @NoCSRFRequired | |||
* | |||
* @return Response | |||
*/ | |||
#[IgnoreOpenAPI] | |||
public function discovery(): Response { | |||
try { | |||
$cap = Server::get( | |||
$this->config->getAppValue( | |||
'core', | |||
'ocm_providers', | |||
'\OCA\CloudFederationAPI\Capabilities' | |||
) | |||
); | |||
return new DataResponse( | |||
$cap->getCapabilities()['ocm'] ?? ['enabled' => false], | |||
Http::STATUS_OK, | |||
[ | |||
'X-NEXTCLOUD-OCM-PROVIDERS' => true, | |||
'Content-Type' => 'application/json' | |||
] | |||
); | |||
} catch (ContainerExceptionInterface|Exception $e) { | |||
$this->logger->error('issue during OCM discovery request', ['exception' => $e]); | |||
return new DataResponse( | |||
['message' => '/ocm-provider/ not supported'], | |||
Http::STATUS_INTERNAL_SERVER_ERROR | |||
); | |||
} | |||
} | |||
} |
@@ -4988,6 +4988,10 @@ | |||
{ | |||
"name": "guest_avatar", | |||
"description": "This controller handles guest avatar requests." | |||
}, | |||
{ | |||
"name": "ocm", | |||
"description": "Controller about the endpoint /ocm-provider/" | |||
} | |||
] | |||
} |
@@ -13,6 +13,7 @@ declare(strict_types=1); | |||
* @author John Molakvoæ <skjnldsv@protonmail.com> | |||
* @author Julius Härtl <jus@bitgrid.net> | |||
* @author Lukas Reschke <lukas@statuscode.ch> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* @author Michael Weimann <mail@michael-weimann.eu> | |||
* @author Roeland Jago Douma <roeland@famdouma.nl> | |||
* @author Thomas Müller <thomas.mueller@tmit.eu> | |||
@@ -103,6 +104,9 @@ $application->registerRoutes($this, [ | |||
// Well known requests https://tools.ietf.org/html/rfc5785 | |||
['name' => 'WellKnown#handle', 'url' => '.well-known/{service}'], | |||
// OCM Provider requests https://github.com/cs3org/OCM-API | |||
['name' => 'OCM#discovery', 'url' => '/ocm-provider/'], | |||
// Unsupported browser | |||
['name' => 'UnsupportedBrowser#index', 'url' => 'unsupported'], | |||
], |
@@ -536,6 +536,11 @@ return array( | |||
'OCP\\Notification\\IManager' => $baseDir . '/lib/public/Notification/IManager.php', | |||
'OCP\\Notification\\INotification' => $baseDir . '/lib/public/Notification/INotification.php', | |||
'OCP\\Notification\\INotifier' => $baseDir . '/lib/public/Notification/INotifier.php', | |||
'OCP\\OCM\\Exceptions\\OCMArgumentException' => $baseDir . '/lib/public/OCM/Exceptions/OCMArgumentException.php', | |||
'OCP\\OCM\\Exceptions\\OCMProviderException' => $baseDir . '/lib/public/OCM/Exceptions/OCMProviderException.php', | |||
'OCP\\OCM\\IOCMDiscoveryService' => $baseDir . '/lib/public/OCM/IOCMDiscoveryService.php', | |||
'OCP\\OCM\\IOCMProvider' => $baseDir . '/lib/public/OCM/IOCMProvider.php', | |||
'OCP\\OCM\\IOCMResource' => $baseDir . '/lib/public/OCM/IOCMResource.php', | |||
'OCP\\OCS\\IDiscoveryService' => $baseDir . '/lib/public/OCS/IDiscoveryService.php', | |||
'OCP\\PreConditionNotMetException' => $baseDir . '/lib/public/PreConditionNotMetException.php', | |||
'OCP\\Preview\\BeforePreviewFetchedEvent' => $baseDir . '/lib/public/Preview/BeforePreviewFetchedEvent.php', | |||
@@ -1074,6 +1079,7 @@ return array( | |||
'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', | |||
'OC\\Core\\Controller\\NavigationController' => $baseDir . '/core/Controller/NavigationController.php', | |||
'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php', | |||
'OC\\Core\\Controller\\OCMController' => $baseDir . '/core/Controller/OCMController.php', | |||
'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php', | |||
'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php', | |||
'OC\\Core\\Controller\\ProfileApiController' => $baseDir . '/core/Controller/ProfileApiController.php', | |||
@@ -1463,6 +1469,9 @@ return array( | |||
'OC\\Notification\\Action' => $baseDir . '/lib/private/Notification/Action.php', | |||
'OC\\Notification\\Manager' => $baseDir . '/lib/private/Notification/Manager.php', | |||
'OC\\Notification\\Notification' => $baseDir . '/lib/private/Notification/Notification.php', | |||
'OC\\OCM\\Model\\OCMProvider' => $baseDir . '/lib/private/OCM/Model/OCMProvider.php', | |||
'OC\\OCM\\Model\\OCMResource' => $baseDir . '/lib/private/OCM/Model/OCMResource.php', | |||
'OC\\OCM\\OCMDiscoveryService' => $baseDir . '/lib/private/OCM/OCMDiscoveryService.php', | |||
'OC\\OCS\\CoreCapabilities' => $baseDir . '/lib/private/OCS/CoreCapabilities.php', | |||
'OC\\OCS\\DiscoveryService' => $baseDir . '/lib/private/OCS/DiscoveryService.php', | |||
'OC\\OCS\\Exception' => $baseDir . '/lib/private/OCS/Exception.php', |
@@ -569,6 +569,11 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 | |||
'OCP\\Notification\\IManager' => __DIR__ . '/../../..' . '/lib/public/Notification/IManager.php', | |||
'OCP\\Notification\\INotification' => __DIR__ . '/../../..' . '/lib/public/Notification/INotification.php', | |||
'OCP\\Notification\\INotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/INotifier.php', | |||
'OCP\\OCM\\Exceptions\\OCMArgumentException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMArgumentException.php', | |||
'OCP\\OCM\\Exceptions\\OCMProviderException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMProviderException.php', | |||
'OCP\\OCM\\IOCMDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMDiscoveryService.php', | |||
'OCP\\OCM\\IOCMProvider' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMProvider.php', | |||
'OCP\\OCM\\IOCMResource' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMResource.php', | |||
'OCP\\OCS\\IDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCS/IDiscoveryService.php', | |||
'OCP\\PreConditionNotMetException' => __DIR__ . '/../../..' . '/lib/public/PreConditionNotMetException.php', | |||
'OCP\\Preview\\BeforePreviewFetchedEvent' => __DIR__ . '/../../..' . '/lib/public/Preview/BeforePreviewFetchedEvent.php', | |||
@@ -1107,6 +1112,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 | |||
'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', | |||
'OC\\Core\\Controller\\NavigationController' => __DIR__ . '/../../..' . '/core/Controller/NavigationController.php', | |||
'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php', | |||
'OC\\Core\\Controller\\OCMController' => __DIR__ . '/../../..' . '/core/Controller/OCMController.php', | |||
'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php', | |||
'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php', | |||
'OC\\Core\\Controller\\ProfileApiController' => __DIR__ . '/../../..' . '/core/Controller/ProfileApiController.php', | |||
@@ -1496,6 +1502,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 | |||
'OC\\Notification\\Action' => __DIR__ . '/../../..' . '/lib/private/Notification/Action.php', | |||
'OC\\Notification\\Manager' => __DIR__ . '/../../..' . '/lib/private/Notification/Manager.php', | |||
'OC\\Notification\\Notification' => __DIR__ . '/../../..' . '/lib/private/Notification/Notification.php', | |||
'OC\\OCM\\Model\\OCMProvider' => __DIR__ . '/../../..' . '/lib/private/OCM/Model/OCMProvider.php', | |||
'OC\\OCM\\Model\\OCMResource' => __DIR__ . '/../../..' . '/lib/private/OCM/Model/OCMResource.php', | |||
'OC\\OCM\\OCMDiscoveryService' => __DIR__ . '/../../..' . '/lib/private/OCM/OCMDiscoveryService.php', | |||
'OC\\OCS\\CoreCapabilities' => __DIR__ . '/../../..' . '/lib/private/OCS/CoreCapabilities.php', | |||
'OC\\OCS\\DiscoveryService' => __DIR__ . '/../../..' . '/lib/private/OCS/DiscoveryService.php', | |||
'OC\\OCS\\Exception' => __DIR__ . '/../../..' . '/lib/private/OCS/Exception.php', |
@@ -1,9 +1,13 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> | |||
* | |||
* @author Bjoern Schiessle <bjoern@schiessle.org> | |||
* @author Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
@@ -32,6 +36,9 @@ use OCP\Federation\ICloudFederationProviderManager; | |||
use OCP\Federation\ICloudFederationShare; | |||
use OCP\Federation\ICloudIdManager; | |||
use OCP\Http\Client\IClientService; | |||
use OCP\IConfig; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
use OCP\OCM\IOCMDiscoveryService; | |||
use Psr\Log\LoggerInterface; | |||
/** | |||
@@ -43,40 +50,16 @@ use Psr\Log\LoggerInterface; | |||
*/ | |||
class CloudFederationProviderManager implements ICloudFederationProviderManager { | |||
/** @var array list of available cloud federation providers */ | |||
private $cloudFederationProvider; | |||
/** @var IAppManager */ | |||
private $appManager; | |||
/** @var IClientService */ | |||
private $httpClientService; | |||
/** @var ICloudIdManager */ | |||
private $cloudIdManager; | |||
private LoggerInterface $logger; | |||
/** @var array cache OCM end-points */ | |||
private $ocmEndPoints = []; | |||
private $supportedAPIVersion = '1.0-proposal1'; | |||
/** | |||
* CloudFederationProviderManager constructor. | |||
* | |||
* @param IAppManager $appManager | |||
* @param IClientService $httpClientService | |||
* @param ICloudIdManager $cloudIdManager | |||
*/ | |||
public function __construct(IAppManager $appManager, | |||
IClientService $httpClientService, | |||
ICloudIdManager $cloudIdManager, | |||
LoggerInterface $logger) { | |||
$this->cloudFederationProvider = []; | |||
$this->appManager = $appManager; | |||
$this->httpClientService = $httpClientService; | |||
$this->cloudIdManager = $cloudIdManager; | |||
$this->logger = $logger; | |||
private array $cloudFederationProvider = []; | |||
public function __construct( | |||
private IConfig $config, | |||
private IAppManager $appManager, | |||
private IClientService $httpClientService, | |||
private ICloudIdManager $cloudIdManager, | |||
private IOCMDiscoveryService $discoveryService, | |||
private LoggerInterface $logger | |||
) { | |||
} | |||
@@ -130,16 +113,18 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager | |||
public function sendShare(ICloudFederationShare $share) { | |||
$cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); | |||
$ocmEndPoint = $this->getOCMEndPoint($cloudID->getRemote()); | |||
if (empty($ocmEndPoint)) { | |||
try { | |||
$ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); | |||
} catch (OCMProviderException $e) { | |||
return false; | |||
} | |||
$client = $this->httpClientService->newClient(); | |||
try { | |||
$response = $client->post($ocmEndPoint . '/shares', [ | |||
$response = $client->post($ocmProvider->getEndPoint() . '/shares', [ | |||
'body' => json_encode($share->getShare()), | |||
'headers' => ['content-type' => 'application/json'], | |||
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), | |||
'timeout' => 10, | |||
'connect_timeout' => 10, | |||
]); | |||
@@ -168,17 +153,18 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager | |||
* @return array|false | |||
*/ | |||
public function sendNotification($url, ICloudFederationNotification $notification) { | |||
$ocmEndPoint = $this->getOCMEndPoint($url); | |||
if (empty($ocmEndPoint)) { | |||
try { | |||
$ocmProvider = $this->discoveryService->discover($url); | |||
} catch (OCMProviderException $e) { | |||
return false; | |||
} | |||
$client = $this->httpClientService->newClient(); | |||
try { | |||
$response = $client->post($ocmEndPoint . '/notifications', [ | |||
$response = $client->post($ocmProvider->getEndPoint() . '/notifications', [ | |||
'body' => json_encode($notification->getMessage()), | |||
'headers' => ['content-type' => 'application/json'], | |||
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), | |||
'timeout' => 10, | |||
'connect_timeout' => 10, | |||
]); | |||
@@ -202,36 +188,4 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager | |||
public function isReady() { | |||
return $this->appManager->isEnabledForUser('cloud_federation_api'); | |||
} | |||
/** | |||
* check if server supports the new OCM api and ask for the correct end-point | |||
* | |||
* @param string $url full base URL of the cloud server | |||
* @return string | |||
*/ | |||
protected function getOCMEndPoint($url) { | |||
if (isset($this->ocmEndPoints[$url])) { | |||
return $this->ocmEndPoints[$url]; | |||
} | |||
$client = $this->httpClientService->newClient(); | |||
try { | |||
$response = $client->get($url . '/ocm-provider/', ['timeout' => 10, 'connect_timeout' => 10]); | |||
} catch (\Exception $e) { | |||
$this->ocmEndPoints[$url] = ''; | |||
return ''; | |||
} | |||
$result = $response->getBody(); | |||
$result = json_decode($result, true); | |||
$supportedVersion = isset($result['apiVersion']) && $result['apiVersion'] === $this->supportedAPIVersion; | |||
if (isset($result['endPoint']) && $supportedVersion) { | |||
$this->ocmEndPoints[$url] = $result['endPoint']; | |||
return $result['endPoint']; | |||
} | |||
$this->ocmEndPoints[$url] = ''; | |||
return ''; | |||
} | |||
} |
@@ -0,0 +1,211 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2023, Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\OCM\Model; | |||
use JsonSerializable; | |||
use OCP\OCM\Exceptions\OCMArgumentException; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
use OCP\OCM\IOCMProvider; | |||
/** | |||
* @since 28.0.0 | |||
*/ | |||
class OCMProvider implements IOCMProvider, JsonSerializable { | |||
private bool $enabled = false; | |||
private string $apiVersion = ''; | |||
private string $endPoint = ''; | |||
/** @var OCMResource[] */ | |||
private array $resourceTypes = []; | |||
/** | |||
* @param bool $enabled | |||
* | |||
* @return OCMProvider | |||
*/ | |||
public function setEnabled(bool $enabled): self { | |||
$this->enabled = $enabled; | |||
return $this; | |||
} | |||
/** | |||
* @return bool | |||
*/ | |||
public function isEnabled(): bool { | |||
return $this->enabled; | |||
} | |||
/** | |||
* @param string $apiVersion | |||
* | |||
* @return OCMProvider | |||
*/ | |||
public function setApiVersion(string $apiVersion): self { | |||
$this->apiVersion = $apiVersion; | |||
return $this; | |||
} | |||
/** | |||
* @return string | |||
*/ | |||
public function getApiVersion(): string { | |||
return $this->apiVersion; | |||
} | |||
/** | |||
* @param string $endPoint | |||
* | |||
* @return OCMProvider | |||
*/ | |||
public function setEndPoint(string $endPoint): self { | |||
$this->endPoint = $endPoint; | |||
return $this; | |||
} | |||
/** | |||
* @return string | |||
*/ | |||
public function getEndPoint(): string { | |||
return $this->endPoint; | |||
} | |||
/** | |||
* @param OCMResource $resource | |||
* | |||
* @return $this | |||
*/ | |||
public function addResourceType(OCMResource $resource): self { | |||
$this->resourceTypes[] = $resource; | |||
return $this; | |||
} | |||
/** | |||
* @param OCMResource[] $resourceTypes | |||
* | |||
* @return OCMProvider | |||
*/ | |||
public function setResourceTypes(array $resourceTypes): self { | |||
$this->resourceTypes = $resourceTypes; | |||
return $this; | |||
} | |||
/** | |||
* @return OCMResource[] | |||
*/ | |||
public function getResourceTypes(): array { | |||
return $this->resourceTypes; | |||
} | |||
/** | |||
* @param string $resourceName | |||
* @param string $protocol | |||
* | |||
* @return string | |||
* @throws OCMArgumentException | |||
*/ | |||
public function extractProtocolEntry(string $resourceName, string $protocol): string { | |||
foreach ($this->getResourceTypes() as $resource) { | |||
if ($resource->getName() === $resourceName) { | |||
$entry = $resource->getProtocols()[$protocol] ?? null; | |||
if (is_null($entry)) { | |||
throw new OCMArgumentException('protocol not found'); | |||
} | |||
return (string)$entry; | |||
} | |||
} | |||
throw new OCMArgumentException('resource not found'); | |||
} | |||
/** | |||
* import data from an array | |||
* | |||
* @param array $data | |||
* | |||
* @return self | |||
* @throws OCMProviderException in case a descent provider cannot be generated from data | |||
* @see self::jsonSerialize() | |||
*/ | |||
public function import(array $data): self { | |||
$this->setEnabled(is_bool($data['enabled'] ?? '') ? $data['enabled'] : false) | |||
->setApiVersion((string)($data['apiVersion'] ?? '')) | |||
->setEndPoint($data['endPoint'] ?? ''); | |||
$resources = []; | |||
foreach (($data['resourceTypes'] ?? []) as $resourceData) { | |||
$resource = new OCMResource(); | |||
$resources[] = $resource->import($resourceData); | |||
} | |||
$this->setResourceTypes($resources); | |||
if (!$this->looksValid()) { | |||
throw new OCMProviderException('remote provider does not look valid'); | |||
} | |||
return $this; | |||
} | |||
/** | |||
* @return bool | |||
*/ | |||
private function looksValid(): bool { | |||
return ($this->getApiVersion() !== '' && $this->getEndPoint() !== ''); | |||
} | |||
/** | |||
* @return array{ | |||
* enabled: bool, | |||
* apiVersion: string, | |||
* endPoint: string, | |||
* resourceTypes: array{ | |||
* name: string, | |||
* shareTypes: string[], | |||
* protocols: array<string, string> | |||
* }[] | |||
* } | |||
*/ | |||
public function jsonSerialize(): array { | |||
$resourceTypes = []; // this is needed for psalm | |||
foreach ($this->getResourceTypes() as $res) { | |||
$resourceTypes[] = $res->jsonSerialize(); | |||
} | |||
return [ | |||
'enabled' => $this->isEnabled(), | |||
'apiVersion' => $this->getApiVersion(), | |||
'endPoint' => $this->getEndPoint(), | |||
'resourceTypes' => $resourceTypes | |||
]; | |||
} | |||
} |
@@ -0,0 +1,125 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2023, Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\OCM\Model; | |||
use JsonSerializable; | |||
use OCP\OCM\IOCMResource; | |||
/** | |||
* @since 28.0.0 | |||
*/ | |||
class OCMResource implements IOCMResource, JsonSerializable { | |||
private string $name = ''; | |||
/** @var string[] */ | |||
private array $shareTypes = []; | |||
/** @var array<string, string> */ | |||
private array $protocols = []; | |||
/** | |||
* @param string $name | |||
* | |||
* @return OCMResource | |||
*/ | |||
public function setName(string $name): self { | |||
$this->name = $name; | |||
return $this; | |||
} | |||
/** | |||
* @return string | |||
*/ | |||
public function getName(): string { | |||
return $this->name; | |||
} | |||
/** | |||
* @param string[] $shareTypes | |||
* | |||
* @return OCMResource | |||
*/ | |||
public function setShareTypes(array $shareTypes): self { | |||
$this->shareTypes = $shareTypes; | |||
return $this; | |||
} | |||
/** | |||
* @return string[] | |||
*/ | |||
public function getShareTypes(): array { | |||
return $this->shareTypes; | |||
} | |||
/** | |||
* @param array<string, string> $protocols | |||
* | |||
* @return $this | |||
*/ | |||
public function setProtocols(array $protocols): self { | |||
$this->protocols = $protocols; | |||
return $this; | |||
} | |||
/** | |||
* @return array<string, string> | |||
*/ | |||
public function getProtocols(): array { | |||
return $this->protocols; | |||
} | |||
/** | |||
* import data from an array | |||
* | |||
* @param array $data | |||
* | |||
* @return self | |||
* @see self::jsonSerialize() | |||
*/ | |||
public function import(array $data): self { | |||
return $this->setName((string)($data['name'] ?? '')) | |||
->setShareTypes($data['shareTypes'] ?? []) | |||
->setProtocols($data['protocols'] ?? []); | |||
} | |||
/** | |||
* | |||
* @return array{ | |||
* name: string, | |||
* shareTypes: string[], | |||
* protocols: array<string, string> | |||
* } | |||
*/ | |||
public function jsonSerialize(): array { | |||
return [ | |||
'name' => $this->getName(), | |||
'shareTypes' => $this->getShareTypes(), | |||
'protocols' => $this->getProtocols() | |||
]; | |||
} | |||
} |
@@ -0,0 +1,138 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2023, Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\OCM; | |||
use JsonException; | |||
use OC\OCM\Model\OCMProvider; | |||
use OCP\AppFramework\Http; | |||
use OCP\Http\Client\IClientService; | |||
use OCP\ICache; | |||
use OCP\ICacheFactory; | |||
use OCP\IConfig; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
use OCP\OCM\IOCMDiscoveryService; | |||
use OCP\OCM\IOCMProvider; | |||
use Psr\Log\LoggerInterface; | |||
/** | |||
* @since 28.0.0 | |||
*/ | |||
class OCMDiscoveryService implements IOCMDiscoveryService { | |||
private ICache $cache; | |||
private array $supportedAPIVersion = | |||
[ | |||
'1.0-proposal1', | |||
'1.0', | |||
'1.1' | |||
]; | |||
public function __construct( | |||
ICacheFactory $cacheFactory, | |||
private IClientService $clientService, | |||
private IConfig $config, | |||
private LoggerInterface $logger | |||
) { | |||
$this->cache = $cacheFactory->createDistributed('ocm-discovery'); | |||
} | |||
/** | |||
* @param string $remote | |||
* @param bool $skipCache | |||
* | |||
* @return IOCMProvider | |||
* @throws OCMProviderException | |||
*/ | |||
public function discover(string $remote, bool $skipCache = false): IOCMProvider { | |||
$remote = rtrim($remote, '/'); | |||
$provider = new OCMProvider(); | |||
if (!$skipCache) { | |||
try { | |||
$provider->import(json_decode($this->cache->get($remote) ?? '', true, 8, JSON_THROW_ON_ERROR) ?? []); | |||
if ($this->supportedAPIVersion($provider->getApiVersion())) { | |||
return $provider; // if cache looks valid, we use it | |||
} | |||
} catch (JsonException|OCMProviderException $e) { | |||
// we ignore cache on issues | |||
} | |||
} | |||
$client = $this->clientService->newClient(); | |||
try { | |||
$response = $client->get( | |||
$remote . '/ocm-provider/', | |||
[ | |||
'timeout' => 10, | |||
'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates'), | |||
'connect_timeout' => 10, | |||
] | |||
); | |||
if ($response->getStatusCode() === Http::STATUS_OK) { | |||
$body = $response->getBody(); | |||
// update provider with data returned by the request | |||
$provider->import(json_decode($body, true, 8, JSON_THROW_ON_ERROR) ?? []); | |||
$this->cache->set($remote, $body, 60 * 60 * 24); | |||
} | |||
} catch (JsonException|OCMProviderException $e) { | |||
throw new OCMProviderException('data returned by remote seems invalid - ' . ($body ?? '')); | |||
} catch (\Exception $e) { | |||
$this->logger->warning('error while discovering ocm provider', [ | |||
'exception' => $e, | |||
'remote' => $remote | |||
]); | |||
throw new OCMProviderException('error while requesting remote ocm provider'); | |||
} | |||
if (!$this->supportedAPIVersion($provider->getApiVersion())) { | |||
throw new OCMProviderException('API version not supported'); | |||
} | |||
return $provider; | |||
} | |||
/** | |||
* Check the version from remote is supported. | |||
* The minor version of the API will be ignored: | |||
* 1.0.1 is identified as 1.0 | |||
* | |||
* @param string $version | |||
* | |||
* @return bool | |||
*/ | |||
private function supportedAPIVersion(string $version): bool { | |||
$dot1 = strpos($version, '.'); | |||
$dot2 = strpos($version, '.', $dot1 + 1); | |||
if ($dot2 > 0) { | |||
$version = substr($version, 0, $dot2); | |||
} | |||
return (in_array($version, $this->supportedAPIVersion)); | |||
} | |||
} |
@@ -124,6 +124,7 @@ use OC\Metadata\Capabilities as MetadataCapabilities; | |||
use OC\Metadata\IMetadataManager; | |||
use OC\Metadata\MetadataManager; | |||
use OC\Notification\Manager; | |||
use OC\OCM\OCMDiscoveryService; | |||
use OC\OCS\DiscoveryService; | |||
use OC\Preview\GeneratorHelper; | |||
use OC\Preview\IMagickSupport; | |||
@@ -228,6 +229,7 @@ use OCP\Lock\ILockingProvider; | |||
use OCP\Lockdown\ILockdownManager; | |||
use OCP\Log\ILogFactory; | |||
use OCP\Mail\IMailer; | |||
use OCP\OCM\IOCMDiscoveryService; | |||
use OCP\Remote\Api\IApiFactory; | |||
use OCP\Remote\IInstanceFactory; | |||
use OCP\RichObjectStrings\IValidator; | |||
@@ -1306,6 +1308,7 @@ class Server extends ServerContainer implements IServerContainer { | |||
$c->get(IClientService::class) | |||
); | |||
}); | |||
$this->registerAlias(IOCMDiscoveryService::class, OCMDiscoveryService::class); | |||
$this->registerService(ICloudIdManager::class, function (ContainerInterface $c) { | |||
return new CloudIdManager( | |||
@@ -1321,9 +1324,11 @@ class Server extends ServerContainer implements IServerContainer { | |||
$this->registerService(ICloudFederationProviderManager::class, function (ContainerInterface $c) { | |||
return new CloudFederationProviderManager( | |||
$c->get(\OCP\IConfig::class), | |||
$c->get(IAppManager::class), | |||
$c->get(IClientService::class), | |||
$c->get(ICloudIdManager::class), | |||
$c->get(IOCMDiscoveryService::class), | |||
$c->get(LoggerInterface::class) | |||
); | |||
}); |
@@ -0,0 +1,34 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2023, Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\OCM\Exceptions; | |||
use Exception; | |||
/** | |||
* @since 28.0.0 | |||
*/ | |||
class OCMArgumentException extends Exception { | |||
} |
@@ -0,0 +1,34 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright 2023, Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\OCM\Exceptions; | |||
use Exception; | |||
/** | |||
* @since 28.0.0 | |||
*/ | |||
class OCMProviderException extends Exception { | |||
} |
@@ -0,0 +1,48 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\OCM; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
/** | |||
* Discover remote OCM services | |||
* | |||
* @since 28.0.0 | |||
*/ | |||
interface IOCMDiscoveryService { | |||
/** | |||
* Discover remote OCM services | |||
* | |||
* @param string $remote address of the remote provider | |||
* @param bool $skipCache ignore cache, refresh data | |||
* | |||
* @return IOCMProvider | |||
* @throws OCMProviderException if no valid discovery data can be returned | |||
* @since 28.0.0 | |||
*/ | |||
public function discover(string $remote, bool $skipCache = false): IOCMProvider; | |||
} |
@@ -0,0 +1,143 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\OCM; | |||
use OC\OCM\Model\OCMResource; | |||
use OCP\OCM\Exceptions\OCMArgumentException; | |||
use OCP\OCM\Exceptions\OCMProviderException; | |||
/** | |||
* Model based on the Open Cloud Mesh Discovery API | |||
* @link https://github.com/cs3org/OCM-API/ | |||
* @since 28.0.0 | |||
*/ | |||
interface IOCMProvider { | |||
/** | |||
* enable OCM | |||
* | |||
* @param bool $enabled | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setEnabled(bool $enabled): self; | |||
/** | |||
* is set as enabled ? | |||
* | |||
* @return bool | |||
* @since 28.0.0 | |||
*/ | |||
public function isEnabled(): bool; | |||
/** | |||
* get set API Version | |||
* | |||
* @param string $apiVersion | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setApiVersion(string $apiVersion): self; | |||
/** | |||
* returns API version | |||
* | |||
* @return string | |||
* @since 28.0.0 | |||
*/ | |||
public function getApiVersion(): string; | |||
/** | |||
* configure endpoint | |||
* | |||
* @param string $endPoint | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setEndPoint(string $endPoint): self; | |||
/** | |||
* get configured endpoint | |||
* | |||
* @return string | |||
* @since 28.0.0 | |||
*/ | |||
public function getEndPoint(): string; | |||
/** | |||
* add a single resource to the object | |||
* | |||
* @param OCMResource $resource | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function addResourceType(OCMResource $resource): self; | |||
/** | |||
* set resources | |||
* | |||
* @param OCMResource[] $resourceTypes | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setResourceTypes(array $resourceTypes): self; | |||
/** | |||
* get all set resources | |||
* | |||
* @return IOCMResource[] | |||
* @since 28.0.0 | |||
*/ | |||
public function getResourceTypes(): array; | |||
/** | |||
* extract a specific string value from the listing of protocols, based on resource-name and protocol-name | |||
* | |||
* @param string $resourceName | |||
* @param string $protocol | |||
* | |||
* @return string | |||
* @throws OCMArgumentException | |||
* @since 28.0.0 | |||
*/ | |||
public function extractProtocolEntry(string $resourceName, string $protocol): string; | |||
/** | |||
* import data from an array | |||
* | |||
* @param array<string, int|string|bool|array> $data | |||
* | |||
* @return self | |||
* @throws OCMProviderException in case a descent provider cannot be generated from data | |||
* @since 28.0.0 | |||
*/ | |||
public function import(array $data): self; | |||
} |
@@ -0,0 +1,99 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2023 Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @author Maxence Lange <maxence@artificial-owl.com> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OCP\OCM; | |||
/** | |||
* Model based on the Open Cloud Mesh Discovery API | |||
* | |||
* @link https://github.com/cs3org/OCM-API/ | |||
* @since 28.0.0 | |||
*/ | |||
interface IOCMResource { | |||
/** | |||
* set name of the resource | |||
* | |||
* @param string $name | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setName(string $name): self; | |||
/** | |||
* get name of the resource | |||
* | |||
* @return string | |||
* @since 28.0.0 | |||
*/ | |||
public function getName(): string; | |||
/** | |||
* set share types | |||
* | |||
* @param string[] $shareTypes | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setShareTypes(array $shareTypes): self; | |||
/** | |||
* get share types | |||
* | |||
* @return string[] | |||
* @since 28.0.0 | |||
*/ | |||
public function getShareTypes(): array; | |||
/** | |||
* set available protocols | |||
* | |||
* @param array<string, string> $protocols | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function setProtocols(array $protocols): self; | |||
/** | |||
* get configured protocols | |||
* | |||
* @return array<string, string> | |||
* @since 28.0.0 | |||
*/ | |||
public function getProtocols(): array; | |||
/** | |||
* import data from an array | |||
* | |||
* @param array $data | |||
* | |||
* @return self | |||
* @since 28.0.0 | |||
*/ | |||
public function import(array $data): self; | |||
} |
@@ -1,40 +0,0 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* 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 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
require_once __DIR__ . '/../lib/base.php'; | |||
header('Content-Type: application/json'); | |||
$server = \OC::$server; | |||
$isEnabled = $server->getAppManager()->isEnabledForUser('cloud_federation_api'); | |||
if ($isEnabled) { | |||
// Make sure the routes are loaded | |||
\OC_App::loadApp('cloud_federation_api'); | |||
$capabilities = new OCA\CloudFederationAPI\Capabilities($server->getURLGenerator()); | |||
header('Content-Type: application/json'); | |||
echo json_encode($capabilities->getCapabilities()['ocm']); | |||
} else { | |||
header($_SERVER["SERVER_PROTOCOL"]." 501 Not Implemented", true, 501); | |||
exit("501 Not Implemented"); | |||
} |
@@ -43,7 +43,6 @@ | |||
<directory name="apps/workflowengine"/> | |||
<directory name="core"/> | |||
<directory name="lib"/> | |||
<directory name="ocm-provider"/> | |||
<directory name="ocs"/> | |||
<directory name="ocs-provider"/> | |||
<file name="index.php"/> |