From 17b141337a606bcd9246cf69f22aa80f44d2d69c Mon Sep 17 00:00:00 2001
From: Vincent Petry <pvince81@owncloud.com>
Date: Tue, 3 Mar 2015 17:29:12 +0100
Subject: [PATCH] Delete orphaned shares in a background job

---
 apps/files_sharing/appinfo/app.php            |   2 +
 .../lib/deleteorphanedsharesjob.php           |  59 ++++++++
 .../tests/deleteorphanedsharesjobtest.php     | 142 ++++++++++++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 apps/files_sharing/lib/deleteorphanedsharesjob.php
 create mode 100644 apps/files_sharing/tests/deleteorphanedsharesjobtest.php

diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index a222973fc3..a0448f7a6a 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -51,6 +51,8 @@ OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
 OCP\Util::addScript('files_sharing', 'share');
 OCP\Util::addScript('files_sharing', 'external');
 
+\OC::$server->getJobList()->add('OCA\Files_sharing\Lib\DeleteOrphanedSharesJob');
+
 \OC::$server->getActivityManager()->registerExtension(function() {
 		return new \OCA\Files_Sharing\Activity(
 			\OC::$server->query('L10NFactory'),
diff --git a/apps/files_sharing/lib/deleteorphanedsharesjob.php b/apps/files_sharing/lib/deleteorphanedsharesjob.php
new file mode 100644
index 0000000000..f39078b778
--- /dev/null
+++ b/apps/files_sharing/lib/deleteorphanedsharesjob.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_sharing\Lib;
+
+use Doctrine\DBAL\Platforms\SqlitePlatform;
+use OCP\IDBConnection;
+use OC\BackgroundJob\TimedJob;
+
+/**
+ * Delete all share entries that have no matching entries in the file cache table.
+ */
+class DeleteOrphanedSharesJob extends TimedJob {
+
+	/**
+	 * Default interval in minutes
+	 *
+	 * @var int $defaultIntervalMin
+	 **/
+	protected $defaultIntervalMin = 15;
+
+	/**
+	 * Makes the background job do its work
+	 *
+	 * @param array $argument unused argument
+	 */
+	public function run($argument) {
+		$connection = \OC::$server->getDatabaseConnection();
+		$logger = \OC::$server->getLogger();
+
+		$sql =
+			'DELETE FROM `*PREFIX*share` ' .
+			'WHERE `item_type` in (\'file\', \'folder\') ' .
+			'AND NOT EXISTS (SELECT `fileid` FROM `*PREFIX*filecache` WHERE `file_source` = `fileid`)';
+
+		$deletedEntries = $connection->executeUpdate($sql);
+		$logger->info("$deletedEntries orphaned share(s) deleted", ['app' => 'DeleteOrphanedSharesJob']);
+	}
+
+}
diff --git a/apps/files_sharing/tests/deleteorphanedsharesjobtest.php b/apps/files_sharing/tests/deleteorphanedsharesjobtest.php
new file mode 100644
index 0000000000..ec9d5f089b
--- /dev/null
+++ b/apps/files_sharing/tests/deleteorphanedsharesjobtest.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\BackgroundJob;
+
+use OCA\Files_sharing\Lib\DeleteOrphanedSharesJob;
+
+class DeleteOrphanedSharesJobTest extends \Test\TestCase {
+
+	/**
+	 * @var DeleteOrphanedSharesJob
+	 */
+	private $job;
+
+	/**
+	 * @var \OCP\IDBConnection
+	 */
+	private $connection;
+
+	/**
+	 * @var string
+	 */
+	private $user1;
+
+	/**
+	 * @var string
+	 */
+	private $user2;
+
+	protected function setup() {
+		parent::setUp();
+
+		$this->connection = \OC::$server->getDatabaseConnection();
+
+		$this->user1 = $this->getUniqueID('user1_');
+		$this->user2 = $this->getUniqueID('user2_');
+
+		$userManager = \OC::$server->getUserManager();
+		$userManager->createUser($this->user1, 'pass');
+		$userManager->createUser($this->user2, 'pass');
+
+		\OC::registerShareHooks();
+
+		$this->job = new DeleteOrphanedSharesJob();
+	}
+
+	protected function tearDown() {
+		$this->connection->executeUpdate('DELETE FROM `*PREFIX*share` WHERE `item_type` in (\'file\', \'folder\')');
+
+		$userManager = \OC::$server->getUserManager();
+		$user1 = $userManager->get($this->user1);
+		if($user1) {
+			$user1->delete();
+		}
+		$user2 = $userManager->get($this->user2);
+		if($user2) {
+			$user2->delete();
+		}
+
+		$this->logout();
+
+		parent::tearDown();
+	}
+
+	private function getShares() {
+		$shares = [];
+		$result = $this->connection->executeQuery('SELECT * FROM `*PREFIX*share`');
+		while ($row = $result->fetch()) {
+			$shares[] = $row;
+		}
+		$result->closeCursor();
+		return $shares;
+	}
+
+	/**
+	 * Test clearing orphaned shares
+	 */
+	public function testClearShares() {
+		$this->loginAsUser($this->user1);
+
+		$view = new \OC\Files\View('/' . $this->user1 . '/');
+		$view->mkdir('files/test');
+		$view->mkdir('files/test/sub');
+
+		$fileInfo = $view->getFileInfo('files/test/sub');
+		$fileId = $fileInfo->getId();
+
+		$this->assertTrue(
+			\OCP\Share::shareItem('folder', $fileId, \OCP\Share::SHARE_TYPE_USER, $this->user2, \OCP\Constants::PERMISSION_READ),
+			'Failed asserting that user 1 successfully shared "test/sub" with user 2.'
+		);
+
+		$this->assertCount(1, $this->getShares());
+
+		$this->job->run([]);
+
+		$this->assertCount(1, $this->getShares(), 'Linked shares not deleted');
+
+		$view->unlink('files/test');
+
+		$this->job->run([]);
+
+		$this->assertCount(0, $this->getShares(), 'Orphaned shares deleted');
+	}
+
+	public function testKeepNonFileShares() {
+		$this->loginAsUser($this->user1);
+
+		\OCP\Share::registerBackend('test', 'Test_Share_Backend');
+
+		$this->assertTrue(
+			\OCP\Share::shareItem('test', 'test.txt', \OCP\Share::SHARE_TYPE_USER, $this->user2, \OCP\Constants::PERMISSION_READ),
+			'Failed asserting that user 1 successfully shared something with user 2.'
+		);
+
+		$this->assertCount(1, $this->getShares());
+
+		$this->job->run([]);
+
+		$this->assertCount(1, $this->getShares(), 'Non-file shares kept');
+	}
+}
+
-- 
GitLab