diff --git a/apps/cloud_federation_api/appinfo/info.xml b/apps/cloud_federation_api/appinfo/info.xml
index 57a8d9b50a0..81343cb49bf 100644
--- a/apps/cloud_federation_api/appinfo/info.xml
+++ b/apps/cloud_federation_api/appinfo/info.xml
@@ -9,7 +9,7 @@
Cloud Federation API
Enable clouds to communicate with each other and exchange data
The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data.
- 1.15.0
+ 1.16.0
agpl
Bjoern Schiessle
CloudFederationAPI
diff --git a/apps/cloud_federation_api/composer/composer/InstalledVersions.php b/apps/cloud_federation_api/composer/composer/InstalledVersions.php
index 6d29bff66aa..51e734a774b 100644
--- a/apps/cloud_federation_api/composer/composer/InstalledVersions.php
+++ b/apps/cloud_federation_api/composer/composer/InstalledVersions.php
@@ -32,11 +32,6 @@ class InstalledVersions
*/
private static $installed;
- /**
- * @var bool
- */
- private static $installedIsLocalDir;
-
/**
* @var bool|null
*/
@@ -314,12 +309,6 @@ class InstalledVersions
{
self::$installed = $data;
self::$installedByVendor = array();
-
- // when using reload, we disable the duplicate protection to ensure that self::$installed data is
- // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
- // so we have to assume it does not, and that may result in duplicate data being returned when listing
- // all installed packages for example
- self::$installedIsLocalDir = false;
}
/**
@@ -333,27 +322,19 @@ class InstalledVersions
}
$installed = array();
- $copiedLocalDir = false;
if (self::$canGetVendors) {
- $selfDir = strtr(__DIR__, '\\', '/');
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
- $vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
$required = require $vendorDir.'/composer/installed.php';
- self::$installedByVendor[$vendorDir] = $required;
- $installed[] = $required;
- if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
- self::$installed = $required;
- self::$installedIsLocalDir = true;
+ $installed[] = self::$installedByVendor[$vendorDir] = $required;
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
}
}
- if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
- $copiedLocalDir = true;
- }
}
}
@@ -369,7 +350,7 @@ class InstalledVersions
}
}
- if (self::$installed !== array() && !$copiedLocalDir) {
+ if (self::$installed !== array()) {
$installed[] = self::$installed;
}
diff --git a/apps/cloud_federation_api/composer/composer/autoload_classmap.php b/apps/cloud_federation_api/composer/composer/autoload_classmap.php
index dd096ebf563..aa0c7c5ad79 100644
--- a/apps/cloud_federation_api/composer/composer/autoload_classmap.php
+++ b/apps/cloud_federation_api/composer/composer/autoload_classmap.php
@@ -11,5 +11,8 @@ return array(
'OCA\\CloudFederationAPI\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
'OCA\\CloudFederationAPI\\Config' => $baseDir . '/../lib/Config.php',
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => $baseDir . '/../lib/Controller/RequestHandlerController.php',
+ 'OCA\\CloudFederationAPI\\Migration\\Version1016Date202502262004' => $baseDir . '/../lib/Migration/Version1016Date202502262004.php',
'OCA\\CloudFederationAPI\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
+ 'OCA\\CloudFederationApi\\Events\\OCMInvitationAcceptedEvent' => $baseDir . '/../lib/Events/OCMInvitationAcceptedEvent.php',
+ 'OCA\\CloudFederationApi\\OCMInvitation' => $baseDir . '/../lib/OCMInvitation.php',
);
diff --git a/apps/cloud_federation_api/composer/composer/autoload_static.php b/apps/cloud_federation_api/composer/composer/autoload_static.php
index 75557a20126..133580e463e 100644
--- a/apps/cloud_federation_api/composer/composer/autoload_static.php
+++ b/apps/cloud_federation_api/composer/composer/autoload_static.php
@@ -26,7 +26,10 @@ class ComposerStaticInitCloudFederationAPI
'OCA\\CloudFederationAPI\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
'OCA\\CloudFederationAPI\\Config' => __DIR__ . '/..' . '/../lib/Config.php',
'OCA\\CloudFederationAPI\\Controller\\RequestHandlerController' => __DIR__ . '/..' . '/../lib/Controller/RequestHandlerController.php',
+ 'OCA\\CloudFederationAPI\\Migration\\Version1016Date202502262004' => __DIR__ . '/..' . '/../lib/Migration/Version1016Date202502262004.php',
'OCA\\CloudFederationAPI\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
+ 'OCA\\CloudFederationApi\\Events\\OCMInvitationAcceptedEvent' => __DIR__ . '/..' . '/../lib/Events/OCMInvitationAcceptedEvent.php',
+ 'OCA\\CloudFederationApi\\OCMInvitation' => __DIR__ . '/..' . '/../lib/OCMInvitation.php',
);
public static function getInitializer(ClassLoader $loader)
diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
index 6a1c66661e9..636f78d0a9d 100644
--- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
+++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
@@ -1,8 +1,10 @@
factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType);
$share->setProtocol($protocol);
$provider->shareReceived($share);
- } catch (ProviderDoesNotExistsException|ProviderCouldNotAddShareException $e) {
+ } catch (ProviderDoesNotExistsException | ProviderCouldNotAddShareException $e) {
return new JSONResponse(
['message' => $e->getMessage()],
Http::STATUS_NOT_IMPLEMENTED
@@ -243,7 +252,8 @@ class RequestHandlerController extends Controller {
#[PublicPage]
#[NoCSRFRequired]
#[BruteForceProtection(action: 'inviteAccepted')]
- public function inviteAccepted(string $recipientProvider, string $token, string $userId, string $email, string $name): JSONResponse {
+ public function inviteAccepted(string $recipientProvider, string $token, string $userId, string $email, string $name): JSONResponse
+ {
$this->logger->debug('Invite accepted for ' . $userId . ' with token ' . $token . ' and email ' . $email . ' and name ' . $name);
/** @var IQueryBuilder $qb */
@@ -255,6 +265,7 @@ class RequestHandlerController extends Controller {
$data = $result->fetch();
$result->closeCursor();
$found_for_this_user = false;
+ $updated = new DateTime("now");
if ($data) {
$found_for_this_user = $data['recipient_user_id'] === $userId && isset($data['user_id']);
}
@@ -265,17 +276,22 @@ class RequestHandlerController extends Controller {
$response->throttle();
return $response;
}
- if(!$this->trustedServers->isTrustedServer($recipientProvider)) {
+ if (!$this->trustedServers->isTrustedServer($recipientProvider)) {
$response = ['message' => 'Remote server not trusted', 'error' => true];
$status = Http::STATUS_FORBIDDEN;
- return new JSONResponse($response,$status);
+ return new JSONResponse($response, $status);
}
// Note: Not implementing 404 Invitation token does not exist, instead using 400
- if ($data['accepted'] === true ) {
+ if ($data['accepted'] === true) {
$response = ['message' => 'Invite already accepted', 'error' => true];
$status = Http::STATUS_CONFLICT;
- return new JSONResponse($response,$status);
+ return new JSONResponse($response, $status);
+ }
+ if ($data['expiresAt'] < $updated) {
+ $response = ['message' => 'Invitation expired', 'error' => true];
+ $status = Http::STATUS_BAD_REQUEST;
+ return new JSONResponse($response, $status);
}
$localUser = $this->userManager->get($data['user_id']);
@@ -284,7 +300,6 @@ class RequestHandlerController extends Controller {
$response = ['userID' => $data['user_id'], 'email' => $sharedFromEmail, 'name' => $sharedFromDisplayName];
$status = Http::STATUS_OK;
- $updated = new DateTime("now");
$qb->update('federated_invites')
->set('accepted', $qb->createNamedParameter(true))
->set('acceptedAt', $qb->createNamedParameter($updated))
@@ -295,7 +310,21 @@ class RequestHandlerController extends Controller {
->where($qb->expr()->eq('token', $qb->createNamedParameter($token)));
$qb->executeStatement();
- return new JSONResponse($response,$status);
+ $invitation = new OCMInvitation(
+ accepted: true,
+ recipient_email: $email,
+ recipient_name: $name,
+ recipient_user_id: $userId,
+ recipient_provider: $recipientProvider,
+ token: $token,
+ user_id: $data['user_id'],
+ acceptedAt: $updated,
+ createdAt: $data['createdAt'],
+ expiresAt: $data['expiresAt']
+ );
+ $this->dispatcher->dispatchTyped(new OCMInvitationAcceptedEvent($invitation));
+
+ return new JSONResponse($response, $status);
}
/**
@@ -316,9 +345,11 @@ class RequestHandlerController extends Controller {
#[NoCSRFRequired]
#[PublicPage]
#[BruteForceProtection(action: 'receiveFederatedShareNotification')]
- public function receiveNotification($notificationType, $resourceType, $providerId, ?array $notification) {
+ public function receiveNotification($notificationType, $resourceType, $providerId, ?array $notification)
+ {
// check if all required parameters are set
- if ($notificationType === null ||
+ if (
+ $notificationType === null ||
$resourceType === null ||
$providerId === null ||
!is_array($notification)
@@ -394,7 +425,8 @@ class RequestHandlerController extends Controller {
* @param string $uid
* @return string mixed
*/
- private function mapUid($uid) {
+ private function mapUid($uid)
+ {
// FIXME this should be a method in the user management instead
$this->logger->debug('shareWith before, ' . $uid, ['app' => $this->appName]);
Util::emitHook(
@@ -417,12 +449,13 @@ class RequestHandlerController extends Controller {
* @return IIncomingSignedRequest|null null if remote does not (and never did) support signed request
* @throws IncomingRequestException
*/
- private function getSignedRequest(): ?IIncomingSignedRequest {
+ private function getSignedRequest(): ?IIncomingSignedRequest
+ {
try {
$signedRequest = $this->signatureManager->getIncomingSignedRequest($this->signatoryManager);
$this->logger->debug('signed request available', ['signedRequest' => $signedRequest]);
return $signedRequest;
- } catch (SignatureNotFoundException|SignatoryNotFoundException $e) {
+ } catch (SignatureNotFoundException | SignatoryNotFoundException $e) {
$this->logger->debug('remote does not support signed request', ['exception' => $e]);
// remote does not support signed request.
// currently we still accept unsigned request until lazy appconfig
@@ -452,7 +485,8 @@ class RequestHandlerController extends Controller {
*
* @throws IncomingRequestException
*/
- private function confirmSignedOrigin(?IIncomingSignedRequest $signedRequest, string $key, string $value): void {
+ private function confirmSignedOrigin(?IIncomingSignedRequest $signedRequest, string $key, string $value): void
+ {
if ($signedRequest === null) {
$instance = $this->getHostFromFederationId($value);
try {
@@ -516,7 +550,8 @@ class RequestHandlerController extends Controller {
* @return void
* @throws IncomingRequestException
*/
- private function confirmNotificationEntry(?IIncomingSignedRequest $signedRequest, string $entry): void {
+ private function confirmNotificationEntry(?IIncomingSignedRequest $signedRequest, string $entry): void
+ {
$instance = $this->getHostFromFederationId($entry);
if ($signedRequest === null) {
try {
@@ -535,7 +570,8 @@ class RequestHandlerController extends Controller {
* @return string
* @throws IncomingRequestException
*/
- private function getHostFromFederationId(string $entry): string {
+ private function getHostFromFederationId(string $entry): string
+ {
if (!str_contains($entry, '@')) {
throw new IncomingRequestException('entry ' . $entry . ' does not contains @');
}
diff --git a/apps/cloud_federation_api/lib/Events/OCMInvitationAcceptedEvent.php b/apps/cloud_federation_api/lib/Events/OCMInvitationAcceptedEvent.php
new file mode 100644
index 00000000000..d1ebcdaf7d8
--- /dev/null
+++ b/apps/cloud_federation_api/lib/Events/OCMInvitationAcceptedEvent.php
@@ -0,0 +1,26 @@
+
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+namespace OCA\CloudFederationApi\Events;
+
+use OCP\EventDispatcher\Event;
+use OCA\CloudFederationApi\OCMInvitation;
+
+class OCMInvitationAcceptedEvent extends Event
+{
+ public function __construct(
+ private OCMInvitation $invitation
+ ) {
+ parent::__construct();
+ }
+
+ public function getInvitation(): OCMInvitation
+ {
+ return $this->invitation;
+ }
+}
diff --git a/apps/cloud_federation_api/lib/Migration/Version1015Date202502262004.php b/apps/cloud_federation_api/lib/Migration/Version1016Date202502262004.php
similarity index 97%
rename from apps/cloud_federation_api/lib/Migration/Version1015Date202502262004.php
rename to apps/cloud_federation_api/lib/Migration/Version1016Date202502262004.php
index 436bdcb7f5d..ab4bf627c14 100644
--- a/apps/cloud_federation_api/lib/Migration/Version1015Date202502262004.php
+++ b/apps/cloud_federation_api/lib/Migration/Version1016Date202502262004.php
@@ -15,7 +15,7 @@ use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
-class Version1015Date202502262004 extends SimpleMigrationStep
+class Version1016Date202502262004 extends SimpleMigrationStep
{
/**
diff --git a/apps/cloud_federation_api/lib/OCMInvitation.php b/apps/cloud_federation_api/lib/OCMInvitation.php
new file mode 100644
index 00000000000..ac3dfe3ca53
--- /dev/null
+++ b/apps/cloud_federation_api/lib/OCMInvitation.php
@@ -0,0 +1,18 @@
+ array($baseDir . '/lib/private'),
'OCP\\' => array($baseDir . '/lib/public'),
'NCU\\' => array($baseDir . '/lib/unstable'),
- 'Bamarni\\Composer\\Bin\\' => array($vendorDir . '/bamarni/composer-bin-plugin/src'),
'' => array($baseDir . '/lib/private/legacy'),
);
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 956fa820ac5..7a38da17c82 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -21,10 +21,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
array (
'NCU\\' => 4,
),
- 'B' =>
- array (
- 'Bamarni\\Composer\\Bin\\' => 21,
- ),
);
public static $prefixDirsPsr4 = array (
@@ -44,10 +40,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
array (
0 => __DIR__ . '/../../..' . '/lib/unstable',
),
- 'Bamarni\\Composer\\Bin\\' =>
- array (
- 0 => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src',
- ),
);
public static $fallbackDirsPsr4 = array (