aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2023-05-26 17:23:29 +0200
committerRobin Appelman <robin@icewind.nl>2023-05-26 17:23:29 +0200
commit6748cc62c97beeda946fbeb1d42d7bb532a33e9f (patch)
tree91b346eeccaecd3a4a30e533244b909ff7b58ce3 /apps
parentb6d734375478c791a146e2ec5ad61ac1a699e436 (diff)
downloadnextcloud-server-share-list-set-owner.tar.gz
nextcloud-server-share-list-set-owner.zip
add commands to list shares and set share ownershare-list-set-owner
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'apps')
-rw-r--r--apps/files_sharing/appinfo/info.xml2
-rw-r--r--apps/files_sharing/composer/composer/autoload_classmap.php2
-rw-r--r--apps/files_sharing/composer/composer/autoload_static.php2
-rw-r--r--apps/files_sharing/lib/Command/ListCommand.php197
-rw-r--r--apps/files_sharing/lib/Command/SetOwner.php127
5 files changed, 330 insertions, 0 deletions
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml
index 72a2ed7c138..b193d58e72b 100644
--- a/apps/files_sharing/appinfo/info.xml
+++ b/apps/files_sharing/appinfo/info.xml
@@ -42,6 +42,8 @@ Turning the feature off removes shared files and folders on the server for all s
<commands>
<command>OCA\Files_Sharing\Command\CleanupRemoteStorages</command>
<command>OCA\Files_Sharing\Command\ExiprationNotification</command>
+ <command>OCA\Files_Sharing\Command\ListCommand</command>
+ <command>OCA\Files_Sharing\Command\SetOwner</command>
</commands>
<settings>
diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php
index 50cbfe40d8a..a9c9c95e5ea 100644
--- a/apps/files_sharing/composer/composer/autoload_classmap.php
+++ b/apps/files_sharing/composer/composer/autoload_classmap.php
@@ -25,6 +25,8 @@ return array(
'OCA\\Files_Sharing\\Collaboration\\ShareRecipientSorter' => $baseDir . '/../lib/Collaboration/ShareRecipientSorter.php',
'OCA\\Files_Sharing\\Command\\CleanupRemoteStorages' => $baseDir . '/../lib/Command/CleanupRemoteStorages.php',
'OCA\\Files_Sharing\\Command\\ExiprationNotification' => $baseDir . '/../lib/Command/ExiprationNotification.php',
+ 'OCA\\Files_Sharing\\Command\\ListCommand' => $baseDir . '/../lib/Command/ListCommand.php',
+ 'OCA\\Files_Sharing\\Command\\SetOwner' => $baseDir . '/../lib/Command/SetOwner.php',
'OCA\\Files_Sharing\\Controller\\AcceptController' => $baseDir . '/../lib/Controller/AcceptController.php',
'OCA\\Files_Sharing\\Controller\\DeletedShareAPIController' => $baseDir . '/../lib/Controller/DeletedShareAPIController.php',
'OCA\\Files_Sharing\\Controller\\ExternalSharesController' => $baseDir . '/../lib/Controller/ExternalSharesController.php',
diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php
index 4ba0fd52421..c8243f93955 100644
--- a/apps/files_sharing/composer/composer/autoload_static.php
+++ b/apps/files_sharing/composer/composer/autoload_static.php
@@ -40,6 +40,8 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Collaboration\\ShareRecipientSorter' => __DIR__ . '/..' . '/../lib/Collaboration/ShareRecipientSorter.php',
'OCA\\Files_Sharing\\Command\\CleanupRemoteStorages' => __DIR__ . '/..' . '/../lib/Command/CleanupRemoteStorages.php',
'OCA\\Files_Sharing\\Command\\ExiprationNotification' => __DIR__ . '/..' . '/../lib/Command/ExiprationNotification.php',
+ 'OCA\\Files_Sharing\\Command\\ListCommand' => __DIR__ . '/..' . '/../lib/Command/ListCommand.php',
+ 'OCA\\Files_Sharing\\Command\\SetOwner' => __DIR__ . '/..' . '/../lib/Command/SetOwner.php',
'OCA\\Files_Sharing\\Controller\\AcceptController' => __DIR__ . '/..' . '/../lib/Controller/AcceptController.php',
'OCA\\Files_Sharing\\Controller\\DeletedShareAPIController' => __DIR__ . '/..' . '/../lib/Controller/DeletedShareAPIController.php',
'OCA\\Files_Sharing\\Controller\\ExternalSharesController' => __DIR__ . '/..' . '/../lib/Controller/ExternalSharesController.php',
diff --git a/apps/files_sharing/lib/Command/ListCommand.php b/apps/files_sharing/lib/Command/ListCommand.php
new file mode 100644
index 00000000000..a94159b2a40
--- /dev/null
+++ b/apps/files_sharing/lib/Command/ListCommand.php
@@ -0,0 +1,197 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl>
+ *
+ * @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\Command;
+
+use OC\Core\Command\Base;
+use OCP\IUserManager;
+use OCP\Share\IManager;
+use OCP\Share\IShare;
+use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListCommand extends Base {
+ private IManager $shareManager;
+
+ public function __construct(
+ IManager $shareManager
+ ) {
+ $this->shareManager = $shareManager;
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('sharing:list')
+ ->setDescription('List shares')
+ ->addOption('owner', null, InputOption::VALUE_REQUIRED, "Limit shares by share owner")
+ ->addOption('shared-by', null, InputOption::VALUE_REQUIRED, "Limit shares by share initiator")
+ ->addOption('shared-with', null, InputOption::VALUE_REQUIRED, "Limit shares by share recipient")
+ ->addOption('share-type', null, InputOption::VALUE_REQUIRED, "Limit shares by share recipient")
+ ->addOption('file-id', null, InputOption::VALUE_REQUIRED, "Limit shares to a specific file or folder id");
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int {
+ $ownerInput = $input->getOption('owner');
+ $sharedByInput = $input->getOption('shared-by');
+ $sharedWithInput = $input->getOption('shared-with');
+ $shareTypeInput = $input->getOption('share-type');
+ $fileInput = $input->getOption('file-id');
+
+ if ($shareTypeInput) {
+ $shareType = $this->parseShareType($shareTypeInput);
+ if ($shareType === null) {
+ $output->writeln("<error>Unknown share type $shareTypeInput</error>");
+ $output->writeln("possible values: <info>user</info>, <info>group</info>, <info>link</info>, " .
+ "<info>email</info>, <info>remote</info>, <info>circle</info>, <info>guest</info>, <info>remote_group</info>, " .
+ "<info>room</info>, <info>deck</info>, <info>deck_user</info>, <info>science-mesh</info>");
+ return 1;
+ }
+ } else {
+ $shareTypeInput = null;
+ }
+
+ $allShares = $this->shareManager->getAllShares();
+
+ $filteredShares = new \CallbackFilterIterator($allShares, function(IShare $share) use ($ownerInput, $shareType, $sharedByInput, $sharedWithInput, $fileInput) {
+ return $this->filterShare($share, $ownerInput, $sharedByInput, $sharedWithInput, $shareType, $fileInput);
+ });
+
+ $shareData = [];
+ foreach ($filteredShares as $share) {
+ /** @var IShare $share */
+ $shareData[] = [
+ 'share-id' => $share->getFullId(),
+ 'share-owner' => $share->getShareOwner(),
+ 'shared-by' => $share->getSharedBy(),
+ 'shared-with' => $share->getSharedWith(),
+ 'share-type' => $this->formatShareType($share->getShareType()),
+ 'file-id' => $share->getNodeId(),
+ ];
+ }
+
+ $outputFormat = $input->getOption('output');
+ if ($outputFormat === self::OUTPUT_FORMAT_JSON || $outputFormat === self::OUTPUT_FORMAT_JSON_PRETTY) {
+ $this->writeArrayInOutputFormat($input, $output, $shareData);
+ } else {
+ $table = new Table($output);
+ $table
+ ->setHeaders(['share-id', 'share-owner', 'shared-by', 'shared-with', 'share-type', 'file-id'])
+ ->setRows($shareData);
+ $table->render();
+ }
+
+ return 0;
+ }
+
+ private function filterShare(
+ IShare $share,
+ string $ownerInput = null,
+ string $sharedByInput = null,
+ string $sharedWithInput = null,
+ int $shareType = null,
+ int $fileInput = null
+ ): bool {
+ if ($ownerInput && $share->getShareOwner() !== $ownerInput) {
+ return false;
+ }
+ if ($sharedByInput && $share->getSharedBy() !== $sharedByInput) {
+ return false;
+ }
+ if ($sharedWithInput && $share->getSharedWith() !== $sharedWithInput) {
+ return false;
+ }
+ if ($shareType && $share->getShareType() !== $shareType) {
+ return false;
+ }
+ if ($fileInput && $share->getNodeId() !== $fileInput) {
+ return false;
+ }
+ return true;
+ }
+
+ private function parseShareType(string $type): ?int {
+ switch ($type) {
+ case 'user':
+ return IShare::TYPE_USER;
+ case 'group':
+ return IShare::TYPE_GROUP;
+ case 'link':
+ return IShare::TYPE_LINK;
+ case 'email':
+ return IShare::TYPE_EMAIL;
+ case 'remote':
+ return IShare::TYPE_REMOTE;
+ case 'circle':
+ return IShare::TYPE_CIRCLE;
+ case 'guest':
+ return IShare::TYPE_GUEST;
+ case 'remote_group':
+ return IShare::TYPE_REMOTE_GROUP;
+ case 'room':
+ return IShare::TYPE_ROOM;
+ case 'deck':
+ return IShare::TYPE_DECK;
+ case 'deck_user':
+ return IShare::TYPE_DECK_USER;
+ case 'science-mesh':
+ return IShare::TYPE_SCIENCEMESH;
+ default:
+ return null;
+ }
+ }
+
+ private function formatShareType(int $type): string {
+ switch ($type) {
+ case IShare::TYPE_USER:
+ return 'user';
+ case IShare::TYPE_GROUP:
+ return 'group';
+ case IShare::TYPE_LINK:
+ return 'link';
+ case IShare::TYPE_EMAIL:
+ return 'email';
+ case IShare::TYPE_REMOTE:
+ return 'remote';
+ case IShare::TYPE_CIRCLE:
+ return 'circle';
+ case IShare::TYPE_GUEST:
+ return 'guest';
+ case IShare::TYPE_REMOTE_GROUP:
+ return 'remote_group';
+ case IShare::TYPE_ROOM:
+ return 'room';
+ case IShare::TYPE_DECK:
+ return 'deck';
+ case IShare::TYPE_DECK_USER:
+ return 'deck_user';
+ case IShare::TYPE_SCIENCEMESH:
+ return 'science-mesh';
+ default:
+ return 'other';
+ }
+ }
+}
diff --git a/apps/files_sharing/lib/Command/SetOwner.php b/apps/files_sharing/lib/Command/SetOwner.php
new file mode 100644
index 00000000000..05ba039a0b8
--- /dev/null
+++ b/apps/files_sharing/lib/Command/SetOwner.php
@@ -0,0 +1,127 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl>
+ *
+ * @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\Command;
+
+use OC\Core\Command\Info\FileUtils;
+use OCA\Files_Sharing\SharedMount;
+use OCA\Files_Sharing\SharedStorage;
+use OCP\Files\File;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\IUserManager;
+use OCP\Share\IManager;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+
+class SetOwner extends Command {
+ private FileUtils $fileUtils;
+ private IRootFolder $rootFolder;
+ private IManager $shareManager;
+ private IUserManager $userManager;
+
+ public function __construct(
+ FileUtils $fileUtils,
+ IRootFolder $rootFolder,
+ IManager $shareManager,
+ IUserManager $userManager
+ ) {
+ $this->fileUtils = $fileUtils;
+ $this->rootFolder = $rootFolder;
+ $this->shareManager = $shareManager;
+ $this->userManager = $userManager;
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('sharing:set-owner')
+ ->setDescription('Change the owner of a share, note that the new owner must already have access to the file')
+ ->addArgument('share-id', InputArgument::REQUIRED, "Id of the share to set the owner of")
+ ->addArgument('new-owner', InputArgument::REQUIRED, "User id of to set the owner to");
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int {
+ $shareId = $input->getArgument('share-id');
+ $targetId = $input->getArgument('new-owner');
+
+ $target = $this->userManager->get($targetId);
+ if (!$target) {
+ $output->writeln("<error>User $targetId not found</error>");
+ return 1;
+ }
+
+ $share = $this->shareManager->getShareById($shareId);
+ if (!$share) {
+ $output->writeln("<error>Share $shareId not found</error>");
+ return 1;
+ }
+
+ $sourceFile = $share->getNode();
+ $usersWithAccessToSource = $this->fileUtils->getFilesByUser($sourceFile);
+
+ $targetHasNonSharedAccess = false;
+ $targetHasSharedAccess = false;
+ $fileOrFolder = ($sourceFile instanceof File) ? "file" : "folder";;
+ $sourceName = $sourceFile->getName();
+ $targetNode = null;
+
+ if (isset($usersWithAccessToSource[$target->getUID()])) {
+ $targetSourceNodes = $usersWithAccessToSource[$target->getUID()];
+ foreach ($targetSourceNodes as $targetSourceNode) {
+ $targetNode = $targetSourceNode;
+ $mount = $targetSourceNode->getMountPoint();
+ if ($mount instanceof SharedMount) {
+ if ($mount->getShare()->getId() === $share->getId()) {
+ $targetHasSharedAccess = true;
+ continue;
+ }
+ }
+ $targetHasNonSharedAccess = true;
+ }
+ }
+
+ if (!$targetHasSharedAccess && !$targetHasNonSharedAccess) {
+ $output->writeln("<error>$targetId has no access to the $fileOrFolder $sourceName shared by $shareId</error>");
+ return 1;
+ }
+
+ if (!$targetHasNonSharedAccess && $targetHasSharedAccess) {
+ $output->writeln("<error>Target user $targetId only has access to the shared $fileOrFolder $sourceName through the share $shareId.</error>");
+ return 1;
+ }
+
+ $share->setNode($targetNode);
+ $share->setShareOwner($target->getUID());
+ $share->setSharedBy($target->getUID());
+
+ $this->shareManager->updateShare($share);
+
+ return 0;
+ }
+
+}