Skip to content
Open
4 changes: 4 additions & 0 deletions apps/files_external/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
'OCA\\Files_External\\Controller\\StoragesController' => $baseDir . '/../lib/Controller/StoragesController.php',
'OCA\\Files_External\\Controller\\UserGlobalStoragesController' => $baseDir . '/../lib/Controller/UserGlobalStoragesController.php',
'OCA\\Files_External\\Controller\\UserStoragesController' => $baseDir . '/../lib/Controller/UserStoragesController.php',
'OCA\\Files_External\\Event\\StorageCreatedEvent' => $baseDir . '/../lib/Event/StorageCreatedEvent.php',
'OCA\\Files_External\\Event\\StorageDeletedEvent' => $baseDir . '/../lib/Event/StorageDeletedEvent.php',
'OCA\\Files_External\\Event\\StorageUpdatedEvent' => $baseDir . '/../lib/Event/StorageUpdatedEvent.php',
'OCA\\Files_External\\Lib\\Auth\\AmazonS3\\AccessKey' => $baseDir . '/../lib/Lib/Auth/AmazonS3/AccessKey.php',
'OCA\\Files_External\\Lib\\Auth\\AuthMechanism' => $baseDir . '/../lib/Lib/Auth/AuthMechanism.php',
'OCA\\Files_External\\Lib\\Auth\\Builtin' => $baseDir . '/../lib/Lib/Auth/Builtin.php',
Expand Down Expand Up @@ -117,6 +120,7 @@
'OCA\\Files_External\\Service\\GlobalStoragesService' => $baseDir . '/../lib/Service/GlobalStoragesService.php',
'OCA\\Files_External\\Service\\ImportLegacyStoragesService' => $baseDir . '/../lib/Service/ImportLegacyStoragesService.php',
'OCA\\Files_External\\Service\\LegacyStoragesService' => $baseDir . '/../lib/Service/LegacyStoragesService.php',
'OCA\\Files_External\\Service\\MountCacheService' => $baseDir . '/../lib/Service/MountCacheService.php',
'OCA\\Files_External\\Service\\StoragesService' => $baseDir . '/../lib/Service/StoragesService.php',
'OCA\\Files_External\\Service\\UserGlobalStoragesService' => $baseDir . '/../lib/Service/UserGlobalStoragesService.php',
'OCA\\Files_External\\Service\\UserStoragesService' => $baseDir . '/../lib/Service/UserStoragesService.php',
Expand Down
4 changes: 4 additions & 0 deletions apps/files_external/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class ComposerStaticInitFiles_External
'OCA\\Files_External\\Controller\\StoragesController' => __DIR__ . '/..' . '/../lib/Controller/StoragesController.php',
'OCA\\Files_External\\Controller\\UserGlobalStoragesController' => __DIR__ . '/..' . '/../lib/Controller/UserGlobalStoragesController.php',
'OCA\\Files_External\\Controller\\UserStoragesController' => __DIR__ . '/..' . '/../lib/Controller/UserStoragesController.php',
'OCA\\Files_External\\Event\\StorageCreatedEvent' => __DIR__ . '/..' . '/../lib/Event/StorageCreatedEvent.php',
'OCA\\Files_External\\Event\\StorageDeletedEvent' => __DIR__ . '/..' . '/../lib/Event/StorageDeletedEvent.php',
'OCA\\Files_External\\Event\\StorageUpdatedEvent' => __DIR__ . '/..' . '/../lib/Event/StorageUpdatedEvent.php',
'OCA\\Files_External\\Lib\\Auth\\AmazonS3\\AccessKey' => __DIR__ . '/..' . '/../lib/Lib/Auth/AmazonS3/AccessKey.php',
'OCA\\Files_External\\Lib\\Auth\\AuthMechanism' => __DIR__ . '/..' . '/../lib/Lib/Auth/AuthMechanism.php',
'OCA\\Files_External\\Lib\\Auth\\Builtin' => __DIR__ . '/..' . '/../lib/Lib/Auth/Builtin.php',
Expand Down Expand Up @@ -132,6 +135,7 @@ class ComposerStaticInitFiles_External
'OCA\\Files_External\\Service\\GlobalStoragesService' => __DIR__ . '/..' . '/../lib/Service/GlobalStoragesService.php',
'OCA\\Files_External\\Service\\ImportLegacyStoragesService' => __DIR__ . '/..' . '/../lib/Service/ImportLegacyStoragesService.php',
'OCA\\Files_External\\Service\\LegacyStoragesService' => __DIR__ . '/..' . '/../lib/Service/LegacyStoragesService.php',
'OCA\\Files_External\\Service\\MountCacheService' => __DIR__ . '/..' . '/../lib/Service/MountCacheService.php',
'OCA\\Files_External\\Service\\StoragesService' => __DIR__ . '/..' . '/../lib/Service/StoragesService.php',
'OCA\\Files_External\\Service\\UserGlobalStoragesService' => __DIR__ . '/..' . '/../lib/Service/UserGlobalStoragesService.php',
'OCA\\Files_External\\Service\\UserStoragesService' => __DIR__ . '/..' . '/../lib/Service/UserStoragesService.php',
Expand Down
23 changes: 18 additions & 5 deletions apps/files_external/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
use OCA\Files_External\Config\ConfigAdapter;
use OCA\Files_External\Config\UserPlaceholderHandler;
use OCA\Files_External\ConfigLexicon;
use OCA\Files_External\Event\StorageCreatedEvent;
use OCA\Files_External\Event\StorageDeletedEvent;
use OCA\Files_External\Event\StorageUpdatedEvent;
use OCA\Files_External\Lib\Auth\AmazonS3\AccessKey;
use OCA\Files_External\Lib\Auth\Builtin;
use OCA\Files_External\Lib\Auth\NullMechanism;
Expand Down Expand Up @@ -42,19 +45,22 @@
use OCA\Files_External\Lib\Config\IBackendProvider;
use OCA\Files_External\Listener\GroupDeletedListener;
use OCA\Files_External\Listener\LoadAdditionalListener;
use OCA\Files_External\Listener\StorePasswordListener;
use OCA\Files_External\Listener\UserDeletedListener;
use OCA\Files_External\Service\BackendService;
use OCA\Files_External\Service\MountCacheService;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\QueryException;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Group\Events\BeforeGroupDeletedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\User\Events\PasswordUpdatedEvent;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\User\Events\PostLoginEvent;
use OCP\User\Events\UserCreatedEvent;
use OCP\User\Events\UserDeletedEvent;
use OCP\User\Events\UserLoggedInEvent;

/**
* @package OCA\Files_External\AppInfo
Expand All @@ -75,8 +81,15 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
$context->registerEventListener(GroupDeletedEvent::class, GroupDeletedListener::class);
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
$context->registerEventListener(UserLoggedInEvent::class, StorePasswordListener::class);
$context->registerEventListener(PasswordUpdatedEvent::class, StorePasswordListener::class);
$context->registerEventListener(StorageCreatedEvent::class, MountCacheService::class);
$context->registerEventListener(StorageDeletedEvent::class, MountCacheService::class);
$context->registerEventListener(StorageUpdatedEvent::class, MountCacheService::class);
$context->registerEventListener(BeforeGroupDeletedEvent::class, MountCacheService::class);
$context->registerEventListener(UserCreatedEvent::class, MountCacheService::class);
$context->registerEventListener(UserAddedEvent::class, MountCacheService::class);
$context->registerEventListener(UserRemovedEvent::class, MountCacheService::class);
$context->registerEventListener(PostLoginEvent::class, MountCacheService::class);

$context->registerConfigLexicon(ConfigLexicon::class);
}

Expand Down
17 changes: 11 additions & 6 deletions apps/files_external/lib/Config/ConfigAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use OCA\Files_External\Service\UserGlobalStoragesService;
use OCA\Files_External\Service\UserStoragesService;
use OCP\AppFramework\QueryException;
use OCP\Files\Config\IAuthoritativeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\ObjectStore\IObjectStore;
Expand All @@ -32,7 +33,7 @@
/**
* Make the old files_external config work with the new public mount config api
*/
class ConfigAdapter implements IMountProvider {
class ConfigAdapter implements IMountProvider, IAuthoritativeMountProvider {
public function __construct(
private UserStoragesService $userStoragesService,
private UserGlobalStoragesService $userGlobalStoragesService,
Expand Down Expand Up @@ -73,6 +74,11 @@ private function prepareStorageConfig(StorageConfig &$storage, IUser $user): voi
$storage->getBackend()->manipulateStorageConfig($storage, $user);
}

public function constructStorageForUser(IUser $user, StorageConfig $storage) {
$this->prepareStorageConfig($storage, $user);
return $this->constructStorage($storage);
}

/**
* Construct the storage implementation
*
Expand Down Expand Up @@ -105,8 +111,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {

$storages = array_map(function (StorageConfig $storageConfig) use ($user) {
try {
$this->prepareStorageConfig($storageConfig, $user);
return $this->constructStorage($storageConfig);
return $this->constructStorageForUser($user, $storageConfig);
} catch (\Exception $e) {
// propagate exception into filesystem
return new FailedStorage(['exception' => $e]);
Expand All @@ -123,7 +128,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
$availability = $storage->getAvailability();
if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
$storage = new FailedStorage([
'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available')
'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available'),
]);
}
} catch (\Exception $e) {
Expand All @@ -148,7 +153,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
null,
$loader,
$storageConfig->getMountOptions(),
$storageConfig->getId()
$storageConfig->getId(),
);
} else {
return new SystemMountPoint(
Expand All @@ -158,7 +163,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
null,
$loader,
$storageConfig->getMountOptions(),
$storageConfig->getId()
$storageConfig->getId(),
);
}
}, $storageConfigs, $availableStorages);
Expand Down
24 changes: 24 additions & 0 deletions apps/files_external/lib/Event/StorageCreatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_External\Event;

use OCA\Files_External\Lib\StorageConfig;
use OCP\EventDispatcher\Event;

class StorageCreatedEvent extends Event {
public function __construct(
private readonly StorageConfig $newConfig,
) {
parent::__construct();
}

public function getNewConfig(): StorageConfig {
return $this->newConfig;
}
}
24 changes: 24 additions & 0 deletions apps/files_external/lib/Event/StorageDeletedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_External\Event;

use OCA\Files_External\Lib\StorageConfig;
use OCP\EventDispatcher\Event;

class StorageDeletedEvent extends Event {
public function __construct(
private readonly StorageConfig $oldConfig,
) {
parent::__construct();
}

public function getOldConfig(): StorageConfig {
return $this->oldConfig;
}
}
29 changes: 29 additions & 0 deletions apps/files_external/lib/Event/StorageUpdatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files_External\Event;

use OCA\Files_External\Lib\StorageConfig;
use OCP\EventDispatcher\Event;

class StorageUpdatedEvent extends Event {
public function __construct(
private readonly StorageConfig $oldConfig,
private readonly StorageConfig $newConfig,
) {
parent::__construct();
}

public function getOldConfig(): StorageConfig {
return $this->oldConfig;
}

public function getNewConfig(): StorageConfig {
return $this->newConfig;
}
}
20 changes: 20 additions & 0 deletions apps/files_external/lib/Lib/StorageConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OCA\Files_External\Lib\Auth\IUserProvided;
use OCA\Files_External\Lib\Backend\Backend;
use OCA\Files_External\ResponseDefinitions;
use OCP\IUser;

/**
* External storage configuration
Expand Down Expand Up @@ -435,4 +436,23 @@ protected function formatStorageForUI(): void {
}
}
}

public function getMountPointForUser(IUser $user): string {
return '/' . $user->getUID() . '/files/' . trim($this->mountPoint, '/') . '/';
}

public function __clone() {
$clone = new StorageConfig($this->getId());
$clone->setBackend(clone $this->getBackend());
$clone->setAuthMechanism(clone $this->getAuthMechanism());
$clone->setBackendOptions($this->getBackendOptions());
$clone->setMountPoint($this->getMountPoint());
$clone->setStatus($this->getStatus(), $this->getStatusMessage());
$clone->setPriority($this->getPriority());
$clone->setApplicableUsers($this->getApplicableUsers());
$clone->setApplicableGroups($this->getApplicableGroups());
$clone->setMountOptions($this->getMountOptions());
$clone->setType($this->getType());
return $clone;
}
}
26 changes: 26 additions & 0 deletions apps/files_external/lib/Service/DBConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,32 @@ public function getMountsForUser($userId, $groupIds) {
return $this->getMountsFromQuery($query);
}

public function getMountsForGroups($groupIds) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
->from('external_mounts', 'm')
->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
->where($builder->expr()->andX( // mounts for group
$builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
$builder->expr()->in('a.value', $builder->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY)),
));

return $this->getMountsFromQuery($query);
}

public function getGlobalMounts() {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
->from('external_mounts', 'm')
->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
->where($builder->expr()->andX( // global mounts
$builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GLOBAL, IQueryBuilder::PARAM_INT)),
$builder->expr()->isNull('a.value'),
), );

return $this->getMountsFromQuery($query);
}

public function modifyMountsOnUserDelete(string $uid): void {
$this->modifyMountsOnDelete($uid, self::APPLICABLE_TYPE_USER);
}
Expand Down
35 changes: 35 additions & 0 deletions apps/files_external/lib/Service/GlobalStoragesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
namespace OCA\Files_External\Service;

use OC\Files\Filesystem;
use OCA\Files_External\Event\StorageCreatedEvent;
use OCA\Files_External\Event\StorageDeletedEvent;
use OCA\Files_External\Event\StorageUpdatedEvent;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\MountConfig;
use OCP\IGroup;

/**
* Service class to manage global external storage
Expand Down Expand Up @@ -62,9 +66,13 @@ protected function triggerHooks(StorageConfig $storage, $signal) {
protected function triggerChangeHooks(StorageConfig $oldStorage, StorageConfig $newStorage) {
// if mount point changed, it's like a deletion + creation
if ($oldStorage->getMountPoint() !== $newStorage->getMountPoint()) {
$this->eventDispatcher->dispatchTyped(new StorageDeletedEvent($oldStorage));
$this->eventDispatcher->dispatchTyped(new StorageCreatedEvent($newStorage));
$this->triggerHooks($oldStorage, Filesystem::signal_delete_mount);
$this->triggerHooks($newStorage, Filesystem::signal_create_mount);
return;
} else {
$this->eventDispatcher->dispatchTyped(new StorageUpdatedEvent($oldStorage, $newStorage));
}

$userAdditions = array_diff($newStorage->getApplicableUsers(), $oldStorage->getApplicableUsers());
Expand Down Expand Up @@ -162,4 +170,31 @@ public function getStorageForAllUsers() {

return array_combine($keys, $configs);
}

/**
* Gets all storages for the group, not including any global storages
* @return StorageConfig[]
*/
public function getAllStoragesForGroup(IGroup $group): array {
$mounts = $this->dbConfig->getMountsForGroups([$group->getGID()]);
$configs = array_map($this->getStorageConfigFromDBMount(...), $mounts);
$configs = array_filter($configs, static fn (?StorageConfig $config): bool => $config instanceof StorageConfig);
$keys = array_map(static fn (StorageConfig $config) => $config->getId(), $configs);

$storages = array_combine($keys, $configs);
return array_filter($storages, $this->validateStorage(...));
}

/**
* @return StorageConfig[]
*/
public function getAllGlobalStorages(): array {
$mounts = $this->dbConfig->getGlobalMounts();

$configs = array_map($this->getStorageConfigFromDBMount(...), $mounts);
$configs = array_filter($configs, static fn (?StorageConfig $config): bool => $config instanceof StorageConfig);
$keys = array_map(static fn (StorageConfig $config) => $config->getId(), $configs);
$storages = array_combine($keys, $configs);
return array_filter($storages, $this->validateStorage(...));
}
}
Loading
Loading