Merge pull request #49073 from nextcloud/feat/files_sharing/co-owner

pull/49477/head
Kate 2024-11-25 14:41:40 +07:00 committed by GitHub
commit 235b7d7c26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 427 additions and 57 deletions

@ -310,7 +310,7 @@ class OwnershipTransferService {
foreach ($supportedShareTypes as $shareType) {
$offset = 0;
while (true) {
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset, onlyValid: false);
$progress->advance(count($sharePage));
if (empty($sharePage)) {
break;
@ -464,7 +464,7 @@ class OwnershipTransferService {
}
$share->setNodeId($newNodeId);
$this->shareManager->updateShare($share);
$this->shareManager->updateShare($share, onlyValid: false);
}
}
} catch (NotFoundException $e) {

@ -35,6 +35,7 @@ use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IShareOwnerlessMount;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\HintException;
@ -1235,18 +1236,6 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_LINK
|| $share->getShareType() === IShare::TYPE_EMAIL) {
/**
* We do not allow editing link shares that the current user
* doesn't own. This is confusing and lead to errors when
* someone else edit a password or expiration date without
* the share owner knowing about it.
* We only allow deletion
*/
if ($share->getSharedBy() !== $this->userId) {
throw new OCSForbiddenException($this->l->t('You are not allowed to edit link shares that you don\'t own'));
}
// Update hide download state
$attributes = $share->getAttributes() ?? $share->newAttributes();
if ($hideDownload === 'true') {
@ -1553,6 +1542,12 @@ class ShareAPIController extends OCSController {
return true;
}
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$file = $userFolder->getFirstNodeById($share->getNodeId());
if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
return true;
}
//! we do NOT support some kind of `admin` in groups.
//! You cannot edit shares shared to a group you're
//! a member of if you're not the share owner or the file owner!
@ -1588,6 +1583,12 @@ class ShareAPIController extends OCSController {
return true;
}
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$file = $userFolder->getFirstNodeById($share->getNodeId());
if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
return true;
}
return false;
}

@ -50,10 +50,14 @@ class Updater {
$shareManager = \OC::$server->getShareManager();
// We intentionally include invalid shares, as they have been automatically invalidated due to the node no longer
// being accessible for the user. Only in this case where we adjust the share after it was moved we want to ignore
// this to be able to still adjust it.
// FIXME: should CIRCLES be included here ??
$shares = $shareManager->getSharesBy($user->getUID(), IShare::TYPE_USER, $src, false, -1);
$shares = array_merge($shares, $shareManager->getSharesBy($user->getUID(), IShare::TYPE_GROUP, $src, false, -1));
$shares = array_merge($shares, $shareManager->getSharesBy($user->getUID(), IShare::TYPE_ROOM, $src, false, -1));
$shares = $shareManager->getSharesBy($user->getUID(), IShare::TYPE_USER, $src, false, -1, onlyValid: false);
$shares = array_merge($shares, $shareManager->getSharesBy($user->getUID(), IShare::TYPE_GROUP, $src, false, -1, onlyValid: false));
$shares = array_merge($shares, $shareManager->getSharesBy($user->getUID(), IShare::TYPE_ROOM, $src, false, -1, onlyValid: false));
if ($src instanceof Folder) {
$cacheAccess = Server::get(FileAccess::class);
@ -61,9 +65,9 @@ class Updater {
$sourceStorageId = $src->getStorage()->getCache()->getNumericStorageId();
$sourceInternalPath = $src->getInternalPath();
$subShares = array_merge(
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_USER),
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_GROUP),
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_ROOM),
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_USER, onlyValid: false),
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_GROUP, onlyValid: false),
$shareManager->getSharesBy($user->getUID(), IShare::TYPE_ROOM, onlyValid: false),
);
$shareSourceIds = array_map(fn (IShare $share) => $share->getNodeId(), $subShares);
$shareSources = $cacheAccess->getByFileIdsInStorage($shareSourceIds, $sourceStorageId);
@ -111,7 +115,7 @@ class Updater {
$share->setShareOwner($newOwner);
$share->setPermissions($newPermissions);
$shareManager->updateShare($share);
$shareManager->updateShare($share, onlyValid: false);
}
}

@ -78,6 +78,11 @@ export default {
} else if (this.share.type === this.SHARE_TYPES.SHARE_TYPE_GUEST) {
title += ` (${t('files_sharing', 'guest')})`
}
if (!this.isShareOwner && this.share.ownerDisplayName) {
title += ' ' + t('files_sharing', 'by {initiator}', {
initiator: this.share.ownerDisplayName,
})
}
return title
},
tooltip() {

@ -18,6 +18,7 @@ use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Mount\IShareOwnerlessMount;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorage;
use OCP\IConfig;
@ -232,10 +233,20 @@ class ShareAPIControllerTest extends TestCase {
$this->expectExceptionMessage('Could not delete share');
$node = $this->getMockBuilder(File::class)->getMock();
$node->method('getId')->willReturn(1);
$share = $this->newShare();
$share->setNode($node);
$userFolder = $this->getMockBuilder(Folder::class)->getMock();
$this->rootFolder->method('getUserFolder')
->with($this->currentUser)
->willReturn($userFolder);
$userFolder->method('getFirstNodeById')
->with($share->getNodeId())
->willReturn($node);
$this->shareManager
->expects($this->once())
->method('getShareById')
@ -476,6 +487,62 @@ class ShareAPIControllerTest extends TestCase {
$this->ocs->deleteShare(42);
}
public function testDeleteShareOwnerless(): void {
$ocs = $this->mockFormatShare();
$mount = $this->createMock(IShareOwnerlessMount::class);
$file = $this->createMock(File::class);
$file
->expects($this->exactly(2))
->method('getPermissions')
->willReturn(Constants::PERMISSION_SHARE);
$file
->expects($this->once())
->method('getMountPoint')
->willReturn($mount);
$userFolder = $this->createMock(Folder::class);
$userFolder
->expects($this->exactly(2))
->method('getFirstNodeById')
->with(2)
->willReturn($file);
$this->rootFolder
->method('getUserFolder')
->with($this->currentUser)
->willReturn($userFolder);
$share = $this->createMock(IShare::class);
$share
->expects($this->once())
->method('getNode')
->willReturn($file);
$share
->expects($this->exactly(2))
->method('getNodeId')
->willReturn(2);
$share
->expects($this->exactly(2))
->method('getPermissions')
->willReturn(Constants::PERMISSION_SHARE);
$this->shareManager
->expects($this->once())
->method('getShareById')
->with('ocinternal:1', $this->currentUser)
->willReturn($share);
$this->shareManager
->expects($this->once())
->method('deleteShare')
->with($share);
$result = $ocs->deleteShare(1);
$this->assertInstanceOf(DataResponse::class, $result);
}
/*
* FIXME: Enable once we have a federated Share Provider
@ -3748,6 +3815,63 @@ class ShareAPIControllerTest extends TestCase {
$this->assertInstanceOf(DataResponse::class, $result);
}
public function testUpdateShareOwnerless(): void {
$ocs = $this->mockFormatShare();
$mount = $this->createMock(IShareOwnerlessMount::class);
$file = $this->createMock(File::class);
$file
->expects($this->exactly(2))
->method('getPermissions')
->willReturn(Constants::PERMISSION_SHARE);
$file
->expects($this->once())
->method('getMountPoint')
->willReturn($mount);
$userFolder = $this->createMock(Folder::class);
$userFolder
->expects($this->exactly(2))
->method('getFirstNodeById')
->with(2)
->willReturn($file);
$this->rootFolder
->method('getUserFolder')
->with($this->currentUser)
->willReturn($userFolder);
$share = $this->createMock(IShare::class);
$share
->expects($this->once())
->method('getNode')
->willReturn($file);
$share
->expects($this->exactly(2))
->method('getNodeId')
->willReturn(2);
$share
->expects($this->exactly(2))
->method('getPermissions')
->willReturn(Constants::PERMISSION_SHARE);
$this->shareManager
->expects($this->once())
->method('getShareById')
->with('ocinternal:1', $this->currentUser)
->willReturn($share);
$this->shareManager
->expects($this->once())
->method('updateShare')
->with($share)
->willReturn($share);
$result = $ocs->updateShare(1, Constants::PERMISSION_ALL);
$this->assertInstanceOf(DataResponse::class, $result);
}
public function dataFormatShare() {
$file = $this->getMockBuilder(File::class)->getMock();
$folder = $this->getMockBuilder(Folder::class)->getMock();

2
dist/146-146.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
146-146.js.license

2
dist/4242-4242.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
4242-4242.js.license

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -409,6 +409,7 @@ return array(
'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Mount\\IMovableMount' => $baseDir . '/lib/public/Files/Mount/IMovableMount.php',
'OCP\\Files\\Mount\\IShareOwnerlessMount' => $baseDir . '/lib/public/Files/Mount/IShareOwnerlessMount.php',
'OCP\\Files\\Mount\\ISystemMountPoint' => $baseDir . '/lib/public/Files/Mount/ISystemMountPoint.php',
'OCP\\Files\\Node' => $baseDir . '/lib/public/Files/Node.php',
'OCP\\Files\\NotEnoughSpaceException' => $baseDir . '/lib/public/Files/NotEnoughSpaceException.php',

@ -450,6 +450,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Mount\\IMovableMount' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMovableMount.php',
'OCP\\Files\\Mount\\IShareOwnerlessMount' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IShareOwnerlessMount.php',
'OCP\\Files\\Mount\\ISystemMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/ISystemMountPoint.php',
'OCP\\Files\\Node' => __DIR__ . '/../../..' . '/lib/public/Files/Node.php',
'OCP\\Files\\NotEnoughSpaceException' => __DIR__ . '/../../..' . '/lib/public/Files/NotEnoughSpaceException.php',

@ -798,13 +798,15 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv
->andWhere(
$qb->expr()->orX(
$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)),
$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)),
$qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)),
)
)
->andWhere($qb->expr()->orX(
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->orderBy('id', 'ASC')
->executeQuery();
$shares = [];

@ -17,6 +17,7 @@ use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IShareOwnerlessMount;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\HintException;
@ -785,13 +786,13 @@ class Manager implements IManager {
* @throws \InvalidArgumentException
* @throws GenericShareException
*/
public function updateShare(IShare $share) {
public function updateShare(IShare $share, bool $onlyValid = true) {
$expirationDateUpdated = false;
$this->canShare($share);
try {
$originalShare = $this->getShareById($share->getFullId());
$originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
} catch (\UnexpectedValueException $e) {
throw new \InvalidArgumentException($this->l->t('Share does not have a full ID'));
}
@ -1215,23 +1216,32 @@ class Manager implements IManager {
throw new \Exception('non-shallow getSharesInFolder is no longer supported');
}
return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
foreach ($newShares as $fid => $data) {
if (!isset($shares[$fid])) {
$shares[$fid] = [];
}
$isOwnerless = $node->getMountPoint() instanceof IShareOwnerlessMount;
$shares[$fid] = array_merge($shares[$fid], $data);
$shares = [];
foreach ($providers as $provider) {
if ($isOwnerless) {
foreach ($node->getDirectoryListing() as $childNode) {
$data = $provider->getSharesByPath($childNode);
$fid = $childNode->getId();
$shares[$fid] ??= [];
$shares[$fid] = array_merge($shares[$fid], $data);
}
} else {
foreach ($provider->getSharesInFolder($userId, $node, $reshares) as $fid => $data) {
$shares[$fid] ??= [];
$shares[$fid] = array_merge($shares[$fid], $data);
}
}
return $shares;
}, []);
}
return $shares;
}
/**
* @inheritdoc
*/
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
if ($path !== null &&
!($path instanceof \OCP\Files\File) &&
!($path instanceof \OCP\Files\Folder)) {
@ -1244,7 +1254,11 @@ class Manager implements IManager {
return [];
}
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
$shares = array_filter($provider->getSharesByPath($path), static fn (IShare $share) => $share->getShareType() === $shareType);
} else {
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
}
/*
* Work around so we don't return expired shares but still follow
@ -1256,11 +1270,13 @@ class Manager implements IManager {
while (true) {
$added = 0;
foreach ($shares as $share) {
try {
$this->checkShare($share);
} catch (ShareNotFound $e) {
// Ignore since this basically means the share is deleted
continue;
if ($onlyValid) {
try {
$this->checkShare($share);
} catch (ShareNotFound $e) {
// Ignore since this basically means the share is deleted
continue;
}
}
$added++;
@ -1288,7 +1304,12 @@ class Manager implements IManager {
$offset += $added;
// Fetch again $limit shares
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
if ($path?->getMountPoint() instanceof IShareOwnerlessMount) {
// We already fetched all shares, so end here
$shares = [];
} else {
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
}
// No more shares means we are done
if (empty($shares)) {
@ -1347,7 +1368,7 @@ class Manager implements IManager {
/**
* @inheritdoc
*/
public function getShareById($id, $recipient = null) {
public function getShareById($id, $recipient = null, bool $onlyValid = true) {
if ($id === null) {
throw new ShareNotFound();
}
@ -1362,7 +1383,9 @@ class Manager implements IManager {
$share = $provider->getShareById($id, $recipient);
$this->checkShare($share);
if ($onlyValid) {
$this->checkShare($share);
}
return $share;
}
@ -1469,6 +1492,15 @@ class Manager implements IManager {
$this->deleteShare($share);
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
}
try {
$share->getNode();
// Ignore share, file is still accessible
} catch (NotFoundException) {
// Access lost, but maybe only temporarily, so don't delete the share right away
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
}
if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
$uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
foreach ($uids as $uid) {

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Files\Mount;
/**
* Denotes that shares created under this mountpoint will be manageable by everyone with share permission.
*
* @since 31.0.0
*/
interface IShareOwnerlessMount {
}

@ -41,11 +41,12 @@ interface IManager {
* The state can't be changed this way: use acceptShare
*
* @param IShare $share
* @param bool $onlyValid Only updates valid shares, invalid shares will be deleted automatically and are not updated
* @return IShare The share object
* @throws \InvalidArgumentException
* @since 9.0.0
*/
public function updateShare(IShare $share);
public function updateShare(IShare $share, bool $onlyValid = true);
/**
* Accept a share.
@ -127,10 +128,11 @@ interface IManager {
* @param bool $reshares
* @param int $limit The maximum number of returned results, -1 for all results
* @param int $offset
* @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
* @return IShare[]
* @since 9.0.0
*/
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0);
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true);
/**
* Get shares shared with $user.
@ -168,11 +170,12 @@ interface IManager {
*
* @param string $id
* @param string|null $recipient userID of the recipient
* @param bool $onlyValid Only returns valid shares, invalid shares will be deleted automatically and are not returned
* @return IShare
* @throws ShareNotFound
* @since 9.0.0
*/
public function getShareById($id, $recipient = null);
public function getShareById($id, $recipient = null, bool $onlyValid = true);
/**
* Get the share by token possible with password

@ -7,6 +7,7 @@
namespace Test\Share20;
use OC\Files\Node\Node;
use OC\Share20\DefaultShareProvider;
use OC\Share20\ShareAttributes;
use OCP\AppFramework\Utility\ITimeFactory;
@ -3013,4 +3014,88 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->assertEquals('token5', $share->getToken());
$this->assertEquals('myTarget5', $share->getTarget());
}
public function testGetSharesByPath(): void {
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
->values([
'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
'uid_owner' => $qb->expr()->literal('user1'),
'uid_initiator' => $qb->expr()->literal('user1'),
'share_with' => $qb->expr()->literal('user2'),
'item_type' => $qb->expr()->literal('file'),
'file_source' => $qb->expr()->literal(1),
]);
$qb->execute();
$id1 = $qb->getLastInsertId();
$qb->insert('share')
->values([
'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
'uid_owner' => $qb->expr()->literal('user1'),
'uid_initiator' => $qb->expr()->literal('user1'),
'share_with' => $qb->expr()->literal('user2'),
'item_type' => $qb->expr()->literal('file'),
'file_source' => $qb->expr()->literal(1),
]);
$qb->execute();
$id2 = $qb->getLastInsertId();
$qb->insert('share')
->values([
'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
'uid_owner' => $qb->expr()->literal('user1'),
'uid_initiator' => $qb->expr()->literal('user1'),
'share_with' => $qb->expr()->literal('user2'),
'item_type' => $qb->expr()->literal('file'),
'file_source' => $qb->expr()->literal(1),
]);
$qb->execute();
$id3 = $qb->getLastInsertId();
$ownerPath1 = $this->createMock(File::class);
$shareOwner1Folder = $this->createMock(Folder::class);
$shareOwner1Folder->method('getFirstNodeById')->willReturn($ownerPath1);
$ownerPath2 = $this->createMock(File::class);
$shareOwner2Folder = $this->createMock(Folder::class);
$shareOwner2Folder->method('getFirstNodeById')->willReturn($ownerPath2);
$ownerPath3 = $this->createMock(File::class);
$shareOwner3Folder = $this->createMock(Folder::class);
$shareOwner3Folder->method('getFirstNodeById')->willReturn($ownerPath3);
$this->rootFolder
->method('getUserFolder')
->willReturnMap(
[
['shareOwner1', $shareOwner1Folder],
['shareOwner2', $shareOwner2Folder],
['shareOwner3', $shareOwner3Folder],
]
);
$node = $this->createMock(Node::class);
$node
->expects($this->once())
->method('getId')
->willReturn(1);
$shares = $this->provider->getSharesByPath($node);
$this->assertCount(3, $shares);
$this->assertEquals($id1, $shares[0]->getId());
$this->assertEquals(IShare::TYPE_USER, $shares[0]->getShareType());
$this->assertEquals($id2, $shares[1]->getId());
$this->assertEquals(IShare::TYPE_GROUP, $shares[1]->getShareType());
$this->assertEquals($id3, $shares[2]->getId());
$this->assertEquals(IShare::TYPE_LINK, $shares[2]->getShareType());
}
}

@ -23,7 +23,9 @@ use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Mount\IShareOwnerlessMount;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorage;
use OCP\HintException;
use OCP\IConfig;
@ -666,6 +668,24 @@ class ManagerTest extends \Test\TestCase {
}
public function testGetShareByIdNodeAccessible(): void {
$share = $this->createMock(IShare::class);
$share
->expects($this->once())
->method('getNode')
->willThrowException(new NotFoundException());
$this->defaultProvider
->expects($this->once())
->method('getShareById')
->with(42)
->willReturn($share);
$this->expectException(ShareNotFound::class);
$this->manager->getShareById('default:42');
}
public function testGetExpiredShareById(): void {
$this->expectException(\OCP\Share\Exceptions\ShareNotFound::class);
@ -2874,10 +2894,11 @@ class ManagerTest extends \Test\TestCase {
}
public function testGetSharesBy(): void {
$share = $this->manager->newShare();
$node = $this->createMock(Folder::class);
$share = $this->manager->newShare();
$share->setNode($node);
$this->defaultProvider->expects($this->once())
->method('getSharesBy')
->with(
@ -2895,6 +2916,31 @@ class ManagerTest extends \Test\TestCase {
$this->assertSame($share, $shares[0]);
}
public function testGetSharesByOwnerless(): void {
$mount = $this->createMock(IShareOwnerlessMount::class);
$node = $this->createMock(Folder::class);
$node
->expects($this->once())
->method('getMountPoint')
->willReturn($mount);
$share = $this->manager->newShare();
$share->setNode($node);
$share->setShareType(IShare::TYPE_USER);
$this->defaultProvider
->expects($this->once())
->method('getSharesByPath')
->with($this->equalTo($node))
->willReturn([$share]);
$shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
$this->assertCount(1, $shares);
$this->assertSame($share, $shares[0]);
}
/**
* Test to ensure we correctly remove expired link shares
*
@ -2904,6 +2950,8 @@ class ManagerTest extends \Test\TestCase {
* deleted (as they are evaluated). but share 8 should still be there.
*/
public function testGetSharesByExpiredLinkShares(): void {
$node = $this->createMock(File::class);
$manager = $this->createManagerMock()
->setMethods(['deleteShare'])
->getMock();
@ -2917,6 +2965,7 @@ class ManagerTest extends \Test\TestCase {
for ($i = 0; $i < 8; $i++) {
$share = $this->manager->newShare();
$share->setId($i);
$share->setNode($node);
$shares[] = $share;
}
@ -2937,8 +2986,6 @@ class ManagerTest extends \Test\TestCase {
$shares2[] = clone $shares[$i];
}
$node = $this->createMock(File::class);
/*
* Simulate the getSharesBy call.
*/
@ -3100,8 +3147,10 @@ class ManagerTest extends \Test\TestCase {
$date = new \DateTime();
$date->setTime(0, 0, 0);
$date->add(new \DateInterval('P2D'));
$node = $this->createMock(File::class);
$share = $this->manager->newShare();
$share->setExpirationDate($date);
$share->setNode($node);
$share->setShareOwner('owner');
$share->setSharedBy('sharedBy');
@ -3179,8 +3228,10 @@ class ManagerTest extends \Test\TestCase {
$date = new \DateTime();
$date->setTime(0, 0, 0);
$date->add(new \DateInterval('P2D'));
$node = $this->createMock(Folder::class);
$share = $this->manager->newShare();
$share->setExpirationDate($date);
$share->setNode($node);
$this->defaultProvider->expects($this->once())
->method('getShareByToken')
@ -4493,6 +4544,49 @@ class ManagerTest extends \Test\TestCase {
$this->assertSame($expects, $result);
}
public function testGetSharesInFolderOwnerless(): void {
$factory = new DummyFactory2($this->createMock(IServerContainer::class));
$manager = $this->createManager($factory);
$factory->setProvider($this->defaultProvider);
$extraProvider = $this->createMock(IShareProvider::class);
$factory->setSecondProvider($extraProvider);
$share1 = $this->createMock(IShare::class);
$share2 = $this->createMock(IShare::class);
$mount = $this->createMock(IShareOwnerlessMount::class);
$file = $this->createMock(File::class);
$file
->method('getId')
->willReturn(1);
$folder = $this->createMock(Folder::class);
$folder
->method('getMountPoint')
->willReturn($mount);
$folder
->method('getDirectoryListing')
->willReturn([$file]);
$this->defaultProvider
->method('getSharesByPath')
->with($file)
->willReturn([$share1]);
$extraProvider
->method('getSharesByPath')
->with($file)
->willReturn([$share2]);
$this->assertSame([
1 => [$share1, $share2],
], $manager->getSharesInFolder('user', $folder));
}
public function testGetAccessList(): void {
$factory = new DummyFactory2($this->createMock(IServerContainer::class));