diff options
author | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-04 12:28:19 +0200 |
---|---|---|
committer | Roeland Jago Douma <rullzer@owncloud.com> | 2016-04-04 14:15:38 +0200 |
commit | e0cee43cf0a3899567f50a5fb03d867fc2f0327a (patch) | |
tree | 5f0d5503b3d971e0b813bc14fd7956cf114abbfe | |
parent | f6cea3c9c436142110504ba76320d57ca7899b27 (diff) | |
download | nextcloud-server-e0cee43cf0a3899567f50a5fb03d867fc2f0327a.tar.gz nextcloud-server-e0cee43cf0a3899567f50a5fb03d867fc2f0327a.zip |
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
-rw-r--r-- | apps/federatedfilesharing/lib/federatedshareprovider.php | 17 | ||||
-rw-r--r-- | apps/federatedfilesharing/tests/federatedshareprovidertest.php | 48 | ||||
-rw-r--r-- | lib/base.php | 2 | ||||
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 65 | ||||
-rw-r--r-- | lib/private/Share20/Hooks.php | 27 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 12 | ||||
-rw-r--r-- | lib/private/share/hooks.php | 18 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 11 | ||||
-rw-r--r-- | lib/public/Share/IShareProvider.php | 10 | ||||
-rw-r--r-- | tests/lib/share20/defaultshareprovidertest.php | 127 |
10 files changed, 317 insertions, 20 deletions
diff --git a/apps/federatedfilesharing/lib/federatedshareprovider.php b/apps/federatedfilesharing/lib/federatedshareprovider.php index e54ce08fb04..a450b420cf4 100644 --- a/apps/federatedfilesharing/lib/federatedshareprovider.php +++ b/apps/federatedfilesharing/lib/federatedshareprovider.php @@ -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(); + } } diff --git a/apps/federatedfilesharing/tests/federatedshareprovidertest.php b/apps/federatedfilesharing/tests/federatedshareprovidertest.php index dedd6767b49..92e9f10c3cd 100644 --- a/apps/federatedfilesharing/tests/federatedshareprovidertest.php +++ b/apps/federatedfilesharing/tests/federatedshareprovidertest.php @@ -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); + } } diff --git a/lib/base.php b/lib/base.php index f3076a1181a..d457d1ed485 100644 --- a/lib/base.php +++ b/lib/base.php @@ -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'); diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index f6171f87992..370655f8315 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -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(); + } } diff --git a/lib/private/Share20/Hooks.php b/lib/private/Share20/Hooks.php new file mode 100644 index 00000000000..93669974b28 --- /dev/null +++ b/lib/private/Share20/Hooks.php @@ -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']); + } +} diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 1c9d4d82277..6c665f7e133 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -1019,6 +1019,18 @@ class Manager implements IManager { } /** + * @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. * diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php index 750486ba80f..dae273eefb8 100644 --- a/lib/private/share/hooks.php +++ b/lib/private/share/hooks.php @@ -33,24 +33,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 * @param array $arguments diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 64e5b554de9..e3780ac070c 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -150,6 +150,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. * diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index d00b9da7b59..e201ba81ebc 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -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); } diff --git a/tests/lib/share20/defaultshareprovidertest.php b/tests/lib/share20/defaultshareprovidertest.php index 8d8ae8d4809..d8b594519b6 100644 --- a/tests/lib/share20/defaultshareprovidertest.php +++ b/tests/lib/share20/defaultshareprovidertest.php @@ -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); + } } |