Inherited Shares
Signed-off-by: Maxence Lange <maxence@artificial-owl.com> working on users with resharing rights Signed-off-by: Maxence Lange <maxence@artificial-owl.com> -getLogger() Signed-off-by: Maxence Lange <maxence@artificial-owl.com> cleaning Signed-off-by: Maxence Lange <maxence@artificial-owl.com> fix type Signed-off-by: Maxence Lange <maxence@artificial-owl.com> Update SharingRightsException.php
This commit is contained in:
parent
1e91b6a716
commit
5794f14df9
|
@ -53,6 +53,11 @@ return [
|
|||
'url' => '/api/v1/shares',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'ShareAPI#getInheritedShares',
|
||||
'url' => '/api/v1/shares/inherited',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'ShareAPI#createShare',
|
||||
'url' => '/api/v1/shares',
|
||||
|
|
|
@ -34,6 +34,7 @@ return array(
|
|||
'OCA\\Files_Sharing\\DeleteOrphanedSharesJob' => $baseDir . '/../lib/DeleteOrphanedSharesJob.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\BrokenPath' => $baseDir . '/../lib/Exceptions/BrokenPath.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\S2SException' => $baseDir . '/../lib/Exceptions/S2SException.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\SharingRightsException' => $baseDir . '/../lib/Exceptions/SharingRightsException.php',
|
||||
'OCA\\Files_Sharing\\ExpireSharesJob' => $baseDir . '/../lib/ExpireSharesJob.php',
|
||||
'OCA\\Files_Sharing\\External\\Cache' => $baseDir . '/../lib/External/Cache.php',
|
||||
'OCA\\Files_Sharing\\External\\Manager' => $baseDir . '/../lib/External/Manager.php',
|
||||
|
|
|
@ -49,6 +49,7 @@ class ComposerStaticInitFiles_Sharing
|
|||
'OCA\\Files_Sharing\\DeleteOrphanedSharesJob' => __DIR__ . '/..' . '/../lib/DeleteOrphanedSharesJob.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\BrokenPath' => __DIR__ . '/..' . '/../lib/Exceptions/BrokenPath.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\S2SException' => __DIR__ . '/..' . '/../lib/Exceptions/S2SException.php',
|
||||
'OCA\\Files_Sharing\\Exceptions\\SharingRightsException' => __DIR__ . '/..' . '/../lib/Exceptions/SharingRightsException.php',
|
||||
'OCA\\Files_Sharing\\ExpireSharesJob' => __DIR__ . '/..' . '/../lib/ExpireSharesJob.php',
|
||||
'OCA\\Files_Sharing\\External\\Cache' => __DIR__ . '/..' . '/../lib/External/Cache.php',
|
||||
'OCA\\Files_Sharing\\External\\Manager' => __DIR__ . '/..' . '/../lib/External/Manager.php',
|
||||
|
|
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
* @author Vincent Petry <pvince81@owncloud.com>
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
* @author Maxence Lange <maxence@artificial-owl.com>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
|
@ -31,6 +32,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Files_Sharing\Controller;
|
||||
|
||||
use OCA\Files_Sharing\Exceptions\SharingRightsException;
|
||||
use OCA\Files_Sharing\External\Storage;
|
||||
use OCA\Files\Helper;
|
||||
use OCA\Files_Sharing\External\Storage;
|
||||
use OCP\App\IAppManager;
|
||||
|
@ -42,6 +45,7 @@ use OCP\AppFramework\OCS\OCSNotFoundException;
|
|||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\InvalidPathException;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
|
@ -383,7 +387,7 @@ class ShareAPIController extends OCSController {
|
|||
* @throws OCSException
|
||||
* @throws OCSForbiddenException
|
||||
* @throws OCSNotFoundException
|
||||
* @throws \OCP\Files\InvalidPathException
|
||||
* @throws InvalidPathException
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
public function createShare(
|
||||
|
@ -579,11 +583,12 @@ class ShareAPIController extends OCSController {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param \OCP\Files\File|\OCP\Files\Folder $node
|
||||
* @param null|Node $node
|
||||
* @param boolean $includeTags
|
||||
* @return DataResponse
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSharedWithMe($node = null, bool $includeTags): DataResponse {
|
||||
private function getSharedWithMe($node, bool $includeTags): array {
|
||||
|
||||
$userShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_USER, $node, -1, 0);
|
||||
$groupShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_GROUP, $node, -1, 0);
|
||||
|
@ -592,7 +597,7 @@ class ShareAPIController extends OCSController {
|
|||
|
||||
$shares = array_merge($userShares, $groupShares, $circleShares, $roomShares);
|
||||
|
||||
$shares = array_filter($shares, function (IShare $share) {
|
||||
$shares = array_filter($shares, function(IShare $share) {
|
||||
return $share->getShareOwner() !== $this->currentUser;
|
||||
});
|
||||
|
||||
|
@ -611,13 +616,15 @@ class ShareAPIController extends OCSController {
|
|||
$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
|
||||
}
|
||||
|
||||
return new DataResponse($formatted);
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \OCP\Files\Folder $folder
|
||||
* @param \OCP\Files\Node $folder
|
||||
*
|
||||
* @return array
|
||||
* @throws OCSBadRequestException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
private function getSharesInDir(Node $folder): array {
|
||||
if (!($folder instanceof \OCP\Files\Folder)) {
|
||||
|
@ -638,15 +645,35 @@ class ShareAPIController extends OCSController {
|
|||
if (in_array($share->getId(), $known)) {
|
||||
return false;
|
||||
}
|
||||
$known[] = $share->getId();
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
$format = $this->formatShare($share);
|
||||
|
||||
$known[] = $share->getId();
|
||||
$formatted[] = $format;
|
||||
if ($share->getSharedBy() === $this->currentUser) {
|
||||
$miniFormatted[] = $format;
|
||||
}
|
||||
if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $folder)) {
|
||||
$resharingRight = true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
//Ignore this share
|
||||
}
|
||||
}
|
||||
|
||||
if (!$resharingRight) {
|
||||
$formatted = $miniFormatted;
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* The getShares function.
|
||||
*
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $shared_with_me
|
||||
* @param string $reshares
|
||||
|
@ -659,64 +686,82 @@ class ShareAPIController extends OCSController {
|
|||
* - Get shares for a specific path (?path=...)
|
||||
* - Get all shares in a folder (?subfiles=true&path=..)
|
||||
*
|
||||
* @param string $include_tags
|
||||
*
|
||||
* @return DataResponse
|
||||
* @throws NotFoundException
|
||||
* @throws OCSBadRequestException
|
||||
* @throws OCSNotFoundException
|
||||
*/
|
||||
public function getShares(
|
||||
string $shared_with_me = 'false',
|
||||
string $reshares = 'false',
|
||||
string $subfiles = 'false',
|
||||
string $path = null,
|
||||
string $path = '',
|
||||
string $include_tags = 'false'
|
||||
): DataResponse {
|
||||
|
||||
if ($path !== null) {
|
||||
$node = null;
|
||||
if ($path !== '') {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
|
||||
try {
|
||||
$path = $userFolder->get($path);
|
||||
$this->lock($path);
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
|
||||
$node = $userFolder->get($path);
|
||||
$this->lock($node);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new OCSNotFoundException(
|
||||
$this->l->t('Wrong path, file/folder doesn\'t exist')
|
||||
);
|
||||
} catch (LockedException $e) {
|
||||
throw new OCSNotFoundException($this->l->t('Could not lock path'));
|
||||
throw new OCSNotFoundException($this->l->t('Could not lock node'));
|
||||
}
|
||||
}
|
||||
|
||||
$include_tags = $include_tags === 'true';
|
||||
$shares = $this->getFormattedShares(
|
||||
$this->currentUser,
|
||||
$node,
|
||||
($shared_with_me === 'true'),
|
||||
($reshares === 'true'),
|
||||
($subfiles === 'true'),
|
||||
($include_tags === 'true')
|
||||
);
|
||||
|
||||
if ($shared_with_me === 'true') {
|
||||
$result = $this->getSharedWithMe($path, $include_tags);
|
||||
return $result;
|
||||
return new DataResponse($shares);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $viewer
|
||||
* @param Node $node
|
||||
* @param bool $sharedWithMe
|
||||
* @param bool $reShares
|
||||
* @param bool $subFiles
|
||||
* @param bool $includeTags
|
||||
*
|
||||
* @return array
|
||||
* @throws NotFoundException
|
||||
* @throws OCSBadRequestException
|
||||
*/
|
||||
private function getFormattedShares(
|
||||
string $viewer, $node = null, bool $sharedWithMe = false, bool $reShares = false,
|
||||
bool $subFiles = false, bool $includeTags = false
|
||||
): array {
|
||||
|
||||
if ($sharedWithMe) {
|
||||
return $this->getSharedWithMe($node, $includeTags);
|
||||
}
|
||||
|
||||
if ($reshares === 'true') {
|
||||
$reshares = true;
|
||||
} else {
|
||||
$reshares = false;
|
||||
if ($subFiles) {
|
||||
return $this->getSharesInDir($node);
|
||||
}
|
||||
|
||||
if ($subfiles === 'true') {
|
||||
$shares = $this->getSharesInDir($path);
|
||||
$recipientNode = null;
|
||||
} else {
|
||||
// get all shares
|
||||
$shares = $this->getAllShares($path, $reshares);
|
||||
$recipientNode = $path;
|
||||
}
|
||||
$shares = $this->getSharesFromNode($viewer, $node, $reShares);
|
||||
|
||||
// process all shares
|
||||
$formatted = $miniFormatted = [];
|
||||
$resharingRight = false;
|
||||
foreach ($shares as $share) {
|
||||
/** @var IShare $share */
|
||||
|
||||
// do not list the shares of the current user
|
||||
if ($share->getSharedWith() === $this->currentUser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$format = $this->formatShare($share, $recipientNode);
|
||||
/** @var IShare $share */
|
||||
$format = $this->formatShare($share, $node);
|
||||
$formatted[] = $format;
|
||||
|
||||
// let's also build a list of shares created
|
||||
|
@ -731,8 +776,7 @@ class ShareAPIController extends OCSController {
|
|||
if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $path)) {
|
||||
$resharingRight = true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
//Ignore share
|
||||
} catch (InvalidPathException | NotFoundException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,13 +784,89 @@ class ShareAPIController extends OCSController {
|
|||
$formatted = $miniFormatted;
|
||||
}
|
||||
|
||||
if ($include_tags) {
|
||||
$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
|
||||
if ($includeTags) {
|
||||
$formatted =
|
||||
Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
|
||||
}
|
||||
|
||||
return new DataResponse($formatted);
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The getInheritedShares function.
|
||||
* returns all shares relative to a file, including parent folders shares rights.
|
||||
*
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* - Get shares by the current user
|
||||
* - Get shares by the current user and reshares (?reshares=true)
|
||||
* - Get shares with the current user (?shared_with_me=true)
|
||||
* - Get shares for a specific path (?path=...)
|
||||
* - Get all shares in a folder (?subfiles=true&path=..)
|
||||
*
|
||||
* @return DataResponse
|
||||
* @throws InvalidPathException
|
||||
* @throws NotFoundException
|
||||
* @throws OCSNotFoundException
|
||||
* @throws OCSBadRequestException
|
||||
* @throws SharingRightsException
|
||||
*/
|
||||
public function getInheritedShares(string $path): DataResponse {
|
||||
|
||||
// get Node from (string) path.
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
|
||||
try {
|
||||
$node = $userFolder->get($path);
|
||||
$this->lock($node);
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
|
||||
} catch (LockedException $e) {
|
||||
throw new OCSNotFoundException($this->l->t('Could not lock path'));
|
||||
}
|
||||
|
||||
// current User has resharing rights ?
|
||||
$this->confirmSharingRights($node);
|
||||
|
||||
// initiate real owner.
|
||||
$owner = $node->getOwner()
|
||||
->getUID();
|
||||
if (!$this->userManager->userExists($owner)) {
|
||||
return new DataResponse([]);
|
||||
}
|
||||
|
||||
// get node based on the owner, fix owner in case of external storage
|
||||
$userFolder = $this->rootFolder->getUserFolder($owner);
|
||||
if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
|
||||
$owner = $node->getOwner()
|
||||
->getUID();
|
||||
$userFolder = $this->rootFolder->getUserFolder($owner);
|
||||
$nodes = $userFolder->getById($node->getId());
|
||||
$node = array_shift($nodes);
|
||||
}
|
||||
$basePath = $userFolder->getPath();
|
||||
|
||||
// generate node list for each parent folders
|
||||
/** @var Node[] $nodes */
|
||||
$nodes = [];
|
||||
while ($node->getPath() !== $basePath) {
|
||||
$nodes[] = $node;
|
||||
$node = $node->getParent();
|
||||
}
|
||||
|
||||
// for each nodes, retrieve shares.
|
||||
$shares = [];
|
||||
foreach ($nodes as $node) {
|
||||
$getShares = $this->getFormattedShares($owner, $node, false, true);
|
||||
$this->mergeFormattedShares($shares, $getShares);
|
||||
}
|
||||
|
||||
return new DataResponse(array_values($shares));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
|
@ -1261,6 +1381,90 @@ class ShareAPIController extends OCSController {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $viewer
|
||||
* @param Node $node
|
||||
* @param bool $reShares
|
||||
*
|
||||
* @return IShare[]
|
||||
*/
|
||||
private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
|
||||
|
||||
$providers = [
|
||||
Share::SHARE_TYPE_USER,
|
||||
Share::SHARE_TYPE_GROUP,
|
||||
Share::SHARE_TYPE_LINK,
|
||||
Share::SHARE_TYPE_EMAIL,
|
||||
Share::SHARE_TYPE_EMAIL,
|
||||
Share::SHARE_TYPE_CIRCLE,
|
||||
Share::SHARE_TYPE_ROOM
|
||||
];
|
||||
|
||||
// Should we assume that the (currentUser) viewer is the owner of the node !?
|
||||
$shares = [];
|
||||
foreach ($providers as $provider) {
|
||||
if (!$this->shareManager->shareProviderExists($provider)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$providerShares =
|
||||
$this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
|
||||
$shares = array_merge($shares, $providerShares);
|
||||
}
|
||||
|
||||
if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
|
||||
$federatedShares = $this->shareManager->getSharesBy(
|
||||
$this->currentUser, Share::SHARE_TYPE_REMOTE, $node, $reShares, -1, 0
|
||||
);
|
||||
$shares = array_merge($shares, $federatedShares);
|
||||
}
|
||||
|
||||
if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
|
||||
$federatedShares = $this->shareManager->getSharesBy(
|
||||
$this->currentUser, Share::SHARE_TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
|
||||
);
|
||||
$shares = array_merge($shares, $federatedShares);
|
||||
}
|
||||
|
||||
return $shares;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
*
|
||||
* @throws SharingRightsException
|
||||
*/
|
||||
private function confirmSharingRights(Node $node): void {
|
||||
if (!$this->hasResharingRights($this->currentUser, $node)) {
|
||||
throw new SharingRightsException('no sharing rights on this item');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $viewer
|
||||
* @param Node $node
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasResharingRights($viewer, $node): bool {
|
||||
foreach ([$node, $node->getParent()] as $node) {
|
||||
$shares = $this->getSharesFromNode($viewer, $node, true);
|
||||
foreach ($shares as $share) {
|
||||
try {
|
||||
if ($this->shareProviderResharingRights($viewer, $share, $node)) {
|
||||
return true;
|
||||
}
|
||||
} catch (InvalidPathException | NotFoundException $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if we can find resharing rights in an IShare object for a specific user.
|
||||
*
|
||||
|
@ -1269,9 +1473,10 @@ class ShareAPIController extends OCSController {
|
|||
* @param string $userId
|
||||
* @param IShare $share
|
||||
* @param Node $node
|
||||
*
|
||||
* @return bool
|
||||
* @throws NotFoundException
|
||||
* @throws \OCP\Files\InvalidPathException
|
||||
* @throws InvalidPathException
|
||||
*/
|
||||
private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
|
||||
|
||||
|
@ -1280,7 +1485,7 @@ class ShareAPIController extends OCSController {
|
|||
}
|
||||
|
||||
// we check that current user have parent resharing rights on the current file
|
||||
if ($node !== null && ($node->getPermissions() & \OCP\Constants::PERMISSION_SHARE) !== 0) {
|
||||
if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1357,4 +1562,21 @@ class ShareAPIController extends OCSController {
|
|||
return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $federatedShares, $federatedGroupShares);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* merging already formatted shares.
|
||||
* We'll make an associative array to easily detect duplicate Ids.
|
||||
* Keys _needs_ to be removed after all shares are retrieved and merged.
|
||||
*
|
||||
* @param array $shares
|
||||
* @param array $newShares
|
||||
*/
|
||||
private function mergeFormattedShares(array &$shares, array $newShares) {
|
||||
foreach ($newShares as $newShare) {
|
||||
if (!array_key_exists($newShare['id'], $shares)) {
|
||||
$shares[$newShare['id']] = $newShare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, Maxence Lange <maxence@artificial-owl.com>
|
||||
*
|
||||
* @author Maxence Lange <maxence@artificial-owl.com>
|
||||
*
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Sharing\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
|
||||
/**
|
||||
* Sharing and Resharing rights.
|
||||
*
|
||||
* Class SharingRightsException
|
||||
*
|
||||
* @package OCA\Files_Sharing\Exceptions
|
||||
*/
|
||||
class SharingRightsException extends Exception {
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue