Skip to content

Commit 26227f5

Browse files
committed
feat(ocm): event on ocm discovery and ocm request
Signed-off-by: Maxence Lange <[email protected]>
1 parent 75edec9 commit 26227f5

File tree

19 files changed

+403
-88
lines changed

19 files changed

+403
-88
lines changed

apps/cloud_federation_api/appinfo/routes.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
'url' => '/invite-accepted',
2626
'verb' => 'POST',
2727
'root' => '/ocm',
28-
]
28+
],
29+
30+
// needs to be kept at the bottom of the list
31+
[
32+
'name' => 'OCMRequest#manageOCMRequests',
33+
'url' => '/{ocmPath}',
34+
'requirements' => ['ocmPath' => '.*'],
35+
'verb' => ['GET', 'POST', 'PUT', 'DELETE'],
36+
'root' => '/ocm',
37+
],
2938
],
3039
];

apps/cloud_federation_api/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'OCA\\CloudFederationAPI\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
1111
'OCA\\CloudFederationAPI\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
1212
'OCA\\CloudFederationAPI\\Config' => $baseDir . '/../lib/Config.php',
13+
'OCA\\CloudFederationAPI\\Controller\\OCMRequestController' => $baseDir . '/../lib/Controller/OCMRequestController.php',
1314
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => $baseDir . '/../lib/Controller/RequestHandlerController.php',
1415
'OCA\\CloudFederationAPI\\Db\\FederatedInvite' => $baseDir . '/../lib/Db/FederatedInvite.php',
1516
'OCA\\CloudFederationAPI\\Db\\FederatedInviteMapper' => $baseDir . '/../lib/Db/FederatedInviteMapper.php',

apps/cloud_federation_api/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ComposerStaticInitCloudFederationAPI
2525
'OCA\\CloudFederationAPI\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
2626
'OCA\\CloudFederationAPI\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
2727
'OCA\\CloudFederationAPI\\Config' => __DIR__ . '/..' . '/../lib/Config.php',
28+
'OCA\\CloudFederationAPI\\Controller\\OCMRequestController' => __DIR__ . '/..' . '/../lib/Controller/OCMRequestController.php',
2829
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => __DIR__ . '/..' . '/../lib/Controller/RequestHandlerController.php',
2930
'OCA\\CloudFederationAPI\\Db\\FederatedInvite' => __DIR__ . '/..' . '/../lib/Db/FederatedInvite.php',
3031
'OCA\\CloudFederationAPI\\Db\\FederatedInviteMapper' => __DIR__ . '/..' . '/../lib/Db/FederatedInviteMapper.php',
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\CloudFederationAPI\Controller;
11+
12+
use JsonException;
13+
use NCU\Security\Signature\Exceptions\IncomingRequestException;
14+
use OCP\AppFramework\Http;
15+
use OCP\AppFramework\Controller;
16+
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
17+
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
18+
use OCP\AppFramework\Http\Attribute\OpenAPI;
19+
use OCP\AppFramework\Http\Attribute\PublicPage;
20+
use OCP\AppFramework\Http\DataResponse;
21+
use OCP\AppFramework\Http\JSONResponse;
22+
use OCP\AppFramework\Http\Response;
23+
use OCP\EventDispatcher\IEventDispatcher;
24+
use OCP\IRequest;
25+
use OCP\OCM\Events\OCMEndpointRequestEvent;
26+
use OCP\OCM\Exceptions\OCMArgumentException;
27+
use Psr\Log\LoggerInterface;
28+
29+
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
30+
class OCMRequestController extends Controller {
31+
public function __construct(
32+
string $appName,
33+
IRequest $request,
34+
private readonly IEventDispatcher $eventDispatcher,
35+
private readonly LoggerInterface $logger,
36+
) {
37+
parent::__construct($appName, $request);
38+
}
39+
40+
#[NoCSRFRequired]
41+
#[PublicPage]
42+
#[BruteForceProtection(action: 'receiveOcmRequest')]
43+
public function manageOCMRequests(string $ocmPath): Response {
44+
try {
45+
json_encode($ocmPath, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
46+
} catch (JsonException) {
47+
throw new OCMArgumentException('path is not UTF-8');
48+
}
49+
50+
// TODO: check signature and update $remote
51+
$remote = null;
52+
$event = new OCMEndpointRequestEvent($this->request->getMethod(), str_replace('//', '/', $ocmPath), $payload, $remote);
53+
$this->eventDispatcher->dispatchTyped($event);
54+
55+
if ($event->getResponse() === null) {
56+
return new DataResponse('', Http::STATUS_NOT_FOUND);
57+
}
58+
59+
return $event->getResponse();
60+
}
61+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OCA\CloudFederationAPI\AppInfo;
10+
11+
use NCU\Security\Signature\ISignatureManager;
12+
use Psr\Log\LoggerInterface;
13+
14+
class SignatureService {
15+
16+
public function __construct(
17+
private readonly ISignatureManager $signatureManager,
18+
private readonly LoggerInterface $logger,
19+
) {
20+
}
21+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@
714714
'OCP\\Notification\\InvalidValueException' => $baseDir . '/lib/public/Notification/InvalidValueException.php',
715715
'OCP\\Notification\\NotificationPreloadReason' => $baseDir . '/lib/public/Notification/NotificationPreloadReason.php',
716716
'OCP\\Notification\\UnknownNotificationException' => $baseDir . '/lib/public/Notification/UnknownNotificationException.php',
717+
'OCP\\OCM\\Events\\LocalOCMDiscoveryEvent' => $baseDir . '/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php',
717718
'OCP\\OCM\\Events\\ResourceTypeRegisterEvent' => $baseDir . '/lib/public/OCM/Events/ResourceTypeRegisterEvent.php',
718719
'OCP\\OCM\\Exceptions\\OCMArgumentException' => $baseDir . '/lib/public/OCM/Exceptions/OCMArgumentException.php',
719720
'OCP\\OCM\\Exceptions\\OCMProviderException' => $baseDir . '/lib/public/OCM/Exceptions/OCMProviderException.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
755755
'OCP\\Notification\\InvalidValueException' => __DIR__ . '/../../..' . '/lib/public/Notification/InvalidValueException.php',
756756
'OCP\\Notification\\NotificationPreloadReason' => __DIR__ . '/../../..' . '/lib/public/Notification/NotificationPreloadReason.php',
757757
'OCP\\Notification\\UnknownNotificationException' => __DIR__ . '/../../..' . '/lib/public/Notification/UnknownNotificationException.php',
758+
'OCP\\OCM\\Events\\LocalOCMDiscoveryEvent' => __DIR__ . '/../../..' . '/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php',
758759
'OCP\\OCM\\Events\\ResourceTypeRegisterEvent' => __DIR__ . '/../../..' . '/lib/public/OCM/Events/ResourceTypeRegisterEvent.php',
759760
'OCP\\OCM\\Exceptions\\OCMArgumentException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMArgumentException.php',
760761
'OCP\\OCM\\Exceptions\\OCMProviderException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMProviderException.php',

lib/private/AppFramework/Routing/RouteParser.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ private function processRoute(array $route, string $appName, string $routeNamePr
7575
$root = $this->buildRootPrefix($route, $appName, $routeNamePrefix);
7676

7777
$url = $root . '/' . ltrim($route['url'], '/');
78-
$verb = strtoupper($route['verb'] ?? 'GET');
7978

8079
$split = explode('#', $name, 3);
8180
if (count($split) !== 2) {
@@ -95,7 +94,7 @@ private function processRoute(array $route, string $appName, string $routeNamePr
9594
$routeName = strtolower($routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix);
9695

9796
$routeObject = new Route($url);
98-
$routeObject->method($verb);
97+
$routeObject->method((array) ($route['verb'] ?? ['GET']));
9998

10099
// optionally register requirements for route. This is used to
101100
// tell the route parser how url parameters should be matched
@@ -174,7 +173,6 @@ private function processResources(array $resources, string $appName, string $rou
174173
$url = $root . '/' . ltrim($config['url'], '/');
175174
$method = $action['name'];
176175

177-
$verb = strtoupper($action['verb'] ?? 'GET');
178176
$collectionAction = $action['on-collection'] ?? false;
179177
if (!$collectionAction) {
180178
$url .= '/{id}';
@@ -188,7 +186,7 @@ private function processResources(array $resources, string $appName, string $rou
188186
$routeName = $routeNamePrefix . $appName . '.' . strtolower($resource) . '.' . $method;
189187

190188
$route = new Route($url);
191-
$route->method($verb);
189+
$route->method((array) ($action['verb'] ?? ['GET']));
192190

193191
$route->defaults(['caller' => [$appName, $controllerName, $actionName]]);
194192

lib/private/OCM/Model/OCMProvider.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
use NCU\Security\Signature\Model\Signatory;
1313
use OCP\OCM\Exceptions\OCMArgumentException;
1414
use OCP\OCM\Exceptions\OCMProviderException;
15-
use OCP\OCM\ICapabilityAwareOCMProvider;
15+
use OCP\OCM\IOCMProvider;
1616
use OCP\OCM\IOCMResource;
1717

1818
/**
1919
* @since 28.0.0
2020
*/
21-
class OCMProvider implements ICapabilityAwareOCMProvider {
21+
class OCMProvider implements IOCMProvider {
2222
private bool $enabled = false;
2323
private string $apiVersion = '';
2424
private string $inviteAcceptDialog = '';
@@ -124,12 +124,12 @@ public function getProvider(): string {
124124
* @return $this
125125
*/
126126
public function setCapabilities(array $capabilities): static {
127-
foreach ($capabilities as $value) {
128-
if (!in_array($value, $this->capabilities)) {
129-
array_push($this->capabilities, $value);
130-
}
131-
}
127+
// since ocm 1.2, removing slashes from capabilities
128+
$capabilities = array_map(static function (string $capability): string {
129+
return ltrim($capability, '/');
130+
}, $capabilities);
132131

132+
$this->capabilities = array_unique(array_merge($this->capabilities, $capabilities));
133133
return $this;
134134
}
135135

@@ -139,6 +139,7 @@ public function setCapabilities(array $capabilities): static {
139139
public function getCapabilities(): array {
140140
return $this->capabilities;
141141
}
142+
142143
/**
143144
* create a new resource to later add it with {@see IOCMProvider::addResourceType()}
144145
* @return IOCMResource

lib/private/OCM/OCMDiscoveryService.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
use OCP\ICacheFactory;
2424
use OCP\IConfig;
2525
use OCP\IURLGenerator;
26+
use OCP\OCM\Events\LocalOCMDiscoveryEvent;
2627
use OCP\OCM\Events\ResourceTypeRegisterEvent;
2728
use OCP\OCM\Exceptions\OCMProviderException;
28-
use OCP\OCM\ICapabilityAwareOCMProvider;
2929
use OCP\OCM\IOCMDiscoveryService;
30+
use OCP\OCM\IOCMProvider;
3031
use Psr\Log\LoggerInterface;
3132

3233
/**
@@ -36,8 +37,8 @@ class OCMDiscoveryService implements IOCMDiscoveryService {
3637
private ICache $cache;
3738
public const API_VERSION = '1.1.0';
3839

39-
private ?ICapabilityAwareOCMProvider $localProvider = null;
40-
/** @var array<string, ICapabilityAwareOCMProvider> */
40+
private ?IOCMProvider $localProvider = null;
41+
/** @var array<string, IOCMProvider> */
4142
private array $remoteProviders = [];
4243

4344
public function __construct(
@@ -57,10 +58,10 @@ public function __construct(
5758
* @param string $remote
5859
* @param bool $skipCache
5960
*
60-
* @return ICapabilityAwareOCMProvider
61+
* @return IOCMProvider
6162
* @throws OCMProviderException
6263
*/
63-
public function discover(string $remote, bool $skipCache = false): ICapabilityAwareOCMProvider {
64+
public function discover(string $remote, bool $skipCache = false): IOCMProvider {
6465
$remote = rtrim($remote, '/');
6566
if (!str_starts_with($remote, 'http://') && !str_starts_with($remote, 'https://')) {
6667
// if scheme not specified, we test both;
@@ -154,9 +155,13 @@ public function discover(string $remote, bool $skipCache = false): ICapabilityAw
154155
}
155156

156157
/**
157-
* @return ICapabilityAwareOCMProvider
158+
* return discovery data about local instance.
159+
* will generate an event to that 3rd party can define new resources.
160+
*
161+
* @param bool $fullDetails complete details, include public key. Set to FALSE for client (capabilities) purpose.
162+
* @return IOCMProvider
158163
*/
159-
public function getLocalOCMProvider(bool $fullDetails = true): ICapabilityAwareOCMProvider {
164+
public function getLocalOCMProvider(bool $fullDetails = true): IOCMProvider {
160165
if ($this->localProvider !== null) {
161166
return $this->localProvider;
162167
}
@@ -176,7 +181,7 @@ public function getLocalOCMProvider(bool $fullDetails = true): ICapabilityAwareO
176181
$provider->setEnabled(true);
177182
$provider->setApiVersion(self::API_VERSION);
178183
$provider->setEndPoint(substr($url, 0, $pos));
179-
$provider->setCapabilities(['/invite-accepted', '/notifications', '/shares']);
184+
$provider->setCapabilities(['invite-accepted', 'notifications', 'shares']);
180185

181186
// The inviteAcceptDialog is available from the contacts app, if this config value is set
182187
$inviteAcceptDialog = $this->appConfig->getValueString('core', ConfigLexicon::OCM_INVITE_ACCEPT_DIALOG);
@@ -207,6 +212,10 @@ public function getLocalOCMProvider(bool $fullDetails = true): ICapabilityAwareO
207212
}
208213
}
209214

215+
$event = new LocalOCMDiscoveryEvent($provider);
216+
$this->eventDispatcher->dispatchTyped($event);
217+
218+
// deprecated since 33.0.0
210219
$event = new ResourceTypeRegisterEvent($provider);
211220
$this->eventDispatcher->dispatchTyped($event);
212221

0 commit comments

Comments
 (0)