Migrate post_userDelete hook to share manager

This makes the post_userDelete hook call the sharemanager. This will
cleanup to and from this user.

* All shares owned by this user
* All shares with this user (user)
* All custom group shares
* All link share initiated by this user (to avoid invisible link shares)

Unit tests are added for the defaultshare provider as well as the
federated share provider
This commit is contained in:
Roeland Jago Douma 2016-04-04 12:28:19 +02:00
parent f6cea3c9c4
commit e0cee43cf0
10 changed files with 317 additions and 20 deletions

View file

@ -563,4 +563,21 @@ class FederatedShareProvider implements IShareProvider {
return $nodes[0];
}
/**
* A user is deleted from the system
* So clean up the relevant shares.
*
* @param string $uid
* @param int $shareType
*/
public function userDeleted($uid, $shareType) {
//TODO: probabaly a good idea to send unshare info to remote servers
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
->execute();
}
}

View file

@ -462,4 +462,52 @@ class FederatedShareProviderTest extends TestCase {
$this->assertCount(1, $shares);
$this->assertEquals('user2@server.com', $shares[0]->getSharedWith());
}
public function dataDeleteUser() {
return [
['a', 'b', 'c', 'a', true],
['a', 'b', 'c', 'b', false],
// The recipient is non local.
['a', 'b', 'c', 'c', false],
['a', 'b', 'c', 'd', false],
];
}
/**
* @dataProvider dataDeleteUser
*
* @param string $owner The owner of the share (uid)
* @param string $initiator The initiator of the share (uid)
* @param string $recipient The recipient of the share (uid/gid/pass)
* @param string $deletedUser The user that is deleted
* @param bool $rowDeleted Is the row deleted in this setup
*/
public function testDeleteUser($owner, $initiator, $recipient, $deletedUser, $rowDeleted) {
$qb = $this->connection->getQueryBuilder();
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
->setValue('uid_owner', $qb->createNamedParameter($owner))
->setValue('uid_initiator', $qb->createNamedParameter($initiator))
->setValue('share_with', $qb->createNamedParameter($recipient))
->setValue('item_type', $qb->createNamedParameter('file'))
->setValue('item_source', $qb->createNamedParameter(42))
->setValue('file_source', $qb->createNamedParameter(42))
->execute();
$id = $qb->getLastInsertId();
$this->provider->userDeleted($deletedUser, \OCP\Share::SHARE_TYPE_REMOTE);
$qb = $this->connection->getQueryBuilder();
$qb->select('*')
->from('share')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id))
);
$cursor = $qb->execute();
$data = $cursor->fetchAll();
$cursor->closeCursor();
$this->assertCount($rowDeleted ? 0 : 1, $data);
}
}

View file

@ -776,7 +776,7 @@ class OC {
*/
public static function registerShareHooks() {
if (\OC::$server->getSystemConfig()->getValue('installed')) {
OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share\Hooks', 'post_deleteUser');
OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share20\Hooks', 'post_deleteUser');
OC_Hook::connect('OC_User', 'post_addToGroup', 'OC\Share\Hooks', 'post_addToGroup');
OC_Hook::connect('OC_Group', 'pre_addToGroup', 'OC\Share\Hooks', 'pre_addToGroup');
OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share\Hooks', 'post_removeFromGroup');

View file

@ -28,7 +28,6 @@ use OC\Share20\Exception\ProviderException;
use OCP\Share\Exceptions\ShareNotFound;
use OC\Share20\Exception\BackendError;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\NotFoundException;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUserManager;
@ -804,4 +803,68 @@ class DefaultShareProvider implements IShareProvider {
return $share;
}
/**
* A user is deleted from the system
* So clean up the relevant shares.
*
* @param string $uid
* @param int $shareType
*/
public function userDeleted($uid, $shareType) {
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('share');
if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
/*
* Delete all user shares that are owned by this user
* or that are received by this user
*/
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
)
);
} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
/*
* Delete all group shares that are owned by this user
* Or special user group shares that are received by this user
*/
$qb->where(
$qb->expr()->andX(
$qb->expr()->orX(
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)),
$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))
),
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))
)
);
$qb->orWhere(
$qb->expr()->andX(
$qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)),
$qb->expr()->eq('share_with', $qb->createNamedParameter($uid))
)
);
} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
/*
* Delete all link shares owned by this user.
* And all link shares initiated by this user (until #22327 is in)
*/
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)));
$qb->andWhere(
$qb->expr()->orX(
$qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)),
$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($uid))
)
);
}
$qb->execute();
}
}

View file

@ -0,0 +1,27 @@
<?php
/**
* @author Roeland Jago Douma <rullzer@owncloud.com>
*
* @copyright Copyright (c) 2016, 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 OC\Share20;
class Hooks {
public static function post_deleteUser($arguments) {
\OC::$server->getShareManager()->userDeleted($arguments['uid']);
}
}

View file

@ -1018,6 +1018,18 @@ class Manager implements IManager {
return true;
}
/**
* @inheritdoc
*/
public function userDeleted($uid) {
$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE];
foreach ($types as $type) {
$provider = $this->factory->getProviderForType($type);
$provider->userDeleted($uid, $type);
}
}
/**
* Get access list to a path. This means
* all the users and groups that can access a given path.

View file

@ -32,24 +32,6 @@ class Hooks extends \OC\Share\Constants {
*/
private static $updateTargets = array();
/**
* Function that is called after a user is deleted. Cleans up the shares of that user.
* @param array $arguments
*/
public static function post_deleteUser($arguments) {
// Delete any items shared with the deleted user
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`'
.' WHERE `share_with` = ? AND (`share_type` = ? OR `share_type` = ?)');
$query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
// Delete any items the deleted user shared
$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?');
$result = $query->execute(array($arguments['uid']));
while ($item = $result->fetchRow()) {
Helper::delete($item['id']);
}
}
/**
* Function that is called before a user is added to a group.
* check if we need to create a unique target for the user

View file

@ -149,6 +149,17 @@ interface IManager {
*/
public function checkPassword(IShare $share, $password);
/**
* The user with UID is deleted.
* All share providers have to cleanup the shares with this user as well
* as shares owned by this user.
* Shares only initiated by this user are fine.
*
* @param string $uid
* @since 9.1.0
*/
public function userDeleted($uid);
/**
* Instantiates a new share object. This is to be passed to
* createShare.

View file

@ -146,4 +146,14 @@ interface IShareProvider {
* @since 9.0.0
*/
public function getShareByToken($token);
/**
* A user is deleted from the system
* So clean up the relevant shares.
*
* @param string $uid
* @param int $shareType
* @since 9.1.0
*/
public function userDeleted($uid, $shareType);
}

View file

@ -1872,4 +1872,131 @@ class DefaultShareProviderTest extends \Test\TestCase {
$share = $this->provider->getShareById($id, 'user0');
$this->assertSame('/ultraNewTarget', $share->getTarget());
}
public function dataDeleteUser() {
return [
[\OCP\Share::SHARE_TYPE_USER, 'a', 'b', 'c', 'a', true],
[\OCP\Share::SHARE_TYPE_USER, 'a', 'b', 'c', 'b', false],
[\OCP\Share::SHARE_TYPE_USER, 'a', 'b', 'c', 'c', true],
[\OCP\Share::SHARE_TYPE_USER, 'a', 'b', 'c', 'd', false],
[\OCP\Share::SHARE_TYPE_GROUP, 'a', 'b', 'c', 'a', true],
[\OCP\Share::SHARE_TYPE_GROUP, 'a', 'b', 'c', 'b', false],
// The group c is still valid but user c is deleted so group share stays
[\OCP\Share::SHARE_TYPE_GROUP, 'a', 'b', 'c', 'c', false],
[\OCP\Share::SHARE_TYPE_GROUP, 'a', 'b', 'c', 'd', false],
[\OCP\Share::SHARE_TYPE_LINK, 'a', 'b', 'c', 'a', true],
// To avoid invisible link shares delete initiated link shares as well (see #22327)
[\OCP\Share::SHARE_TYPE_LINK, 'a', 'b', 'c', 'b', true],
[\OCP\Share::SHARE_TYPE_LINK, 'a', 'b', 'c', 'c', false],
[\OCP\Share::SHARE_TYPE_LINK, 'a', 'b', 'c', 'd', false],
];
}
/**
* @dataProvider dataDeleteUser
*
* @param int $type The shareType (user/group/link)
* @param string $owner The owner of the share (uid)
* @param string $initiator The initiator of the share (uid)
* @param string $recipient The recipient of the share (uid/gid/pass)
* @param string $deletedUser The user that is deleted
* @param bool $rowDeleted Is the row deleted in this setup
*/
public function testDeleteUser($type, $owner, $initiator, $recipient, $deletedUser, $rowDeleted) {
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter($type))
->setValue('uid_owner', $qb->createNamedParameter($owner))
->setValue('uid_initiator', $qb->createNamedParameter($initiator))
->setValue('share_with', $qb->createNamedParameter($recipient))
->setValue('item_type', $qb->createNamedParameter('file'))
->setValue('item_source', $qb->createNamedParameter(42))
->setValue('file_source', $qb->createNamedParameter(42))
->execute();
$id = $qb->getLastInsertId();
$this->provider->userDeleted($deletedUser, $type);
$qb = $this->dbConn->getQueryBuilder();
$qb->select('*')
->from('share')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id))
);
$cursor = $qb->execute();
$data = $cursor->fetchAll();
$cursor->closeCursor();
$this->assertCount($rowDeleted ? 0 : 1, $data);
}
public function dataDeleteUserGroup() {
return [
['a', 'b', 'c', 'a', true, true],
['a', 'b', 'c', 'b', false, false],
['a', 'b', 'c', 'c', false, true],
['a', 'b', 'c', 'd', false, false],
];
}
/**
* @dataProvider dataDeleteUserGroup
*
* @param string $owner The owner of the share (uid)
* @param string $initiator The initiator of the share (uid)
* @param string $recipient The recipient of the usergroup share (uid)
* @param string $deletedUser The user that is deleted
* @param bool $groupShareDeleted
* @param bool $userGroupShareDeleted
*/
public function testDeleteUserGroup($owner, $initiator, $recipient, $deletedUser, $groupShareDeleted, $userGroupShareDeleted) {
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
->setValue('uid_owner', $qb->createNamedParameter($owner))
->setValue('uid_initiator', $qb->createNamedParameter($initiator))
->setValue('share_with', $qb->createNamedParameter('group'))
->setValue('item_type', $qb->createNamedParameter('file'))
->setValue('item_source', $qb->createNamedParameter(42))
->setValue('file_source', $qb->createNamedParameter(42))
->execute();
$groupId = $qb->getLastInsertId();
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
->setValue('share_type', $qb->createNamedParameter(2))
->setValue('uid_owner', $qb->createNamedParameter($owner))
->setValue('uid_initiator', $qb->createNamedParameter($initiator))
->setValue('share_with', $qb->createNamedParameter($recipient))
->setValue('item_type', $qb->createNamedParameter('file'))
->setValue('item_source', $qb->createNamedParameter(42))
->setValue('file_source', $qb->createNamedParameter(42))
->execute();
$userGroupId = $qb->getLastInsertId();
$this->provider->userDeleted($deletedUser, \OCP\Share::SHARE_TYPE_GROUP);
$qb = $this->dbConn->getQueryBuilder();
$qb->select('*')
->from('share')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($userGroupId))
);
$cursor = $qb->execute();
$data = $cursor->fetchAll();
$cursor->closeCursor();
$this->assertCount($userGroupShareDeleted ? 0 : 1, $data);
$qb = $this->dbConn->getQueryBuilder();
$qb->select('*')
->from('share')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($groupId))
);
$cursor = $qb->execute();
$data = $cursor->fetchAll();
$cursor->closeCursor();
$this->assertCount($groupShareDeleted ? 0 : 1, $data);
}
}