Merge pull request #39863 from nextcloud/sharing-mask-wrapper

move share permission logic to storage wrapper
pull/39864/head
Robin Appelman 9 months ago committed by GitHub
commit 64f62f7cfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -36,6 +36,8 @@
namespace OCA\Files_Sharing\Tests;
use OC\Files\Cache\Scanner;
use OC\Files\Filesystem;
use OC\Files\SetupManager;
use OCA\Files_Sharing\Controller\ShareAPIController;
use OCP\App\IAppManager;
use OCP\AppFramework\OCS\OCSBadRequestException;
@ -74,6 +76,8 @@ class ApiTest extends TestCase {
\OC::$server->getConfig()->setAppValue('core', 'shareapi_exclude_groups', 'no');
\OC::$server->getConfig()->setAppValue('core', 'shareapi_expire_after_n_days', '7');
Filesystem::getLoader()->removeStorageWrapper('sharing_mask');
$this->folder = self::TEST_FOLDER_NAME;
$this->subfolder = '/subfolder_share_api_test';
$this->subsubfolder = '/subsubfolder_share_api_test';

@ -30,6 +30,7 @@ namespace OCA\Files_Sharing\Tests;
use OC\KnownUser\KnownUserService;
use OC\Share20\Manager;
use OC\Share20\ShareDisableChecker;
use OCA\Files_Sharing\Capabilities;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\IRootFolder;
@ -94,7 +95,8 @@ class CapabilitiesTest extends \Test\TestCase {
$this->createMock(\OC_Defaults::class),
$this->createMock(IEventDispatcher::class),
$this->createMock(IUserSession::class),
$this->createMock(KnownUserService::class)
$this->createMock(KnownUserService::class),
$this->createMock(ShareDisableChecker::class)
);
$cap = new Capabilities($config, $shareManager);
$result = $this->getFilesSharingPart($cap->getCapabilities());

@ -1655,6 +1655,7 @@ return array(
'OC\\Share20\\PublicShareTemplateFactory' => $baseDir . '/lib/private/Share20/PublicShareTemplateFactory.php',
'OC\\Share20\\Share' => $baseDir . '/lib/private/Share20/Share.php',
'OC\\Share20\\ShareAttributes' => $baseDir . '/lib/private/Share20/ShareAttributes.php',
'OC\\Share20\\ShareDisableChecker' => $baseDir . '/lib/private/Share20/ShareDisableChecker.php',
'OC\\Share20\\ShareHelper' => $baseDir . '/lib/private/Share20/ShareHelper.php',
'OC\\Share20\\UserRemovedListener' => $baseDir . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php',

@ -1688,6 +1688,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Share20\\PublicShareTemplateFactory' => __DIR__ . '/../../..' . '/lib/private/Share20/PublicShareTemplateFactory.php',
'OC\\Share20\\Share' => __DIR__ . '/../../..' . '/lib/private/Share20/Share.php',
'OC\\Share20\\ShareAttributes' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareAttributes.php',
'OC\\Share20\\ShareDisableChecker' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareDisableChecker.php',
'OC\\Share20\\ShareHelper' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareHelper.php',
'OC\\Share20\\UserRemovedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php',

@ -233,7 +233,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
}
/**
* Return the currently version used for the HMAC in the encryption app
* Return the current version used for the HMAC in the encryption app
*/
public function getEncryptedVersion(): int {
return isset($this->data['encryptedVersion']) ? (int) $this->data['encryptedVersion'] : 1;
@ -243,11 +243,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* @return int
*/
public function getPermissions() {
$perms = (int) $this->data['permissions'];
if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) {
$perms = $perms & ~\OCP\Constants::PERMISSION_SHARE;
}
return $perms;
return (int) $this->data['permissions'];
}
/**

@ -26,6 +26,7 @@
namespace OC\Files\ObjectStore;
use OC\User\User;
use OCP\IUser;
class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IHomeStorage {
/**
@ -61,7 +62,7 @@ class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IH
* @param string $path, optional
* @return \OC\User\User
*/
public function getUser($path = null) {
public function getUser($path = null): IUser {
return $this->user;
}
}

@ -34,9 +34,12 @@ use OC\Files\Storage\Wrapper\Encoding;
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\Storage\Wrapper\Quota;
use OC\Lockdown\Filesystem\NullStorage;
use OC\Share\Share;
use OC\Share20\ShareDisableChecker;
use OC_App;
use OC_Hook;
use OC_Util;
use OCA\Files_Sharing\ISharedStorage;
use OCP\Constants;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
@ -64,52 +67,33 @@ use Psr\Log\LoggerInterface;
class SetupManager {
private bool $rootSetup = false;
private IEventLogger $eventLogger;
private MountProviderCollection $mountProviderCollection;
private IMountManager $mountManager;
private IUserManager $userManager;
// List of users for which at least one mount is setup
private array $setupUsers = [];
// List of users for which all mounts are setup
private array $setupUsersComplete = [];
/** @var array<string, string[]> */
private array $setupUserMountProviders = [];
private IEventDispatcher $eventDispatcher;
private IUserMountCache $userMountCache;
private ILockdownManager $lockdownManager;
private IUserSession $userSession;
private ICache $cache;
private LoggerInterface $logger;
private IConfig $config;
private bool $listeningForProviders;
private array $fullSetupRequired = [];
private bool $setupBuiltinWrappersDone = false;
public function __construct(
IEventLogger $eventLogger,
MountProviderCollection $mountProviderCollection,
IMountManager $mountManager,
IUserManager $userManager,
IEventDispatcher $eventDispatcher,
IUserMountCache $userMountCache,
ILockdownManager $lockdownManager,
IUserSession $userSession,
private IEventLogger $eventLogger,
private MountProviderCollection $mountProviderCollection,
private IMountManager $mountManager,
private IUserManager $userManager,
private IEventDispatcher $eventDispatcher,
private IUserMountCache $userMountCache,
private ILockdownManager $lockdownManager,
private IUserSession $userSession,
ICacheFactory $cacheFactory,
LoggerInterface $logger,
IConfig $config
private LoggerInterface $logger,
private IConfig $config,
private ShareDisableChecker $shareDisableChecker,
) {
$this->eventLogger = $eventLogger;
$this->mountProviderCollection = $mountProviderCollection;
$this->mountManager = $mountManager;
$this->userManager = $userManager;
$this->eventDispatcher = $eventDispatcher;
$this->userMountCache = $userMountCache;
$this->lockdownManager = $lockdownManager;
$this->logger = $logger;
$this->userSession = $userSession;
$this->cache = $cacheFactory->createDistributed('setupmanager::');
$this->listeningForProviders = false;
$this->config = $config;
$this->setupListeners();
}
@ -139,15 +123,23 @@ class SetupManager {
return $storage;
});
Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
if (!$mount->getOption('enable_sharing', true)) {
return new PermissionsMask([
'storage' => $storage,
'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
]);
$reSharingEnabled = Share::isResharingAllowed();
$user = $this->userSession->getUser();
$sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true;
Filesystem::addStorageWrapper(
'sharing_mask',
function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) {
$sharingEnabledForMount = $mount->getOption('enable_sharing', true);
$isShared = $storage->instanceOfStorage(ISharedStorage::class);
if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) {
return new PermissionsMask([
'storage' => $storage,
'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
]);
}
return $storage;
}
return $storage;
});
);
// install storage availability wrapper, before most other wrappers
Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) {

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OC\Files;
use OC\Share20\ShareDisableChecker;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IMountProviderCollection;
@ -36,40 +37,21 @@ use OCP\Lockdown\ILockdownManager;
use Psr\Log\LoggerInterface;
class SetupManagerFactory {
private IEventLogger $eventLogger;
private IMountProviderCollection $mountProviderCollection;
private IUserManager $userManager;
private IEventDispatcher $eventDispatcher;
private IUserMountCache $userMountCache;
private ILockdownManager $lockdownManager;
private IUserSession $userSession;
private ?SetupManager $setupManager;
private ICacheFactory $cacheFactory;
private LoggerInterface $logger;
private IConfig $config;
public function __construct(
IEventLogger $eventLogger,
IMountProviderCollection $mountProviderCollection,
IUserManager $userManager,
IEventDispatcher $eventDispatcher,
IUserMountCache $userMountCache,
ILockdownManager $lockdownManager,
IUserSession $userSession,
ICacheFactory $cacheFactory,
LoggerInterface $logger,
IConfig $config
private IEventLogger $eventLogger,
private IMountProviderCollection $mountProviderCollection,
private IUserManager $userManager,
private IEventDispatcher $eventDispatcher,
private IUserMountCache $userMountCache,
private ILockdownManager $lockdownManager,
private IUserSession $userSession,
private ICacheFactory $cacheFactory,
private LoggerInterface $logger,
private IConfig $config,
private ShareDisableChecker $shareDisableChecker,
) {
$this->eventLogger = $eventLogger;
$this->mountProviderCollection = $mountProviderCollection;
$this->userManager = $userManager;
$this->eventDispatcher = $eventDispatcher;
$this->userMountCache = $userMountCache;
$this->lockdownManager = $lockdownManager;
$this->userSession = $userSession;
$this->cacheFactory = $cacheFactory;
$this->logger = $logger;
$this->config = $config;
$this->setupManager = null;
}
@ -86,7 +68,8 @@ class SetupManagerFactory {
$this->userSession,
$this->cacheFactory,
$this->logger,
$this->config
$this->config,
$this->shareDisableChecker,
);
}
return $this->setupManager;

@ -26,6 +26,7 @@
namespace OC\Files\Storage;
use OC\Files\Cache\HomePropagator;
use OCP\IUser;
/**
* Specialized version of Local storage for home directory usage
@ -94,7 +95,7 @@ class Home extends Local implements \OCP\Files\IHomeStorage {
*
* @return \OC\User\User owner of this home storage
*/
public function getUser() {
public function getUser(): IUser {
return $this->user;
}

@ -147,6 +147,7 @@ use OC\Security\TrustedDomainHelper;
use OC\Security\VerificationToken\VerificationToken;
use OC\Session\CryptoWrapper;
use OC\Share20\ProviderFactory;
use OC\Share20\ShareDisableChecker;
use OC\Share20\ShareHelper;
use OC\SpeechToText\SpeechToTextManager;
use OC\SystemTag\ManagerFactory as SystemTagManagerFactory;
@ -1252,7 +1253,8 @@ class Server extends ServerContainer implements IServerContainer {
$c->get('ThemingDefaults'),
$c->get(IEventDispatcher::class),
$c->get(IUserSession::class),
$c->get(KnownUserService::class)
$c->get(KnownUserService::class),
$c->get(ShareDisableChecker::class)
);
return $manager;

@ -41,7 +41,6 @@
*/
namespace OC\Share20;
use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MoveableMount;
use OC\KnownUser\KnownUserService;
use OC\Share20\Exception\ProviderException;
@ -106,8 +105,6 @@ class Manager implements IManager {
private $userManager;
/** @var IRootFolder */
private $rootFolder;
/** @var CappedMemoryCache */
private $sharingDisabledForUsersCache;
/** @var LegacyHooks */
private $legacyHooks;
/** @var IMailer */
@ -122,6 +119,7 @@ class Manager implements IManager {
private $userSession;
/** @var KnownUserService */
private $knownUserService;
private ShareDisableChecker $shareDisableChecker;
public function __construct(
LoggerInterface $logger,
@ -140,7 +138,8 @@ class Manager implements IManager {
\OC_Defaults $defaults,
IEventDispatcher $dispatcher,
IUserSession $userSession,
KnownUserService $knownUserService
KnownUserService $knownUserService,
ShareDisableChecker $shareDisableChecker
) {
$this->logger = $logger;
$this->config = $config;
@ -153,7 +152,6 @@ class Manager implements IManager {
$this->factory = $factory;
$this->userManager = $userManager;
$this->rootFolder = $rootFolder;
$this->sharingDisabledForUsersCache = new CappedMemoryCache();
// The constructor of LegacyHooks registers the listeners of share events
// do not remove if those are not properly migrated
$this->legacyHooks = new LegacyHooks($dispatcher);
@ -163,6 +161,7 @@ class Manager implements IManager {
$this->dispatcher = $dispatcher;
$this->userSession = $userSession;
$this->knownUserService = $knownUserService;
$this->shareDisableChecker = $shareDisableChecker;
}
/**
@ -2025,37 +2024,7 @@ class Manager implements IManager {
* @return bool
*/
public function sharingDisabledForUser($userId) {
if ($userId === null) {
return false;
}
if (isset($this->sharingDisabledForUsersCache[$userId])) {
return $this->sharingDisabledForUsersCache[$userId];
}
if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
$excludedGroups = json_decode($groupsList);
if (is_null($excludedGroups)) {
$excludedGroups = explode(',', $groupsList);
$newValue = json_encode($excludedGroups);
$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
}
$user = $this->userManager->get($userId);
$usersGroups = $this->groupManager->getUserGroupIds($user);
if (!empty($usersGroups)) {
$remainingGroups = array_diff($usersGroups, $excludedGroups);
// if the user is only in groups which are disabled for sharing then
// sharing is also disabled for the user
if (empty($remainingGroups)) {
$this->sharingDisabledForUsersCache[$userId] = true;
return true;
}
}
}
$this->sharingDisabledForUsersCache[$userId] = false;
return false;
return $this->shareDisableChecker->sharingDisabledForUser($userId);
}
/**

@ -0,0 +1,65 @@
<?php
namespace OC\Share20;
use OCP\Cache\CappedMemoryCache;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUserManager;
/**
* split of from the share manager to allow using it with minimal DI
*/
class ShareDisableChecker {
private CappedMemoryCache $sharingDisabledForUsersCache;
public function __construct(
private IConfig $config,
private IUserManager $userManager,
private IGroupManager $groupManager,
) {
$this->sharingDisabledForUsersCache = new CappedMemoryCache();
}
/**
* @param ?string $userId
* @return bool
*/
public function sharingDisabledForUser(?string $userId) {
if ($userId === null) {
return false;
}
if (isset($this->sharingDisabledForUsersCache[$userId])) {
return $this->sharingDisabledForUsersCache[$userId];
}
if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
$excludedGroups = json_decode($groupsList);
if (is_null($excludedGroups)) {
$excludedGroups = explode(',', $groupsList);
$newValue = json_encode($excludedGroups);
$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
}
$user = $this->userManager->get($userId);
if (!$user) {
return false;
}
$usersGroups = $this->groupManager->getUserGroupIds($user);
if (!empty($usersGroups)) {
$remainingGroups = array_diff($usersGroups, $excludedGroups);
// if the user is only in groups which are disabled for sharing then
// sharing is also disabled for the user
if (empty($remainingGroups)) {
$this->sharingDisabledForUsersCache[$userId] = true;
return true;
}
}
}
$this->sharingDisabledForUsersCache[$userId] = false;
return false;
}
}

@ -27,6 +27,7 @@
namespace OCP\Files;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
/**
* Interface IHomeStorage
@ -34,4 +35,11 @@ use OCP\Files\Storage\IStorage;
* @since 7.0.0
*/
interface IHomeStorage extends IStorage {
/**
* Get the user for this home storage
*
* @return IUser
* @since 28.0.0
*/
public function getUser(): IUser;
}

@ -7,6 +7,7 @@
namespace Test\Files;
use OC\Share20\ShareDisableChecker;
use OCP\Cache\CappedMemoryCache;
use OC\Files\Cache\Watcher;
use OC\Files\Filesystem;
@ -296,7 +297,7 @@ class ViewTest extends \Test\TestCase {
*/
public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) {
// Reset sharing disabled for users cache
self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
$config = \OC::$server->getConfig();
$oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no');
@ -321,7 +322,7 @@ class ViewTest extends \Test\TestCase {
$config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList);
// Reset sharing disabled for users cache
self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
}
public function testCacheIncompleteFolder() {

@ -27,6 +27,7 @@ use OC\Share20\DefaultShareProvider;
use OC\Share20\Exception;
use OC\Share20\Manager;
use OC\Share20\Share;
use OC\Share20\ShareDisableChecker;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
@ -111,6 +112,8 @@ class ManagerTest extends \Test\TestCase {
protected $userSession;
/** @var KnownUserService|MockObject */
protected $knownUserService;
/** @var ShareDisableChecker|MockObject */
protected $shareDisabledChecker;
protected function setUp(): void {
$this->logger = $this->createMock(LoggerInterface::class);
@ -128,6 +131,8 @@ class ManagerTest extends \Test\TestCase {
$this->userSession = $this->createMock(IUserSession::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->l = $this->createMock(IL10N::class);
$this->l->method('t')
@ -158,7 +163,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker
);
$this->defaultProvider = $this->createMock(DefaultShareProvider::class);
@ -188,7 +194,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
]);
}
@ -2755,7 +2762,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$share = $this->createMock(IShare::class);
@ -2802,7 +2810,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$share = $this->createMock(IShare::class);
@ -2856,7 +2865,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$share = $this->createMock(IShare::class);
@ -4255,7 +4265,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$this->assertSame($expected,
$manager->shareProviderExists($shareType)
@ -4289,7 +4300,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$factory->setProvider($this->defaultProvider);
@ -4354,7 +4366,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$factory->setProvider($this->defaultProvider);
@ -4471,7 +4484,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker,
);
$factory->setProvider($this->defaultProvider);
@ -4597,7 +4611,8 @@ class ManagerTest extends \Test\TestCase {
$this->defaults,
$this->dispatcher,
$this->userSession,
$this->knownUserService
$this->knownUserService,
$this->shareDisabledChecker
);
$factory->setProvider($this->defaultProvider);

Loading…
Cancel
Save