Skip to content

Commit 958dffc

Browse files
feat(ocm): advertise exchange-token capability and token endpoint
Co-authored-by: Micke Nordin <kano@sunet.se> Signed-off-by: Micke Nordin <kano@sunet.se> Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
1 parent feb8460 commit 958dffc

9 files changed

Lines changed: 290 additions & 27 deletions

File tree

lib/private/Federation/CloudFederationFactory.php

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@
99
use OCP\Federation\ICloudFederationFactory;
1010
use OCP\Federation\ICloudFederationNotification;
1111
use OCP\Federation\ICloudFederationShare;
12+
use OCP\Federation\ICloudIdManager;
13+
use OCP\OCM\Exceptions\OCMProviderException;
14+
use OCP\OCM\IOCMDiscoveryService;
15+
use Psr\Log\LoggerInterface;
1216

1317
class CloudFederationFactory implements ICloudFederationFactory {
18+
public function __construct(
19+
private IOCMDiscoveryService $ocmDiscoveryService,
20+
private ICloudIdManager $cloudIdManager,
21+
private LoggerInterface $logger,
22+
) {
23+
}
1424
/**
1525
* get a CloudFederationShare Object to prepare a share you want to send
1626
*
@@ -31,7 +41,51 @@ class CloudFederationFactory implements ICloudFederationFactory {
3141
*/
3242
#[\Override]
3343
public function getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $sharedSecret, $shareType, $resourceType) {
34-
return new CloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $shareType, $resourceType, $sharedSecret);
44+
$useExchangeToken = false;
45+
$remoteDomain = null;
46+
47+
try {
48+
$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
49+
$remoteDomain = $cloudId->getRemote();
50+
51+
try {
52+
$remoteProvider = $this->ocmDiscoveryService->discover($remoteDomain);
53+
$capabilities = $remoteProvider->getCapabilities();
54+
$useExchangeToken = $capabilities->hasExchangeToken();
55+
56+
$this->logger->debug('OCM provider capabilities discovered', [
57+
'remote' => $remoteDomain,
58+
'capabilities' => $capabilities->toArray(),
59+
'useExchangeToken' => $useExchangeToken,
60+
]);
61+
} catch (OCMProviderException $e) {
62+
$this->logger->warning('Failed to discover OCM provider, using legacy share method', [
63+
'remote' => $remoteDomain,
64+
'exception' => $e->getMessage(),
65+
]);
66+
}
67+
} catch (\InvalidArgumentException $e) {
68+
$this->logger->warning('Invalid cloud ID format, using legacy share method', [
69+
'shareWith' => $shareWith,
70+
'exception' => $e->getMessage(),
71+
]);
72+
}
73+
74+
return new CloudFederationShare(
75+
$shareWith,
76+
$name,
77+
$description,
78+
$providerId,
79+
$owner,
80+
$ownerDisplayName,
81+
$sharedBy,
82+
$sharedByDisplayName,
83+
$shareType,
84+
$resourceType,
85+
$sharedSecret,
86+
$useExchangeToken,
87+
$remoteDomain
88+
);
3589
}
3690

3791
/**

lib/private/Federation/CloudFederationShare.php

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class CloudFederationShare implements ICloudFederationShare {
4040
* @param string $shareType ('group' or 'user' share)
4141
* @param string $resourceType ('file', 'calendar',...)
4242
* @param string $sharedSecret
43+
* @param bool $useExchangeToken whether to use exchange-token protocol (new way) or sharedSecret (old way)
44+
* @param string|null $remoteDomain remote domain for constructing webdav URI
4345
*/
4446
public function __construct($shareWith = '',
4547
$name = '',
@@ -52,6 +54,8 @@ public function __construct($shareWith = '',
5254
$shareType = '',
5355
$resourceType = '',
5456
$sharedSecret = '',
57+
$useExchangeToken = false,
58+
$remoteDomain = null,
5559
) {
5660
$this->setShareWith($shareWith);
5761
$this->setResourceName($name);
@@ -61,13 +65,27 @@ public function __construct($shareWith = '',
6165
$this->setOwnerDisplayName($ownerDisplayName);
6266
$this->setSharedBy($sharedBy);
6367
$this->setSharedByDisplayName($sharedByDisplayName);
64-
$this->setProtocol([
65-
'name' => 'webdav',
66-
'options' => [
67-
'sharedSecret' => $sharedSecret,
68-
'permissions' => '{http://open-cloud-mesh.org/ns}share-permissions'
69-
]
70-
]);
68+
69+
if ($useExchangeToken) {
70+
$webdavUri = $remoteDomain ? 'https://' . $remoteDomain . '/public.php/webdav/' : '';
71+
$this->setProtocol([
72+
'name' => 'webdav',
73+
'webdav' => [
74+
'uri' => $webdavUri,
75+
'sharedSecret' => $sharedSecret,
76+
'permissions' => ['{http://open-cloud-mesh.org/ns}share-permissions']
77+
]
78+
]);
79+
} else {
80+
$this->setProtocol([
81+
'name' => 'webdav',
82+
'options' => [
83+
'sharedSecret' => $sharedSecret,
84+
'permissions' => '{http://open-cloud-mesh.org/ns}share-permissions'
85+
]
86+
]);
87+
}
88+
7189
$this->setShareType($shareType);
7290
$this->setResourceType($resourceType);
7391
}
@@ -351,7 +369,19 @@ public function getShareType() {
351369
*/
352370
#[\Override]
353371
public function getShareSecret() {
354-
return $this->share['protocol']['options']['sharedSecret'];
372+
$protocol = $this->share['protocol'];
373+
if (isset($protocol['options']['sharedSecret'])) {
374+
return $protocol['options']['sharedSecret'];
375+
}
376+
377+
if (isset($protocol['name'])) {
378+
$protocolName = $protocol['name'];
379+
if (isset($protocol[$protocolName]['sharedSecret'])) {
380+
return $protocol[$protocolName]['sharedSecret'];
381+
}
382+
}
383+
384+
return '';
355385
}
356386

357387
/**

lib/private/OCM/Model/OCMProvider.php

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCP\OCM\Exceptions\OCMProviderException;
1414
use OCP\OCM\IOCMProvider;
1515
use OCP\OCM\IOCMResource;
16+
use OCP\OCM\OCMCapabilities;
1617
use OCP\Security\Signature\Model\Signatory;
1718

1819
/**
@@ -24,6 +25,7 @@ class OCMProvider implements IOCMProvider {
2425
private string $inviteAcceptDialog = '';
2526
private array $capabilities = [];
2627
private string $endPoint = '';
28+
private string $tokenEndPoint = '';
2729
/** @var IOCMResource[] */
2830
private array $resourceTypes = [];
2931
private ?Signatory $signatory = null;
@@ -119,6 +121,29 @@ public function getEndPoint(): string {
119121
return $this->endPoint;
120122
}
121123

124+
/**
125+
* @param string $tokenEndPoint
126+
*
127+
* @return $this
128+
*/
129+
#[\Override]
130+
public function setTokenEndPoint(string $endPoint): static {
131+
$this->tokenEndPoint = $endPoint;
132+
133+
return $this;
134+
}
135+
136+
/**
137+
* @return string
138+
*/
139+
#[\Override]
140+
public function getTokenEndPoint(): string {
141+
if (in_array('exchange-token', $this->capabilities)) {
142+
return $this->tokenEndPoint;
143+
}
144+
return '';
145+
}
146+
122147
/**
123148
* @return string
124149
*/
@@ -141,12 +166,9 @@ public function setCapabilities(array $capabilities): static {
141166
return $this;
142167
}
143168

144-
/**
145-
* @return array
146-
*/
147169
#[\Override]
148-
public function getCapabilities(): array {
149-
return $this->capabilities;
170+
public function getCapabilities(): OCMCapabilities {
171+
return new OCMCapabilities($this->capabilities);
150172
}
151173

152174
/**
@@ -268,6 +290,12 @@ public function import(array $data): static {
268290
$this->setSignatory($signatory);
269291
}
270292
}
293+
if (isset($data['capabilities'])) {
294+
$this->setCapabilities($data['capabilities']);
295+
}
296+
if (isset($data['tokenEndPoint'])) {
297+
$this->setTokenEndPoint($data['tokenEndPoint']);
298+
}
271299

272300
if (!$this->looksValid()) {
273301
throw new OCMProviderException('remote provider does not look valid');
@@ -304,9 +332,12 @@ public function jsonSerialize(): array {
304332
'resourceTypes' => $resourceTypes
305333
];
306334

307-
$capabilities = $this->getCapabilities();
308-
if ($capabilities) {
309-
$response['capabilities'] = $capabilities;
335+
if ($this->capabilities !== []) {
336+
$response['capabilities'] = $this->capabilities;
337+
}
338+
$tokenEndpoint = $this->getTokenEndPoint();
339+
if ($tokenEndpoint) {
340+
$response['tokenEndPoint'] = $tokenEndpoint;
310341
}
311342
$inviteAcceptDialog = $this->getInviteAcceptDialog();
312343
if ($inviteAcceptDialog !== '') {

lib/private/OCM/OCMDiscoveryService.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
#[Consumable(since: '28.0.0')]
5151
final class OCMDiscoveryService implements IOCMDiscoveryService {
5252
private ICache $cache;
53-
public const API_VERSION = '1.1.0';
53+
public const API_VERSION = '1.1.2';
5454
private ?IOCMProvider $localProvider = null;
5555
/** @var array<string, IOCMProvider> */
5656
private array $remoteProviders = [];
@@ -94,6 +94,7 @@ public function discover(string $remote, bool $skipCache = false): IOCMProvider
9494
}
9595

9696
if (array_key_exists($remote, $this->remoteProviders)) {
97+
9798
return $this->remoteProviders[$remote];
9899
}
99100

@@ -130,7 +131,6 @@ public function discover(string $remote, bool $skipCache = false): IOCMProvider
130131
$remote . '/ocm-provider',
131132
];
132133

133-
134134
foreach ($urls as $url) {
135135
$exception = null;
136136
$body = null;
@@ -195,6 +195,7 @@ public function getLocalOCMProvider(bool $fullDetails = true): IOCMProvider {
195195
}
196196

197197
$url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare');
198+
$tokenUrl = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.Token.accessToken');
198199
$pos = strrpos($url, '/');
199200
if ($pos === false) {
200201
$this->logger->debug('generated route should contain a slash character');
@@ -206,7 +207,8 @@ public function getLocalOCMProvider(bool $fullDetails = true): IOCMProvider {
206207
$provider->setEnabled(true);
207208
$provider->setApiVersion(self::API_VERSION);
208209
$provider->setEndPoint(substr($url, 0, $pos));
209-
$provider->setCapabilities(['invite-accepted', 'notifications', 'shares']);
210+
$provider->setCapabilities(['invite-accepted', 'notifications', 'shares', 'exchange-token']);
211+
$provider->setTokenEndPoint($tokenUrl);
210212
if ($signingEnabled) {
211213
$provider->setCapabilities(['http-sig']);
212214
}

lib/private/Server.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,11 @@ public function __construct(
12061206
$this->registerAlias(\OCP\GlobalScale\IConfig::class, \OC\GlobalScale\Config::class);
12071207
$this->registerAlias(ICloudFederationProviderManager::class, CloudFederationProviderManager::class);
12081208
$this->registerService(ICloudFederationFactory::class, function (Server $c) {
1209-
return new CloudFederationFactory();
1209+
return new CloudFederationFactory(
1210+
$c->get(\OCP\OCM\IOCMDiscoveryService::class),
1211+
$c->get(\OCP\Federation\ICloudIdManager::class),
1212+
$c->get(\Psr\Log\LoggerInterface::class)
1213+
);
12101214
});
12111215

12121216
$this->registerAlias(IControllerMethodReflector::class, ControllerMethodReflector::class);

lib/public/OCM/ICapabilityAwareOCMProvider.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,23 @@
1515
* @deprecated 33.0.0 {@see IOCMProvider}
1616
*/
1717
interface ICapabilityAwareOCMProvider extends IOCMProvider {
18+
/**
19+
* get the token endpoint URL
20+
*
21+
* @return string
22+
* @since 32.0.0
23+
*/
24+
#[\Override]
25+
public function getTokenEndPoint(): string;
26+
27+
/**
28+
* set the token endpoint URL
29+
*
30+
* @param string $endPoint
31+
*
32+
* @return $this
33+
* @since 32.0.0
34+
*/
35+
#[\Override]
36+
public function setTokenEndPoint(string $endPoint): static;
1837
}

lib/public/OCM/IOCMProvider.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,11 @@ public function setResourceTypes(array $resourceTypes): static;
110110
public function getResourceTypes(): array;
111111

112112
/**
113-
* get the capabilities
113+
* get the capabilities advertised by this provider
114114
*
115-
* @return array
116115
* @since 33.0.0
117116
*/
118-
public function getCapabilities(): array;
117+
public function getCapabilities(): OCMCapabilities;
119118

120119
/**
121120
* return if provider supports $capability
@@ -158,8 +157,25 @@ public function setCapabilities(array $capabilities): static;
158157
* @return $this
159158
* @since 33.0.0
160159
*/
161-
162160
public function setInviteAcceptDialog(string $inviteAcceptDialog): static;
161+
162+
/**
163+
* get the token endpoint URL
164+
*
165+
* @return string
166+
* @since 33.0.0
167+
*/
168+
public function getTokenEndPoint(): string;
169+
170+
/**
171+
* set the token endpoint URL
172+
*
173+
* @param string $endPoint
174+
*
175+
* @return $this
176+
* @since 33.0.0
177+
*/
178+
public function setTokenEndPoint(string $endPoint): static;
163179
/**
164180
* extract a specific string value from the listing of protocols, based on resource-name and protocol-name
165181
*

0 commit comments

Comments
 (0)