Propagate shares etag when group membership changed
This commit is contained in:
parent
191f1b2d49
commit
d546c5bb59
|
@ -27,6 +27,7 @@ namespace OCA\Files_Sharing\AppInfo;
|
|||
use OCA\Files_Sharing\Helper;
|
||||
use OCA\Files_Sharing\MountProvider;
|
||||
use OCA\Files_Sharing\Propagation\PropagationManager;
|
||||
use OCA\Files_Sharing\Propagation\GroupPropagationManager;
|
||||
use OCP\AppFramework\App;
|
||||
use OC\AppFramework\Utility\SimpleContainer;
|
||||
use OCA\Files_Sharing\Controllers\ExternalSharesController;
|
||||
|
@ -128,6 +129,16 @@ class Application extends App {
|
|||
);
|
||||
});
|
||||
|
||||
$container->registerService('GroupPropagationManager', function (IContainer $c) {
|
||||
/** @var \OCP\IServerContainer $server */
|
||||
$server = $c->query('ServerContainer');
|
||||
return new GroupPropagationManager(
|
||||
$server->getUserSession(),
|
||||
$server->getGroupManager(),
|
||||
$c->query('PropagationManager')
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
* Register capabilities
|
||||
*/
|
||||
|
@ -144,5 +155,7 @@ class Application extends App {
|
|||
public function setupPropagation() {
|
||||
$propagationManager = $this->getContainer()->query('PropagationManager');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'setup', $propagationManager, 'globalSetup');
|
||||
|
||||
$this->getContainer()->query('GroupPropagationManager')->globalSetup();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
/**
|
||||
* @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\Propagation;
|
||||
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\View;
|
||||
use OCP\IConfig;
|
||||
use OCP\IUserSession;
|
||||
use OCP\IGroup;
|
||||
use OCP\IUser;
|
||||
use OCP\IGroupManager;
|
||||
use OCA\Files_Sharing\Propagation\PropagationManager;
|
||||
|
||||
/**
|
||||
* Propagate changes on group changes
|
||||
*/
|
||||
class GroupPropagationManager {
|
||||
/**
|
||||
* @var \OCP\IUserSession
|
||||
*/
|
||||
private $userSession;
|
||||
|
||||
/**
|
||||
* @var \OCP\IGroupManager
|
||||
*/
|
||||
private $groupManager;
|
||||
|
||||
/**
|
||||
* @var PropagationManager
|
||||
*/
|
||||
private $propagationManager;
|
||||
|
||||
/**
|
||||
* Items shared with a given user.
|
||||
* Key is user id and value is an array of shares.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $userShares = [];
|
||||
|
||||
public function __construct(IUserSession $userSession, IGroupManager $groupManager, PropagationManager $propagationManager) {
|
||||
$this->userSession = $userSession;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->propagationManager = $propagationManager;
|
||||
}
|
||||
|
||||
public function onPreProcessUser(IGroup $group, IUser $targetUser) {
|
||||
$this->userShares[$targetUser->getUID()] = $this->getUserShares($targetUser->getUID());
|
||||
}
|
||||
|
||||
public function onPostAddUser(IGroup $group, IUser $targetUser) {
|
||||
$targetUserId = $targetUser->getUID();
|
||||
$sharesAfter = $this->getUserShares($targetUserId);
|
||||
|
||||
$this->propagateSharesDiff($targetUserId, $sharesAfter, $this->userShares[$targetUserId]);
|
||||
unset($this->userShares[$targetUserId]);
|
||||
}
|
||||
|
||||
public function onPostRemoveUser(IGroup $group, IUser $targetUser) {
|
||||
$targetUserId = $targetUser->getUID();
|
||||
$sharesAfter = $this->getUserShares($targetUserId);
|
||||
|
||||
$this->propagateSharesDiff($targetUserId, $this->userShares[$targetUserId], $sharesAfter);
|
||||
unset($this->userShares[$targetUserId]);
|
||||
}
|
||||
|
||||
private function getUserShares($targetUserId) {
|
||||
return \OCP\Share::getItemsSharedWithUser('file', $targetUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate etag for the shares that are in $shares1 but not in $shares2.
|
||||
*
|
||||
* @param string $targetUserId user id for which to propagate shares
|
||||
* @param array $shares1
|
||||
* @param array $shares2
|
||||
*/
|
||||
private function propagateSharesDiff($targetUserId, $shares1, $shares2) {
|
||||
$sharesToPropagate = array_udiff(
|
||||
$shares1,
|
||||
$shares2,
|
||||
function($share1, $share2) {
|
||||
return ($share2['id'] - $share1['id']);
|
||||
}
|
||||
);
|
||||
|
||||
\OC\Files\Filesystem::initMountPoints($targetUserId);
|
||||
$this->propagationManager->propagateSharesToUser($sharesToPropagate, $targetUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called from setupFS trough a hook
|
||||
*
|
||||
* Sets up listening to changes made to shares owned by the current user
|
||||
*/
|
||||
public function globalSetup() {
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->groupManager->listen('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']);
|
||||
$this->groupManager->listen('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']);
|
||||
$this->groupManager->listen('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']);
|
||||
$this->groupManager->listen('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
$this->groupManager->removeListener('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']);
|
||||
$this->groupManager->removeListener('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']);
|
||||
$this->groupManager->removeListener('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']);
|
||||
$this->groupManager->removeListener('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']);
|
||||
}
|
||||
}
|
|
@ -80,6 +80,21 @@ class PropagationManager {
|
|||
return $this->changePropagators[$user];
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates etag changes for the given shares to the given user
|
||||
*
|
||||
* @param array array of shares for which to trigger etag change
|
||||
* @param string $user
|
||||
*/
|
||||
public function propagateSharesToUser($shares, $user) {
|
||||
$changePropagator = $this->getChangePropagator($user);
|
||||
foreach ($shares as $share) {
|
||||
$changePropagator->addChange($share['file_target']);
|
||||
}
|
||||
$time = microtime(true);
|
||||
$changePropagator->propagateChanges(floor($time));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @return \OCA\Files_Sharing\Propagation\RecipientPropagator
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Robin Appelman <icewind@owncloud.com>
|
||||
* @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\Tests;
|
||||
|
||||
use OC\Files\View;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IGroup;
|
||||
use OCP\IUser;
|
||||
use OCP\Share;
|
||||
use OCA\Files_Sharing\Propagation\GroupPropagationManager;
|
||||
use OCA\Files_Sharing\Propagation\PropagationManager;
|
||||
|
||||
class GroupPropagationManagerTest extends TestCase {
|
||||
|
||||
/**
|
||||
* @var GroupPropagationManager
|
||||
*/
|
||||
private $groupPropagationManager;
|
||||
|
||||
/**
|
||||
* @var IGroupManager
|
||||
*/
|
||||
private $groupManager;
|
||||
|
||||
/**
|
||||
* @var PropagationManager
|
||||
*/
|
||||
private $propagationManager;
|
||||
|
||||
/**
|
||||
* @var IGroup
|
||||
*/
|
||||
private $recipientGroup;
|
||||
|
||||
/**
|
||||
* @var IUser
|
||||
*/
|
||||
private $recipientUser;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $fileInfo;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$user->method('getUID')->willReturn(self::TEST_FILES_SHARING_API_USER1);
|
||||
$userSession = $this->getMockBuilder('\OCP\IUserSession')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$userSession->method('getUser')->willReturn(selF::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$this->propagationManager = $this->getMockBuilder('OCA\Files_Sharing\Propagation\PropagationManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->groupManager = \OC::$server->getGroupManager();
|
||||
$this->groupPropagationManager = new GroupPropagationManager(
|
||||
$userSession,
|
||||
$this->groupManager,
|
||||
$this->propagationManager
|
||||
);
|
||||
$this->groupPropagationManager->globalSetup();
|
||||
|
||||
// since the sharing code is not mockable, we have to create a real folder
|
||||
$this->loginAsUser(self::TEST_FILES_SHARING_API_USER1);
|
||||
$view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
|
||||
$view1->mkdir('/folder');
|
||||
|
||||
$this->fileInfo = $view1->getFileInfo('/folder');
|
||||
|
||||
$this->recipientGroup = $this->groupManager->get(self::TEST_FILES_SHARING_API_GROUP1);
|
||||
$this->recipientUser = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER3);
|
||||
|
||||
Share::shareItem(
|
||||
'folder',
|
||||
$this->fileInfo['fileid'],
|
||||
Share::SHARE_TYPE_GROUP,
|
||||
$this->recipientGroup->getGID(),
|
||||
\OCP\Constants::PERMISSION_READ
|
||||
);
|
||||
|
||||
$this->loginAsUser($this->recipientUser->getUID());
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
$this->groupPropagationManager->tearDown();
|
||||
$this->recipientGroup->removeUser($this->recipientUser);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testPropagateWhenAddedToGroup() {
|
||||
$this->propagationManager->expects($this->once())
|
||||
->method('propagateSharesToUser')
|
||||
->with($this->callback(function($shares) {
|
||||
if (count($shares) !== 1) {
|
||||
return false;
|
||||
}
|
||||
$share = array_values($shares)[0];
|
||||
return $share['file_source'] === $this->fileInfo['fileid'] &&
|
||||
$share['share_with'] === $this->recipientGroup->getGID() &&
|
||||
$share['file_target'] === '/folder';
|
||||
}), $this->recipientUser->getUID());
|
||||
|
||||
$this->recipientGroup->addUser($this->recipientUser);
|
||||
}
|
||||
|
||||
public function testPropagateWhenRemovedFromGroup() {
|
||||
$this->recipientGroup->addUser($this->recipientUser);
|
||||
|
||||
$this->propagationManager->expects($this->once())
|
||||
->method('propagateSharesToUser')
|
||||
->with($this->callback(function($shares) {
|
||||
if (count($shares) !== 1) {
|
||||
return false;
|
||||
}
|
||||
$share = array_values($shares)[0];
|
||||
return $share['file_source'] === $this->fileInfo['fileid'] &&
|
||||
$share['share_with'] === $this->recipientGroup->getGID() &&
|
||||
$share['file_target'] === '/folder';
|
||||
}), $this->recipientUser->getUID());
|
||||
|
||||
$this->recipientGroup->removeUser($this->recipientUser);
|
||||
}
|
||||
|
||||
public function testPropagateWhenRemovedFromGroupWithSubdirTarget() {
|
||||
$this->recipientGroup->addUser($this->recipientUser);
|
||||
|
||||
// relogin to refresh mount points
|
||||
$this->loginAsUser($this->recipientUser->getUID());
|
||||
$recipientView = new View('/' . $this->recipientUser->getUID() . '/files');
|
||||
|
||||
$this->assertTrue($recipientView->mkdir('sub'));
|
||||
$this->assertTrue($recipientView->rename('folder', 'sub/folder'));
|
||||
|
||||
$this->propagationManager->expects($this->once())
|
||||
->method('propagateSharesToUser')
|
||||
->with($this->callback(function($shares) {
|
||||
if (count($shares) !== 1) {
|
||||
return false;
|
||||
}
|
||||
$share = array_values($shares)[0];
|
||||
return $share['file_source'] === $this->fileInfo['fileid'] &&
|
||||
$share['share_with'] === $this->recipientGroup->getGID() &&
|
||||
$share['file_target'] === '/sub/folder';
|
||||
}), $this->recipientUser->getUID());
|
||||
|
||||
$this->recipientGroup->removeUser($this->recipientUser);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue