diff --git a/apps/cloud_federation_api/lib/Config.php b/apps/cloud_federation_api/lib/Config.php index f7c14a75c37d4..3432f4ab3be5d 100644 --- a/apps/cloud_federation_api/lib/Config.php +++ b/apps/cloud_federation_api/lib/Config.php @@ -31,8 +31,15 @@ public function __construct( */ public function getSupportedShareTypes($resourceType) { try { - $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType); - return $provider->getSupportedShareTypes(); + $supportedShareTypes = []; + $cloudFederationProviders = $this->cloudFederationProviderManager->getAllCloudFederationProviders(); + foreach ($cloudFederationProviders as $providerWrapper) { + if ($providerWrapper['resourceType'] === $resourceType) { + $providerSupportedShareTypes = $providerWrapper['provider']->getSupportedShareTypes(); + $supportedShareTypes = array_merge($supportedShareTypes, $providerSupportedShareTypes); + } + } + return array_unique($supportedShareTypes); } catch (\Exception $e) { $this->logger->error('Failed to create federation provider', ['exception' => $e]); return []; diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index cbd66f52382d3..b3a0ee4dbebd3 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -168,6 +168,10 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $ } } + if ($shareType === 'federation') { + // Allow creation of pending shares by federation provider + } + // if no explicit display name is given, we use the uid as display name $ownerDisplayName = $ownerDisplayName === null ? $owner : $ownerDisplayName; $sharedByDisplayName = $sharedByDisplayName === null ? $sharedBy : $sharedByDisplayName; @@ -179,7 +183,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $ } try { - $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType); + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType, $shareType); $share = $this->factory->getCloudFederationShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, '', $shareType, $resourceType); $share->setProtocol($protocol); $provider->shareReceived($share); @@ -258,7 +262,7 @@ public function receiveNotification($notificationType, $resourceType, $providerI } try { - $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType); + $provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType, $notification['shareType']); $result = $provider->notificationReceived($notificationType, $providerId, $notification); } catch (ProviderDoesNotExistsException $e) { return new JSONResponse( diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php index 92751ead730b4..9c631e5dfd83a 100644 --- a/apps/files_sharing/composer/composer/autoload_classmap.php +++ b/apps/files_sharing/composer/composer/autoload_classmap.php @@ -80,6 +80,7 @@ 'OCA\\Files_Sharing\\Migration\\Version22000Date20210216084241' => $baseDir . '/../lib/Migration/Version22000Date20210216084241.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220208195521' => $baseDir . '/../lib/Migration/Version24000Date20220208195521.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220404142216' => $baseDir . '/../lib/Migration/Version24000Date20220404142216.php', + 'OCA\\Files_Sharing\\Migration\\Version26000Date20230117143027' => $baseDir . '/../lib/Migration/Version32000Date20230117143027.php', 'OCA\\Files_Sharing\\Migration\\Version31000Date20240821142813' => $baseDir . '/../lib/Migration/Version31000Date20240821142813.php', 'OCA\\Files_Sharing\\MountProvider' => $baseDir . '/../lib/MountProvider.php', 'OCA\\Files_Sharing\\Notification\\Listener' => $baseDir . '/../lib/Notification/Listener.php', diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php index 6ca952007a130..f33fb2692b3bd 100644 --- a/apps/files_sharing/composer/composer/autoload_static.php +++ b/apps/files_sharing/composer/composer/autoload_static.php @@ -95,6 +95,7 @@ class ComposerStaticInitFiles_Sharing 'OCA\\Files_Sharing\\Migration\\Version22000Date20210216084241' => __DIR__ . '/..' . '/../lib/Migration/Version22000Date20210216084241.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220208195521' => __DIR__ . '/..' . '/../lib/Migration/Version24000Date20220208195521.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220404142216' => __DIR__ . '/..' . '/../lib/Migration/Version24000Date20220404142216.php', + 'OCA\\Files_Sharing\\Migration\\Version26000Date20230117143027' => __DIR__ . '/..' . '/../lib/Migration/Version32000Date20230117143027.php', 'OCA\\Files_Sharing\\Migration\\Version31000Date20240821142813' => __DIR__ . '/..' . '/../lib/Migration/Version31000Date20240821142813.php', 'OCA\\Files_Sharing\\MountProvider' => __DIR__ . '/..' . '/../lib/MountProvider.php', 'OCA\\Files_Sharing\\Notification\\Listener' => __DIR__ . '/..' . '/../lib/Notification/Listener.php', diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 23ba9da1568d3..ee792c722123b 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -105,7 +105,7 @@ public function __construct( * * @param IShare $share * @param Node|null $recipientNode - * @return Files_SharingShare + * @return array * @throws NotFoundException In case the node can't be resolved. * * @suppress PhanUndeclaredClassMethod @@ -305,6 +305,14 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra $result = array_merge($result, $scienceMeshShare); } catch (ContainerExceptionInterface $e) { } + } elseif ($share->getShareType() === IShare::TYPE_FEDERATED_GROUP) { + $group = $this->groupManager->get($share->getSharedWith()); + $result['share_with'] = $share->getSharedWith(); + $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); + try { + $result = array_merge($result, $this->getFederatedGroupShareHelper()->formatShare($share)); + } catch (QueryException $e) { + } } @@ -746,6 +754,15 @@ public function createShare( $share->setPermissions($permissions); $share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false)); } elseif ($shareType === IShare::TYPE_REMOTE_GROUP) { + if ($expireDate !== '') { + try { + $expireDate = $this->parseDate($expireDate); + $share->setExpirationDate($expireDate); + } catch (\Exception $e) { + throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD')); + } + } + } elseif ($shareType === IShare::TYPE_FEDERATED_GROUP) { if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) { throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType])); } @@ -1771,6 +1788,16 @@ private function getShareById(string $id): IShare { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { throw new ShareNotFound(); } + + try { + if ($this->shareManager->shareProviderExists(IShare::TYPE_FEDERATED_GROUP)) { + $share = $this->shareManager->getShareById('ocFederatedGroupShare:' . $id, $this->userId); + return $share; + } + } catch (ShareNotFound $e) { + // Do nothing, just try the other share type + } + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->userId); return $share; @@ -1848,6 +1875,25 @@ private function getSciencemeshShareHelper() { return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper'); } + /** + * Returns the helper of ShareAPIHelper for federated group shares. + * + * If the VO federation application is not enabled or the helper is not available + * a QueryException is thrown instead. + * + * @return \OCA\VO_Federation\Sharing\ShareAPIHelper + * @throws ContainerExceptionInterface + * @throws QueryException + * @throws \Psr\Container\NotFoundExceptionInterface + */ + private function getFederatedGroupShareHelper() { + if (!$this->appManager->isEnabledForUser('vo_federation')) { + throw new QueryException(); + } + + return $this->serverContainer->get('\OCA\VO_Federation\Sharing\ShareAPIHelper'); + } + /** * @param string $viewer * @param Node $node @@ -1890,6 +1936,12 @@ private function getSharesFromNode(string $viewer, $node, bool $reShares): array $federatedShares = $this->shareManager->getSharesBy( $this->userId, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0 ); + if ($this->shareManager->shareProviderExists(IShare::TYPE_FEDERATED_GROUP)) { + $federatedGroupShares = $this->shareManager->getSharesBy( + $this->userId, IShare::TYPE_FEDERATED_GROUP, $node, $reShares, -1, 0 + ); + $federatedShares = array_merge($federatedShares, $federatedGroupShares); + } $shares = array_merge($shares, $federatedShares); } @@ -2025,17 +2077,19 @@ private function getAllShares(?Node $path = null, bool $reshares = false) { // FEDERATION if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { - $federatedShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0); + $remoteShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0); } else { - $federatedShares = []; + $remoteShares = []; } if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) { - $federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0); + $remoteGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0); + $federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_FEDERATED_GROUP, $path, $reshares, -1, 0); } else { + $remoteGroupShares = []; $federatedGroupShares = []; } - return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $federatedShares, $federatedGroupShares); + return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $remoteShares, $remoteGroupShares, $federatedGroupShares); } diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php index 0c458ce9662de..59ed76b05c0ce 100644 --- a/apps/files_sharing/lib/Controller/ShareesAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php @@ -52,6 +52,7 @@ class ShareesAPIController extends OCSController { 'groups' => [], 'remotes' => [], 'remote_groups' => [], + 'federated_groups' => [], 'emails' => [], 'circles' => [], 'rooms' => [], @@ -60,6 +61,7 @@ class ShareesAPIController extends OCSController { 'groups' => [], 'remotes' => [], 'remote_groups' => [], + 'federated_groups' => [], 'emails' => [], 'lookup' => [], 'circles' => [], @@ -137,6 +139,10 @@ public function search(string $search = '', ?string $itemType = null, int $page if ($this->isRemoteGroupSharingAllowed($itemType)) { $shareTypes[] = IShare::TYPE_REMOTE_GROUP; + + if ($this->shareManager->shareProviderExists(IShare::TYPE_FEDERATED_GROUP)) { + $shareTypes[] = IShare::TYPE_FEDERATED_GROUP; + } } if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { @@ -239,6 +245,7 @@ private function sortShareesByFrequency(array $sharees): array { IShare::TYPE_GROUP => 'groups', IShare::TYPE_REMOTE => 'remotes', IShare::TYPE_REMOTE_GROUP => 'remote_groups', + IShare::TYPE_FEDERATED_GROUP => 'federated_groups', IShare::TYPE_EMAIL => 'emails', ]; @@ -312,6 +319,10 @@ public function findRecommended(string $itemType, $shareType = null): DataRespon if ($this->isRemoteGroupSharingAllowed($itemType)) { $shareTypes[] = IShare::TYPE_REMOTE_GROUP; + + if ($this->shareManager->shareProviderExists(IShare::TYPE_FEDERATED_GROUP)) { + $shareTypes[] = IShare::TYPE_FEDERATED_GROUP; + } } if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { diff --git a/apps/files_sharing/lib/Migration/Version32000Date20230117143027.php b/apps/files_sharing/lib/Migration/Version32000Date20230117143027.php new file mode 100644 index 0000000000000..169ac09d747f9 --- /dev/null +++ b/apps/files_sharing/lib/Migration/Version32000Date20230117143027.php @@ -0,0 +1,72 @@ + + * + * @author Sandro Mesterheide + * + * @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 . + * + */ + +namespace OCA\Files_Sharing\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version32000Date20230117143027 extends SimpleMigrationStep { + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('share_external'); + $changed = false; + + $column = $table->getColumn('user'); + if ($column->getLength() < 255) { + $column->setLength(255); + $changed = true; + } + + if (!$table->hasColumn('remote_share_type')) { + $table->addColumn('remote_share_type', Types::INTEGER, [ + 'notnull' => false, + 'length' => 4, + ]); + $changed = true; + } + + if ($changed) { + return $schema; + } + + return null; + } +} diff --git a/apps/files_sharing/src/components/SharingEntry.vue b/apps/files_sharing/src/components/SharingEntry.vue index 4ff5fae364bba..af4404867f2ff 100644 --- a/apps/files_sharing/src/components/SharingEntry.vue +++ b/apps/files_sharing/src/components/SharingEntry.vue @@ -83,6 +83,8 @@ export default { title += ` (${t('files_sharing', 'remote group')})` } else if (this.share.type === ShareType.Guest) { title += ` (${t('files_sharing', 'guest')})` + } else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_FEDERATED_GROUP) { + title += ` (${t('files_sharing', 'federated group')})` } if (!this.isShareOwner && this.share.ownerDisplayName) { title += ' ' + t('files_sharing', 'by {initiator}', { diff --git a/apps/files_sharing/src/components/SharingInput.vue b/apps/files_sharing/src/components/SharingInput.vue index b886ba95a177e..98394b1af350d 100644 --- a/apps/files_sharing/src/components/SharingInput.vue +++ b/apps/files_sharing/src/components/SharingInput.vue @@ -194,7 +194,7 @@ export default { let shareType = [] - const remoteTypes = [ShareType.Remote, ShareType.RemoteGroup] + const remoteTypes = [ShareType.Remote, ShareType.RemoteGroup, ShareType.FederatedGroup] if (this.isExternal && !this.config.showFederatedSharesAsInternal) { shareType.push(...remoteTypes) @@ -443,6 +443,11 @@ export default { icon: 'icon-sciencemesh', iconTitle: t('files_sharing', 'ScienceMesh'), } + case this.SHARE_TYPES.SHARE_TYPE_FEDERATED_GROUP: + return { + icon: 'icon-organization', + iconTitle: t('files_sharing', 'Virtual organization'), + } default: return {} } diff --git a/core/img/actions/organization.svg b/core/img/actions/organization.svg new file mode 100644 index 0000000000000..c936c713cdbf5 --- /dev/null +++ b/core/img/actions/organization.svg @@ -0,0 +1,16 @@ + + + + + diff --git a/core/src/icons.js b/core/src/icons.js index 5845b01fea14c..648c443fda351 100644 --- a/core/src/icons.js +++ b/core/src/icons.js @@ -76,6 +76,7 @@ const icons = { 'menu': path.join(__dirname, '../img', 'actions', 'menu.svg'), 'more': path.join(__dirname, '../img', 'actions', 'more.svg'), 'music': path.join(__dirname, '../img', 'places', 'music.svg'), + 'organization': path.join(__dirname, '../img', 'actions', 'organization.svg'), 'password': path.join(__dirname, '../img', 'actions', 'password.svg'), 'pause': path.join(__dirname, '../img', 'actions', 'pause.svg'), 'phone': path.join(__dirname, '../img', 'clients', 'phone.svg'), diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index e93542943510a..4ffb99777ec65 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -60,26 +60,27 @@ public function __construct( * @param callable $callback */ public function addCloudFederationProvider($resourceType, $displayName, callable $callback) { - $this->cloudFederationProvider[$resourceType] = [ + $provider = call_user_func($callback); + $this->cloudFederationProvider[$provider::class] = [ 'resourceType' => $resourceType, 'displayName' => $displayName, - 'callback' => $callback, + 'provider' => $provider, ]; } /** * remove cloud federation provider * - * @param string $providerId + * @param string $providerClass */ - public function removeCloudFederationProvider($providerId) { - unset($this->cloudFederationProvider[$providerId]); + public function removeCloudFederationProvider($providerClass) { + unset($this->cloudFederationProvider[$providerClass]); } /** * get a list of all cloudFederationProviders * - * @return array [resourceType => ['resourceType' => $resourceType, 'displayName' => $displayName, 'callback' => callback]] + * @return array [providerClass => ['resourceType' => $resourceType, 'displayName' => $displayName, 'provider' => ICloudFederationProvider]] */ public function getAllCloudFederationProviders() { return $this->cloudFederationProvider; @@ -89,15 +90,20 @@ public function getAllCloudFederationProviders() { * get a specific cloud federation provider * * @param string $resourceType + * @param string|null $shareType * @return ICloudFederationProvider * @throws ProviderDoesNotExistsException */ - public function getCloudFederationProvider($resourceType) { - if (isset($this->cloudFederationProvider[$resourceType])) { - return call_user_func($this->cloudFederationProvider[$resourceType]['callback']); - } else { - throw new ProviderDoesNotExistsException($resourceType); + public function getCloudFederationProvider($resourceType, $shareType = null) { + foreach ($this->cloudFederationProvider as $providerWrapper) { + $provider = $providerWrapper['provider']; + if ($providerWrapper['resourceType'] === $resourceType) { + if (is_null($shareType) || in_array($shareType, $provider->getSupportedShareTypes())) { + return $provider; + } + } } + throw new ProviderDoesNotExistsException($resourceType, $shareType); } /** diff --git a/lib/private/Federation/CloudFederationShare.php b/lib/private/Federation/CloudFederationShare.php index 3ec53d89ed33b..f83c083b112c5 100644 --- a/lib/private/Federation/CloudFederationShare.php +++ b/lib/private/Federation/CloudFederationShare.php @@ -184,7 +184,7 @@ public function setProtocol(array $protocol) { } /** - * share type (group or user) + * share type (group, user or federation) * * @param string $shareType * @@ -193,6 +193,9 @@ public function setProtocol(array $protocol) { public function setShareType($shareType) { if ($shareType === 'group' || $shareType === IShare::TYPE_REMOTE_GROUP) { $this->share['shareType'] = 'group'; + } elseif ($shareType === 'federation' || $shareType === IShare::TYPE_FEDERATED_GROUP) { + // OCM proposel, currently only supported by Nextcloud + $this->share['shareType'] = 'federation'; } else { $this->share['shareType'] = 'user'; } diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index baff04fbc4a87..b1a312b892bcf 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -56,6 +56,8 @@ class Constants { // const SHARE_TYPE_DECK_USER = 13; // Internal type used by DeckShareProvider // Note to developers: Do not add new share types here + // Relied on by appinfo XML schema collaboration plugin share-type attribute + public const SHARE_TYPE_FEDERATED_GROUP = 14; public const FORMAT_NONE = -1; public const FORMAT_STATUSES = -2; diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 2104c07593aec..94df59278afbd 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -163,6 +163,10 @@ protected function generalCreateChecks(IShare $share, bool $isUpdate = false) { if ($share->getSharedWith() === null) { throw new \InvalidArgumentException($this->l->t('Share recipient should not be empty')); } + } elseif ($share->getShareType() === IShare::TYPE_FEDERATED_GROUP) { + if ($share->getSharedWith() === null) { + throw new \InvalidArgumentException('SharedWith should not be empty'); + } } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) { $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith()); if ($circle === null) { @@ -276,7 +280,9 @@ protected function generalCreateChecks(IShare $share, bool $isUpdate = false) { * @throws \Exception */ protected function validateExpirationDateInternal(IShare $share) { - $isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP; + $isRemote = $share->getShareType() === IShare::TYPE_REMOTE + || $share->getShareType() === IShare::TYPE_REMOTE_GROUP + || $share->getShareType() === IShare::TYPE_FEDERATED_GROUP; $expirationDate = $share->getExpirationDate(); @@ -675,6 +681,9 @@ public function createShare(IShare $share) { } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) { // Verify the expiration date $share = $this->validateExpirationDateInternal($share); + } elseif ($share->getShareType() === IShare::TYPE_FEDERATED_GROUP) { + //Verify the expiration date + $share = $this->validateExpirationDateInternal($share); } elseif ($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL) { $this->linkCreateChecks($share); @@ -856,7 +865,9 @@ public function updateShare(IShare $share, bool $onlyValid = true) { $this->validateExpirationDateLink($share); $expirationDateUpdated = true; } - } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) { + } elseif ($share->getShareType() === IShare::TYPE_REMOTE + || $share->getShareType() === IShare::TYPE_REMOTE_GROUP + || $share->getShareType() === IShare::TYPE_FEDERATED_GROUP) { if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { // Verify the expiration date $this->validateExpirationDateInternal($share); @@ -1432,6 +1443,16 @@ public function getShareByToken($token) { } } + // If it is not a link share try to fetch a federated group share by token + if ($share === null && $this->shareProviderExists(IShare::TYPE_FEDERATED_GROUP)) { + try { + $provider = $this->factory->getProviderForType(IShare::TYPE_FEDERATED_GROUP); + $share = $provider->getShareByToken($token); + } catch (ProviderException $e) { + } catch (ShareNotFound $e) { + } + } + // If it is not a link share try to fetch a mail share by token if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) { try { diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index eba3f4f26f146..2ff2e2f67751a 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -35,6 +35,8 @@ class ProviderFactory implements IProviderFactory { * @var ?RoomShareProvider */ private $roomShareProvider = null; + /** @var \OCA\VO_Federation\FederatedGroupShareProvider */ + private $federatedGroupShareProvider = null; private array $registeredShareProviders = []; @@ -125,6 +127,31 @@ protected function getRoomShareProvider() { return $this->roomShareProvider; } + /** + * Create the federated group share provider + * + * @return \OCA\VO_Federation\FederatedGroupShareProvider|null + */ + protected function getFederatedGroupShareProvider() { + if ($this->federatedGroupShareProvider === null) { + /* + * Check if the app is enabled + */ + $appManager = $this->serverContainer->getAppManager(); + if (!$appManager->isEnabledForUser('vo_federation')) { + return null; + } + + try { + $this->federatedGroupShareProvider = $this->serverContainer->query('\OCA\VO_Federation\FederatedGroupShareProvider'); + } catch (\OCP\AppFramework\QueryException $e) { + return null; + } + } + + return $this->federatedGroupShareProvider; + } + /** * @inheritdoc */ @@ -142,6 +169,8 @@ public function getProvider($id) { $provider = $this->getShareByMailProvider(); } elseif ($id === 'ocRoomShare') { $provider = $this->getRoomShareProvider(); + } elseif ($id === 'ocFederatedGroupShare') { + $provider = $this->getFederatedGroupShareProvider(); } foreach ($this->registeredShareProviders as $shareProvider) { @@ -191,6 +220,8 @@ public function getProviderForType($shareType) { $provider = $this->getProvider('deck'); } elseif ($shareType === IShare::TYPE_SCIENCEMESH) { $provider = $this->getProvider('sciencemesh'); + } elseif ($shareType === IShare::TYPE_FEDERATED_GROUP) { + $provider = $this->getFederatedGroupShareProvider(); } @@ -211,6 +242,10 @@ public function getAllProviders() { if ($roomShare !== null) { $shares[] = $roomShare; } + $federatedGroupShare = $this->getFederatedGroupShareProvider(); + if ($federatedGroupShare !== null) { + $shares[] = $federatedGroupShare; + } foreach ($this->registeredShareProviders as $shareProvider) { try { diff --git a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php index 4b3d0341fe964..dfe8e6035d81f 100644 --- a/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php +++ b/lib/public/Federation/Exceptions/ProviderDoesNotExistsException.php @@ -19,12 +19,13 @@ class ProviderDoesNotExistsException extends HintException { * * @since 14.0.0 * - * @param string $providerId cloud federation provider ID + * @param string $resourceId cloud federation resourceId + * @param string|null $shareId cloud federation shareId */ - public function __construct($providerId) { + public function __construct($resourceId, $shareId) { $l = \OCP\Util::getL10N('federation'); - $message = 'Cloud Federation Provider with ID: "' . $providerId . '" does not exist.'; - $hint = $l->t('Cloud Federation Provider with ID: "%s" does not exist.', [$providerId]); + $message = 'Cloud Federation Provider with resourceId: "' . $resourceId . '" and shareId: "' . $shareId . '" does not exist.'; + $hint = $l->t('Cloud Federation Provider with resourceId: "%s" and shareId: "%s" does not exist.', [$resourceId, $shareId]); parent::__construct($message, $hint); } } diff --git a/lib/public/Federation/ICloudFederationProviderManager.php b/lib/public/Federation/ICloudFederationProviderManager.php index 9f5258721ab21..bb856919ad3a7 100644 --- a/lib/public/Federation/ICloudFederationProviderManager.php +++ b/lib/public/Federation/ICloudFederationProviderManager.php @@ -32,18 +32,18 @@ public function addCloudFederationProvider($resourceType, $displayName, callable /** * remove cloud federation provider * - * @param string $resourceType + * @param string $providerClass * - * @since 14.0.0 + * @since 26.0.0 */ - public function removeCloudFederationProvider($resourceType); + public function removeCloudFederationProvider($providerClass); /** * get a list of all cloudFederationProviders * - * @return array [resourceType => ['resourceType' => $resourceType, 'displayName' => $displayName, 'callback' => callback]] + * @return array [providerClass => ['resourceType' => $resourceType, 'displayName' => $displayName, 'provider' => ICloudFederationProvider]] * - * @since 14.0.0 + * @since 26.0.0 */ public function getAllCloudFederationProviders(); @@ -51,12 +51,13 @@ public function getAllCloudFederationProviders(); * get a specific cloud federation provider * * @param string $resourceType + * @param string|null $shareType * @return ICloudFederationProvider * @throws Exceptions\ProviderDoesNotExistsException * * @since 14.0.0 */ - public function getCloudFederationProvider($resourceType); + public function getCloudFederationProvider($resourceType, $shareType = null); /** * send federated share diff --git a/lib/public/Federation/ICloudFederationShare.php b/lib/public/Federation/ICloudFederationShare.php index 197ebf1af9a72..32e41d15cf8d6 100644 --- a/lib/public/Federation/ICloudFederationShare.php +++ b/lib/public/Federation/ICloudFederationShare.php @@ -202,7 +202,7 @@ public function getSharedBy(); public function getSharedByDisplayName(); /** - * get share type (group or user) + * get share type (group, user or federation) * * @return string * diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php index 337210e3b910f..9d5a774504d88 100644 --- a/lib/public/Share/IShare.php +++ b/lib/public/Share/IShare.php @@ -101,6 +101,12 @@ interface IShare { */ public const TYPE_SCIENCEMESH = 15; + /** + * Extension to remote group share + * @since 32.0.0 + */ + public const TYPE_FEDERATED_GROUP = 14; + /** * @since 18.0.0 */