Skip to content

Commit 1a44a26

Browse files
committed
feat(sharing): implement path-based mount provider changes in MountProviders
[skip ci] Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
1 parent ae0c613 commit 1a44a26

File tree

8 files changed

+441
-2
lines changed

8 files changed

+441
-2
lines changed

apps/federatedfilesharing/lib/FederatedShareProvider.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,45 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
748748
return $shares;
749749
}
750750

751+
public function getSharedWithByNodeIds(
752+
$userId,
753+
$shareType,
754+
$nodeIds,
755+
$limit,
756+
$offset
757+
) {
758+
/** @var IShare[] $shares */
759+
$shares = [];
760+
761+
//Get shares directly with this user
762+
$qb = $this->dbConnection->getQueryBuilder();
763+
$qb->select('*')
764+
->from('share');
765+
766+
// Order by id
767+
$qb->orderBy('id');
768+
769+
// Set limit and offset
770+
if ($limit !== -1) {
771+
$qb->setMaxResults($limit);
772+
}
773+
$qb->setFirstResult($offset);
774+
775+
$qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
776+
$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
777+
$qb->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($nodeIds, IQueryBuilder::PARAM_INT_ARRAY)));
778+
779+
$cursor = $qb->executeQuery();
780+
781+
while ($data = $cursor->fetch()) {
782+
$shares[] = $this->createShareObject($data);
783+
}
784+
$cursor->closeCursor();
785+
786+
787+
return $shares;
788+
}
789+
751790
/**
752791
* Get a share by token
753792
*

apps/files_sharing/lib/External/MountProvider.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@
77
*/
88
namespace OCA\Files_Sharing\External;
99

10+
use LogicException;
1011
use OCP\DB\QueryBuilder\IQueryBuilder;
1112
use OCP\Federation\ICloudIdManager;
1213
use OCP\Files\Config\IMountProvider;
14+
use OCP\Files\Config\IPartialMountProvider;
1315
use OCP\Files\Storage\IStorageFactory;
1416
use OCP\Http\Client\IClientService;
1517
use OCP\IDBConnection;
1618
use OCP\IUser;
1719
use OCP\Server;
1820
use OCP\Share\IShare;
21+
use function str_starts_with;
22+
use function strlen;
23+
use function substr;
1924

20-
class MountProvider implements IMountProvider {
25+
class MountProvider implements IMountProvider, IPartialMountProvider {
2126
public const STORAGE = '\OCA\Files_Sharing\External\Storage';
2227

2328
/**
@@ -67,4 +72,61 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
6772
$result->closeCursor();
6873
return $mounts;
6974
}
75+
76+
public function getMountsFromMountPoints(
77+
string $path,
78+
array $mountsInfo,
79+
array $mountsMetadata,
80+
IStorageFactory $loader,
81+
): array {
82+
if (empty($mountsInfo)) {
83+
return [];
84+
}
85+
86+
$uniqueMountOwnerIds = [];
87+
$uniqueRootIds = [];
88+
$user = null;
89+
foreach ($mountsInfo as $mountInfo) {
90+
// get a list of unique owner IDs root mount IDs
91+
$user ??= $mountInfo->getUser();
92+
$uniqueMountOwnerIds[$user->getUID()] ??= true;
93+
$uniqueRootIds[$mountInfo->getRootId()] ??= true;
94+
}
95+
$uniqueMountOwnerIds = array_keys($uniqueMountOwnerIds);
96+
$uniqueRootIds = array_keys($uniqueRootIds);
97+
98+
// make sure the MPs belong to the same user
99+
if (count($uniqueMountOwnerIds) !== 1) {
100+
// question: what kind of exception to throw in here?
101+
throw new LogicException();
102+
}
103+
104+
$mountOwnerId = $user->getUID();
105+
$pathPrefix = "/$mountOwnerId/files";
106+
$pathHashes = [];
107+
foreach ($mountsInfo as $mountInfo) {
108+
$mountPoint = rtrim($mountInfo->getMountPoint(), '/');
109+
if (str_starts_with($mountPoint, $pathPrefix)) {
110+
$pathHashes[] = md5(substr($mountPoint, strlen($pathPrefix)));
111+
}
112+
}
113+
114+
$qb = $this->connection->getQueryBuilder();
115+
$qb->select('remote', 'share_token', 'password', 'mountpoint', 'owner')
116+
->from('share_external')
117+
->where($qb->expr()->eq('user', $qb->createNamedParameter($user->getUID())))
118+
->andWhere($qb->expr()->in('mountpoint_hash',
119+
$qb->createNamedParameter($pathHashes, IQueryBuilder::PARAM_STR_ARRAY)))
120+
->andWhere($qb->expr()->eq('accepted', $qb->createNamedParameter
121+
(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)));
122+
$result = $qb->executeQuery();
123+
$mounts = [];
124+
while ($row = $result->fetch()) {
125+
$row['manager'] = $this;
126+
$row['token'] = $row['share_token'];
127+
$mounts[] = $this->getMount($user, $row, $loader);
128+
}
129+
$result->closeCursor();
130+
return $mounts;
131+
}
70132
}

apps/files_sharing/lib/MountProvider.php

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,55 @@
99

1010
use Exception;
1111
use InvalidArgumentException;
12+
use LogicException;
1213
use OC\Files\View;
1314
use OCA\Files_Sharing\Event\ShareMountedEvent;
15+
use OCA\Talk\Share\RoomShareProvider;
1416
use OCP\Cache\CappedMemoryCache;
17+
use OCP\DB\QueryBuilder\IQueryBuilder;
1518
use OCP\EventDispatcher\IEventDispatcher;
1619
use OCP\Files\Config\IMountProvider;
20+
use OCP\Files\Config\IPartialMountProvider;
21+
use OCP\Files\IRootFolder;
1722
use OCP\Files\Mount\IMountManager;
1823
use OCP\Files\Mount\IMountPoint;
1924
use OCP\Files\Storage\IStorageFactory;
2025
use OCP\ICacheFactory;
2126
use OCP\IConfig;
27+
use OCP\IDBConnection;
2228
use OCP\IUser;
2329
use OCP\Share\IAttributes;
2430
use OCP\Share\IManager;
2531
use OCP\Share\IShare;
2632
use Psr\Log\LoggerInterface;
33+
use function array_filter;
34+
use function array_values;
2735
use function count;
2836

29-
class MountProvider implements IMountProvider {
37+
class MountProvider implements IMountProvider, IPartialMountProvider {
38+
39+
private const SUPPORTED_SHARE_TYPES = [
40+
IShare::TYPE_USER,
41+
IShare::TYPE_GROUP,
42+
IShare::TYPE_USERGROUP,
43+
RoomShareProvider::SHARE_TYPE_USERROOM,
44+
IShare::TYPE_CIRCLE,
45+
IShare::TYPE_ROOM,
46+
IShare::TYPE_DECK,
47+
IShare::TYPE_SCIENCEMESH,
48+
];
49+
50+
/**
51+
* Maps internal share types to the right provider
52+
*
53+
* NOTE: this information should come either from the provider or from
54+
* the provider factory!
55+
*/
56+
private const TYPE_MAPPING = [
57+
IShare::TYPE_USERGROUP => IShare::TYPE_GROUP,
58+
RoomShareProvider::SHARE_TYPE_USERROOM => IShare::TYPE_ROOM,
59+
];
60+
3061
/**
3162
* @param IConfig $config
3263
* @param IManager $shareManager
@@ -39,6 +70,8 @@ public function __construct(
3970
protected IEventDispatcher $eventDispatcher,
4071
protected ICacheFactory $cacheFactory,
4172
protected IMountManager $mountManager,
73+
protected IDBConnection $dbConn,
74+
protected IRootFolder $rootFolder,
4275
) {
4376
}
4477

@@ -244,6 +277,66 @@ public function adjustTarget(
244277
);
245278
}
246279
}
280+
281+
/**
282+
* @inheritdoc
283+
*/
284+
public function getMountsFromMountPoints(
285+
string $path,
286+
array $mountsInfo,
287+
array $mountsMetadata,
288+
IStorageFactory $loader,
289+
): array {
290+
/**
291+
* If path and count of mount info match, we can use the getsharedwith
292+
* If we get a path passed which doesn't match the MP in the infos we
293+
* get, it means we are loading data for a collection.
294+
*/
295+
$uniqueMountOwnerIds = [];
296+
$uniqueRootIds = [];
297+
$user = null;
298+
foreach ($mountsInfo as $mountInfo) {
299+
// get a list of unique owner IDs root mount IDs
300+
$user ??= $mountInfo->getUser();
301+
$uniqueMountOwnerIds[$user->getUID()] ??= true;
302+
$uniqueRootIds[$mountInfo->getRootId()] ??= true;
303+
}
304+
$uniqueMountOwnerIds = array_keys($uniqueMountOwnerIds);
305+
$uniqueRootIds = array_keys($uniqueRootIds);
306+
307+
// make sure the MPs belong to the same user
308+
if (count($uniqueMountOwnerIds) !== 1) {
309+
// question: what kind of exception to throw in here?
310+
throw new LogicException();
311+
}
312+
313+
$rootIdsByShareProviderType = $this->getShareInfo($user, $uniqueRootIds);
314+
$sharesInPath = [];
315+
foreach ($rootIdsByShareProviderType as $shareType => $rootIds) {
316+
// todo: pagination for many rootIds
317+
$sharesInPath[] = $this->shareManager->getSharedWithByNodes(
318+
$uniqueMountOwnerIds[0],
319+
$shareType,
320+
$rootIds,
321+
-1
322+
);
323+
}
324+
$sharesInPath = array_merge(...$sharesInPath);
325+
326+
// filter out shares owned or shared by the user and ones for which
327+
// the user has no permissions
328+
$shares = $this->filterShares($sharesInPath, $user->getUID());
329+
$superShares = $this->buildSuperShares($shares, $user);
330+
331+
return $this->getMountsFromSuperShares(
332+
$user->getUID(),
333+
$superShares,
334+
$loader,
335+
$user,
336+
false,
337+
);
338+
}
339+
247340
/**
248341
* @param string $userId
249342
* @param array $superShares
@@ -354,4 +447,59 @@ static function (IShare $share) use ($userId) {
354447
}
355448
);
356449
}
450+
451+
/**
452+
* Helper function to retrieve data needed to determine which
453+
* IShareProviders need to be queried: IShare::TYPE_*.
454+
*
455+
* @see IShare::TYPE_* constants
456+
*
457+
* @param int[] $uniqueRootIds
458+
* @return array<int, int[]> Array of the shared node IDs,
459+
* keyed by the share type.
460+
* @throws \OCP\DB\Exception
461+
*/
462+
public function getShareInfo(IUser $user, array $uniqueRootIds): array {
463+
$mountOwnerId = $user->getUID();
464+
// retrieve the share type for the received files
465+
$qb = $this->dbConn->getQueryBuilder();
466+
$qb->select('file_source', 'share_type')
467+
->from('share')
468+
->where(
469+
$qb->expr()->in(
470+
'file_source',
471+
$qb->createNamedParameter(
472+
$uniqueRootIds,
473+
IQueryBuilder::PARAM_STR_ARRAY
474+
)
475+
)
476+
)
477+
->andWhere(
478+
$qb->expr()->in(
479+
'share_type',
480+
$qb->createNamedParameter(
481+
self::SUPPORTED_SHARE_TYPES,
482+
IQueryBuilder::PARAM_INT_ARRAY
483+
)
484+
)
485+
)
486+
->andWhere(
487+
$qb->expr()->eq(
488+
'share_with',
489+
$qb->createNamedParameter($mountOwnerId)
490+
)
491+
)
492+
->groupBy('file_source', 'share_type');
493+
$cursor = $qb->executeQuery();
494+
495+
// group IDs of the roots of the mountpoints by type
496+
$rootIdsByType = [];
497+
while ($row = $cursor->fetch()) {
498+
$mappedType = self::TYPE_MAPPING[$row['share_type']] ?? $row['share_type'];
499+
$rootIdsByType[$mappedType][] = $row['file_source'];
500+
}
501+
$cursor->closeCursor();
502+
503+
return $rootIdsByType;
504+
}
357505
}

apps/sharebymail/lib/ShareByMailProvider.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,39 @@ public function getSharedWith($userId, $shareType, $node, $limit, $offset): arra
944944
return $shares;
945945
}
946946

947+
public function getSharedWithByNodeIds($userId, $shareType, $nodeIds, $limit, $offset): array {
948+
/** @var IShare[] $shares */
949+
$shares = [];
950+
951+
//Get shares directly with this user
952+
$qb = $this->dbConnection->getQueryBuilder();
953+
$qb->select('*')
954+
->from('share');
955+
956+
// Order by id
957+
$qb->orderBy('id');
958+
959+
// Set limit and offset
960+
if ($limit !== -1) {
961+
$qb->setMaxResults($limit);
962+
}
963+
$qb->setFirstResult($offset);
964+
965+
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)));
966+
$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
967+
$qb->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter ($nodeIds, IQueryBuilder::PARAM_INT_ARRAY)));
968+
969+
$cursor = $qb->executeQuery();
970+
971+
while ($data = $cursor->fetch()) {
972+
$shares[] = $this->createShareObject($data);
973+
}
974+
$cursor->closeCursor();
975+
976+
977+
return $shares;
978+
}
979+
947980
/**
948981
* Get a share by token
949982
*

0 commit comments

Comments
 (0)