Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib/Files
diff options
context:
space:
mode:
authorDaniel Calviño Sánchez <danxuliu@gmail.com>2018-07-18 15:06:57 +0300
committerDaniel Calviño Sánchez <danxuliu@gmail.com>2019-09-26 11:45:01 +0300
commitedc54ea5d57e6019a14b9b70b3065c62d6d8953e (patch)
tree929bc4cf215058d5238862d39a07799895f5b125 /lib/Files
parentb0daa126657df88e6a26fd437fbf565eba54ebdb (diff)
Add support for public shares to file rooms
Until now file rooms were available only to users with direct access to the file. Now file rooms are available to any user or guest too if the link is publicly shared (with a link share, for example). Public shares are identified by a share token instead of a file id, so a new endpoint, which is a counterpart of FilesController but for share tokens, was added. The file room, however, is still associated to the file id like before. When checking if a participant can join a room if the current user is a user without direct access to the file or a guest it is not even possible to know if the file id belongs to a publicly shared file. Due to this when the room is got for a share token the share token is stored in the session and then used in following requests when checking whether the participant can join a room or not. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Diffstat (limited to 'lib/Files')
-rw-r--r--lib/Files/Listener.php57
-rw-r--r--lib/Files/Util.php52
2 files changed, 93 insertions, 16 deletions
diff --git a/lib/Files/Listener.php b/lib/Files/Listener.php
index 332bed37f..0cac3a6fa 100644
--- a/lib/Files/Listener.php
+++ b/lib/Files/Listener.php
@@ -26,6 +26,7 @@ namespace OCA\Talk\Files;
use OCA\Talk\Exceptions\ParticipantNotFoundException;
use OCA\Talk\Exceptions\UnauthorizedException;
use OCA\Talk\Room;
+use OCA\Talk\TalkSession;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
@@ -34,11 +35,12 @@ use Symfony\Component\EventDispatcher\GenericEvent;
*
* The rooms for files are intended to give the users a way to talk about a
* specific shared file, for example, when collaboratively editing it. The room
- * is persistent and can be accessed simultaneously by any user with direct
+ * is persistent and can be accessed simultaneously by any user or guest if the
+ * file is publicly shared (link share, for example), or by any user with direct
* access (user, group, circle and room share, but not link share, for example)
* to that file (or to an ancestor). The room has no owner, although self joined
- * users become persistent participants automatically when they join until they
- * explicitly leave or no longer have access to the file.
+ * users with direct access become persistent participants automatically when
+ * they join until they explicitly leave or no longer have access to the file.
*
* These rooms are associated to a "file" object, and their custom behaviour is
* provided by calling the methods of this class as a response to different room
@@ -48,9 +50,13 @@ class Listener {
/** @var Util */
protected $util;
+ /** @var TalkSession */
+ protected $talkSession;
- public function __construct(Util $util) {
+ public function __construct(Util $util,
+ TalkSession $talkSession) {
$this->util = $util;
+ $this->talkSession = $talkSession;
}
public static function register(EventDispatcherInterface $dispatcher): void {
@@ -61,7 +67,7 @@ class Listener {
$listener = \OC::$server->query(self::class);
try {
- $listener->preventUsersWithoutDirectAccessToTheFileFromJoining($room, $event->getArgument('userId'));
+ $listener->preventUsersWithoutAccessToTheFileFromJoining($room, $event->getArgument('userId'));
$listener->addUserAsPersistentParticipant($room, $event->getArgument('userId'));
} catch (UnauthorizedException $e) {
$event->setArgument('cancel', true);
@@ -72,8 +78,11 @@ class Listener {
$listener = function(GenericEvent $event) {
/** @var Room $room */
$room = $event->getSubject();
+ /** @var self $listener */
+ $listener = \OC::$server->query(self::class);
+
try {
- self::preventGuestsFromJoining($room);
+ $listener->preventGuestsFromJoiningIfNotPubliclyAccessible($room);
} catch (UnauthorizedException $e) {
$event->setArgument('cancel', true);
}
@@ -82,8 +91,10 @@ class Listener {
}
/**
- * Prevents users from joining if they do not have direct access to the
- * file.
+ * Prevents users from joining if they do not have access to the file.
+ *
+ * A user has access to the file if the file is publicly accessible (through
+ * a link share, for example) or if the user has direct access to it.
*
* A user has direct access to a file if she received the file (or an
* ancestor) through a user, group, circle or room share (but not through a
@@ -95,16 +106,22 @@ class Listener {
* @param string $userId
* @throws UnauthorizedException
*/
- public function preventUsersWithoutDirectAccessToTheFileFromJoining(Room $room, string $userId): void {
+ public function preventUsersWithoutAccessToTheFileFromJoining(Room $room, string $userId): void {
if ($room->getObjectType() !== 'file') {
return;
}
- $share = $this->util->getAnyDirectShareOfFileAccessibleByUser($room->getObjectId(), $userId);
+ // If a guest can access the file then any user can too.
+ $shareToken = $this->talkSession->getFileShareTokenForRoom($room->getToken());
+ if ($shareToken && $this->util->canGuestAccessFile($shareToken)) {
+ return;
+ }
+
+ $share = $this->util->getAnyPublicShareOfFileOwnedByUserOrAnyDirectShareOfFileAccessibleByUser($room->getObjectId(), $userId);
if (!$share) {
$groupFolder = $this->util->getGroupFolderNode($room->getObjectId(), $userId);
if (!$groupFolder) {
- throw new UnauthorizedException('User does not have direct access to the file');
+ throw new UnauthorizedException('User does not have access to the file');
}
}
}
@@ -112,6 +129,9 @@ class Listener {
/**
* Add user as a persistent participant of a file room.
*
+ * Only users with direct access to the file are added as persistent
+ * participants of the room.
+ *
* This method should be called before a user joins a room, but only if the
* user should be able to join the room.
*
@@ -123,6 +143,10 @@ class Listener {
return;
}
+ if (!$this->util->getAnyPublicShareOfFileOwnedByUserOrAnyDirectShareOfFileAccessibleByUser($room->getObjectId(), $userId)) {
+ return;
+ }
+
try {
$room->getParticipant($userId);
} catch (ParticipantNotFoundException $e) {
@@ -131,19 +155,24 @@ class Listener {
}
/**
- * Prevents guests from joining the room.
+ * Prevents guests from joining the room if it is not publicly accessible.
*
* This method should be called before a guest joins a room.
*
* @param Room $room
* @throws UnauthorizedException
*/
- protected static function preventGuestsFromJoining(Room $room): void {
+ protected function preventGuestsFromJoiningIfNotPubliclyAccessible(Room $room): void {
if ($room->getObjectType() !== 'file') {
return;
}
- throw new UnauthorizedException('Guests are not allowed in rooms for files');
+ $shareToken = $this->talkSession->getFileShareTokenForRoom($room->getToken());
+ if ($shareToken && $this->util->canGuestAccessFile($shareToken)) {
+ return;
+ }
+
+ throw new UnauthorizedException('Guests are not allowed in this room');
}
}
diff --git a/lib/Files/Util.php b/lib/Files/Util.php
index 5658a1e7b..c3f45dabc 100644
--- a/lib/Files/Util.php
+++ b/lib/Files/Util.php
@@ -28,6 +28,7 @@ use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
+use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as IShareManager;
use OCP\Share\IShare;
@@ -67,8 +68,23 @@ class Util {
return \in_array($userId, $this->getUsersWithAccessFile($fileId), true);
}
+ public function canGuestAccessFile(string $shareToken): bool {
+ try {
+ $this->shareManager->getShareByToken($shareToken);
+ return true;
+ } catch (ShareNotFound $e) {
+ return false;
+ }
+ }
+
/**
- * Returns any share of the file that the user has direct access to.
+ * Returns any share of the file that is public and owned by the user, or
+ * that the user has direct access to.
+ *
+ * A public share is one accessible by any user, including guests, like a
+ * share by link. Note that only a share of the file itself is taken into
+ * account; if an ancestor folder is shared publicly that share will not be
+ * returned.
*
* A user has direct access to a share and, thus, to a file, if she received
* the file through a user, group, circle or room share (but not through a
@@ -82,7 +98,7 @@ class Util {
* @param string $userId
* @return IShare|null
*/
- public function getAnyDirectShareOfFileAccessibleByUser(string $fileId, string $userId): ?IShare {
+ public function getAnyPublicShareOfFileOwnedByUserOrAnyDirectShareOfFileAccessibleByUser(string $fileId, string $userId): ?IShare {
$userFolder = $this->rootFolder->getUserFolder($userId);
$nodes = $userFolder->getById($fileId);
if (empty($nodes)) {
@@ -93,6 +109,13 @@ class Util {
return $node->getType() === FileInfo::TYPE_FILE;
});
+ if (!empty($nodes)) {
+ $share = $this->getAnyPublicShareOfNodeOwnedByUser($nodes[0], $userId);
+ if ($share) {
+ return $share;
+ }
+ }
+
while (!empty($nodes)) {
$node = array_pop($nodes);
@@ -111,6 +134,31 @@ class Util {
}
/**
+ * Returns any public share of the node (like a link share) created by the
+ * user.
+ *
+ * @param Node $node
+ * @param string $userId
+ * @return IShare|null
+ */
+ private function getAnyPublicShareOfNodeOwnedByUser(Node $node, string $userId): ?IShare {
+ $reshares = false;
+ $limit = 1;
+
+ $shares = $this->shareManager->getSharesBy($userId, \OCP\Share::SHARE_TYPE_LINK, $node, $reshares, $limit);
+ if (\count($shares) > 0) {
+ return $shares[0];
+ }
+
+ $shares = $this->shareManager->getSharesBy($userId, \OCP\Share::SHARE_TYPE_EMAIL, $node, $reshares, $limit);
+ if (\count($shares) > 0) {
+ return $shares[0];
+ }
+
+ return null;
+ }
+
+ /**
* Returns any share of the node that the user has direct access to.
*
* @param Node $node