parent
ca2fd30073
commit
e5a7e3124a
@ -0,0 +1,117 @@ |
||||
<?php |
||||
/** |
||||
* @author Joas Schilling <nickvergessen@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 OC\Repair; |
||||
|
||||
|
||||
use OC\Hooks\BasicEmitter; |
||||
use OC\RepairStep; |
||||
use OCP\IDBConnection; |
||||
use OCP\IGroupManager; |
||||
use OCP\Share; |
||||
|
||||
class OldGroupMembershipShares extends BasicEmitter implements RepairStep { |
||||
|
||||
/** @var \OCP\IDBConnection */ |
||||
protected $connection; |
||||
|
||||
/** @var \OCP\IGroupManager */ |
||||
protected $groupManager; |
||||
|
||||
/** |
||||
* @var array [gid => [uid => (bool)]] |
||||
*/ |
||||
protected $memberships; |
||||
|
||||
/** |
||||
* @param IDBConnection $connection |
||||
* @param IGroupManager $groupManager |
||||
*/ |
||||
public function __construct(IDBConnection $connection, IGroupManager $groupManager) { |
||||
$this->connection = $connection; |
||||
$this->groupManager = $groupManager; |
||||
} |
||||
|
||||
/** |
||||
* Returns the step's name |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getName() { |
||||
return 'Remove shares of old group memberships'; |
||||
} |
||||
|
||||
/** |
||||
* Run repair step. |
||||
* Must throw exception on error. |
||||
* |
||||
* @throws \Exception in case of failure |
||||
*/ |
||||
public function run() { |
||||
$deletedEntries = 0; |
||||
|
||||
$query = $this->connection->getQueryBuilder(); |
||||
$query->select(['s1.id', $query->createFunction('s1.`share_with` AS `user`'), $query->createFunction('s2.`share_with` AS `group`')]) |
||||
->from('share', 's1') |
||||
->where($query->expr()->isNotNull('s1.parent')) |
||||
// \OC\Share\Constant::$shareTypeGroupUserUnique === 2 |
||||
->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2))) |
||||
->andWhere($query->expr()->isNotNull('s2.id')) |
||||
->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(Share::SHARE_TYPE_GROUP))) |
||||
->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')); |
||||
|
||||
$deleteQuery = $this->connection->getQueryBuilder(); |
||||
$deleteQuery->delete('share') |
||||
->where($query->expr()->eq('id', $deleteQuery->createParameter('share'))); |
||||
|
||||
$result = $query->execute(); |
||||
while ($row = $result->fetch()) { |
||||
if (!$this->isMember($row['group'], $row['user'])) { |
||||
$deletedEntries += $deleteQuery->setParameter('share', (int) $row['id']) |
||||
->execute(); |
||||
} |
||||
} |
||||
$result->closeCursor(); |
||||
|
||||
if ($deletedEntries) { |
||||
$this->emit('\OC\Repair', 'info', array('Removed ' . $deletedEntries . ' shares where user is not a member of the group anymore')); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $gid |
||||
* @param string $uid |
||||
* @return bool |
||||
*/ |
||||
protected function isMember($gid, $uid) { |
||||
if (isset($this->memberships[$gid][$uid])) { |
||||
return $this->memberships[$gid][$uid]; |
||||
} |
||||
|
||||
$isMember = $this->groupManager->isInGroup($uid, $gid); |
||||
if (!isset($this->memberships[$gid])) { |
||||
$this->memberships[$gid] = []; |
||||
} |
||||
$this->memberships[$gid][$uid] = $isMember; |
||||
|
||||
return $isMember; |
||||
} |
||||
} |
||||
@ -0,0 +1,138 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
namespace Test\Repair; |
||||
|
||||
use OC\Repair\OldGroupMembershipShares; |
||||
use OC\Share\Constants; |
||||
|
||||
class OldGroupMembershipSharesTest extends \Test\TestCase { |
||||
|
||||
/** @var OldGroupMembershipShares */ |
||||
protected $repair; |
||||
|
||||
/** @var \OCP\IDBConnection */ |
||||
protected $connection; |
||||
|
||||
/** @var \OCP\IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ |
||||
protected $groupManager; |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
/** \OCP\IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ |
||||
$this->groupManager = $this->getMockBuilder('OCP\IGroupManager') |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
$this->connection = \OC::$server->getDatabaseConnection(); |
||||
|
||||
$this->deleteAllShares(); |
||||
} |
||||
|
||||
protected function tearDown() { |
||||
$this->deleteAllShares(); |
||||
|
||||
parent::tearDown(); |
||||
} |
||||
|
||||
protected function deleteAllShares() { |
||||
$qb = $this->connection->getQueryBuilder(); |
||||
$qb->delete('share')->execute(); |
||||
} |
||||
|
||||
public function testRun() { |
||||
$repair = new OldGroupMembershipShares( |
||||
$this->connection, |
||||
$this->groupManager |
||||
); |
||||
|
||||
$this->groupManager->expects($this->exactly(2)) |
||||
->method('isInGroup') |
||||
->willReturnMap([ |
||||
['member', 'group', true], |
||||
['not-a-member', 'group', false], |
||||
]); |
||||
|
||||
$parent = $this->createShare(Constants::SHARE_TYPE_GROUP, 'group', null); |
||||
$group2 = $this->createShare(Constants::SHARE_TYPE_GROUP, 'group2', $parent); |
||||
$user1 = $this->createShare(Constants::SHARE_TYPE_USER, 'user1', $parent); |
||||
|
||||
// \OC\Share\Constant::$shareTypeGroupUserUnique === 2 |
||||
$member = $this->createShare(2, 'member', $parent); |
||||
$notAMember = $this->createShare(2, 'not-a-member', $parent); |
||||
|
||||
$query = $this->connection->getQueryBuilder(); |
||||
$result = $query->select('id') |
||||
->from('share') |
||||
->orderBy('id', 'ASC') |
||||
->execute(); |
||||
$rows = $result->fetchAll(); |
||||
$this->assertSame([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member], ['id' => $notAMember]], $rows); |
||||
$result->closeCursor(); |
||||
|
||||
$repair->run(); |
||||
|
||||
$query = $this->connection->getQueryBuilder(); |
||||
$result = $query->select('id') |
||||
->from('share') |
||||
->orderBy('id', 'ASC') |
||||
->execute(); |
||||
$rows = $result->fetchAll(); |
||||
$this->assertSame([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member]], $rows); |
||||
$result->closeCursor(); |
||||
} |
||||
|
||||
/** |
||||
* @param string $shareType |
||||
* @param string $shareWith |
||||
* @param null|int $parent |
||||
* @return int |
||||
*/ |
||||
protected function createShare($shareType, $shareWith, $parent) { |
||||
$qb = $this->connection->getQueryBuilder(); |
||||
$shareValues = [ |
||||
'share_type' => $qb->expr()->literal($shareType), |
||||
'share_with' => $qb->expr()->literal($shareWith), |
||||
'uid_owner' => $qb->expr()->literal('user1'), |
||||
'item_type' => $qb->expr()->literal('folder'), |
||||
'item_source' => $qb->expr()->literal(123), |
||||
'item_target' => $qb->expr()->literal('/123'), |
||||
'file_source' => $qb->expr()->literal(123), |
||||
'file_target' => $qb->expr()->literal('/test'), |
||||
'permissions' => $qb->expr()->literal(1), |
||||
'stime' => $qb->expr()->literal(time()), |
||||
'expiration' => $qb->expr()->literal('2015-09-25 00:00:00'), |
||||
]; |
||||
|
||||
if ($parent) { |
||||
$shareValues['parent'] = $qb->expr()->literal($parent); |
||||
} |
||||
|
||||
$qb = $this->connection->getQueryBuilder(); |
||||
$qb->insert('share') |
||||
->values($shareValues) |
||||
->execute(); |
||||
|
||||
return $this->getLastShareId(); |
||||
} |
||||
|
||||
/** |
||||
* @return int |
||||
*/ |
||||
protected function getLastShareId() { |
||||
// select because lastInsertId does not work with OCI |
||||
$query = $this->connection->getQueryBuilder(); |
||||
$result = $query->select('id') |
||||
->from('share') |
||||
->orderBy('id', 'DESC') |
||||
->execute(); |
||||
$row = $result->fetch(); |
||||
$result->closeCursor(); |
||||
return $row['id']; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue