Merge pull request #22156 from owncloud/share2_unshare_ifexpired

Delete a link share if it is expired on access
This commit is contained in:
Thomas Müller 2016-02-08 09:59:00 +01:00
commit 62b5948ff5
3 changed files with 239 additions and 3 deletions

View File

@ -22,6 +22,7 @@
namespace OC\Share20;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
use OCP\Share\IManager;
use OCP\Share\IProviderFactory;
@ -778,7 +779,57 @@ class Manager implements IManager {
$provider = $this->factory->getProviderForType($shareType);
return $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
/*
* Work around so we don't return expired shares but still follow
* proper pagination.
*/
if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
$shares2 = [];
$today = new \DateTime();
while(true) {
$added = 0;
foreach ($shares as $share) {
// Check if the share is expired and if so delete it
if ($share->getExpirationDate() !== null &&
$share->getExpirationDate() <= $today
) {
try {
$this->deleteShare($share);
} catch (NotFoundException $e) {
//Ignore since this basically means the share is deleted
}
continue;
}
$added++;
$shares2[] = $share;
if (count($shares2) === $limit) {
break;
}
}
if (count($shares2) === $limit) {
break;
}
$offset += $added;
// Fetch again $limit shares
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
// No more shares means we are done
if (empty($shares)) {
break;
}
}
$shares = $shares2;
}
return $shares;
}
/**
@ -803,6 +854,14 @@ class Manager implements IManager {
$share = $provider->getShareById($id, $recipient);
// Validate link shares expiration date
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
$share->getExpirationDate() !== null &&
$share->getExpirationDate() <= new \DateTime()) {
$this->deleteShare($share);
throw new ShareNotFound();
}
return $share;
}
@ -831,7 +890,11 @@ class Manager implements IManager {
$share = $provider->getShareByToken($token);
//TODO check if share expired
if ($share->getExpirationDate() !== null &&
$share->getExpirationDate() <= new \DateTime()) {
$this->deleteShare($share);
throw new ShareNotFound();
}
return $share;
}

View File

@ -101,7 +101,7 @@ interface IShareProvider {
* @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
* @param int $limit The maximum number of shares to be returned, -1 for all shares
* @param int $offset
* @return \OCP\Share\IShare Share[]
* @return \OCP\Share\IShare[]
* @since 9.0.0
*/
public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset);

View File

@ -22,6 +22,7 @@ namespace Test\Share20;
use OCP\Files\IRootFolder;
use OCP\IUserManager;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IProviderFactory;
use OCP\Share\IShare;
use OC\Share20\Manager;
@ -499,6 +500,33 @@ class ManagerTest extends \Test\TestCase {
$this->assertEquals($share, $this->manager->getShareById('default:42'));
}
/**
* @expectedException \OCP\Share\Exceptions\ShareNotFound
*/
public function testGetExpiredShareById() {
$manager = $this->createManagerMock()
->setMethods(['deleteShare'])
->getMock();
$date = new \DateTime();
$date->setTime(0,0,0);
$share = $this->manager->newShare();
$share->setExpirationDate($date)
->setShareType(\OCP\Share::SHARE_TYPE_LINK);
$this->defaultProvider->expects($this->once())
->method('getShareById')
->with('42')
->willReturn($share);
$manager->expects($this->once())
->method('deleteShare')
->with($share);
$manager->getShareById('default:42');
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Passwords are enforced for link shares
@ -1672,6 +1700,109 @@ class ManagerTest extends \Test\TestCase {
$manager->createShare($share);
}
public function testGetSharesBy() {
$share = $this->manager->newShare();
$node = $this->getMock('OCP\Files\Folder');
$this->defaultProvider->expects($this->once())
->method('getSharesBy')
->with(
$this->equalTo('user'),
$this->equalTo(\OCP\Share::SHARE_TYPE_USER),
$this->equalTo($node),
$this->equalTo(true),
$this->equalTo(1),
$this->equalTo(1)
)->willReturn([$share]);
$shares = $this->manager->getSharesBy('user', \OCP\Share::SHARE_TYPE_USER, $node, true, 1, 1);
$this->assertCount(1, $shares);
$this->assertSame($share, $shares[0]);
}
/**
* Test to ensure we correctly remove expired link shares
*
* We have 8 Shares and we want the 3 first valid shares.
* share 3-6 and 8 are expired. Thus at the end of this test we should
* have received share 1,2 and 7. And from the manager. Share 3-6 should be
* deleted (as they are evaluated). but share 8 should still be there.
*/
public function testGetSharesByExpiredLinkShares() {
$manager = $this->createManagerMock()
->setMethods(['deleteShare'])
->getMock();
/** @var \OCP\Share\IShare[] $shares */
$shares = [];
/*
* This results in an array of 8 IShare elements
*/
for ($i = 0; $i < 8; $i++) {
$share = $this->manager->newShare();
$share->setId($i);
$shares[] = $share;
}
$today = new \DateTime();
$today->setTime(0,0,0);
/*
* Set the expiration date to today for some shares
*/
$shares[2]->setExpirationDate($today);
$shares[3]->setExpirationDate($today);
$shares[4]->setExpirationDate($today);
$shares[5]->setExpirationDate($today);
/** @var \OCP\Share\IShare[] $i */
$shares2 = [];
for ($i = 0; $i < 8; $i++) {
$shares2[] = clone $shares[$i];
}
$node = $this->getMock('OCP\Files\File');
/*
* Simulate the getSharesBy call.
*/
$this->defaultProvider
->method('getSharesBy')
->will($this->returnCallback(function($uid, $type, $node, $reshares, $limit, $offset) use (&$shares2) {
return array_slice($shares2, $offset, $limit);
}));
/*
* Simulate the deleteShare call.
*/
$manager->method('deleteShare')
->will($this->returnCallback(function($share) use (&$shares2) {
for($i = 0; $i < count($shares2); $i++) {
if ($shares2[$i]->getId() === $share->getId()) {
array_splice($shares2, $i, 1);
break;
}
}
}));
$res = $manager->getSharesBy('user', \OCP\Share::SHARE_TYPE_LINK, $node, true, 3, 0);
$this->assertCount(3, $res);
$this->assertEquals($shares[0]->getId(), $res[0]->getId());
$this->assertEquals($shares[1]->getId(), $res[1]->getId());
$this->assertEquals($shares[6]->getId(), $res[2]->getId());
$this->assertCount(4, $shares2);
$this->assertEquals(0, $shares2[0]->getId());
$this->assertEquals(1, $shares2[1]->getId());
$this->assertEquals(6, $shares2[2]->getId());
$this->assertEquals(7, $shares2[3]->getId());
$this->assertSame($today, $shares[3]->getExpirationDate());
}
public function testGetShareByToken() {
$factory = $this->getMock('\OCP\Share\IProviderFactory');
@ -1704,6 +1835,48 @@ class ManagerTest extends \Test\TestCase {
$this->assertSame($share, $ret);
}
/**
* @expectedException \OCP\Share\Exceptions\ShareNotFound
*/
public function testGetShareByTokenExpired() {
$manager = $this->createManagerMock()
->setMethods(['deleteShare'])
->getMock();
$date = new \DateTime();
$date->setTime(0,0,0);
$share = $this->manager->newShare();
$share->setExpirationDate($date);
$this->defaultProvider->expects($this->once())
->method('getShareByToken')
->with('expiredToken')
->willReturn($share);
$manager->expects($this->once())
->method('deleteShare')
->with($this->equalTo($share));
$manager->getShareByToken('expiredToken');
}
public function testGetShareByTokenNotExpired() {
$date = new \DateTime();
$date->setTime(0,0,0);
$date->add(new \DateInterval('P2D'));
$share = $this->manager->newShare();
$share->setExpirationDate($date);
$this->defaultProvider->expects($this->once())
->method('getShareByToken')
->with('expiredToken')
->willReturn($share);
$res = $this->manager->getShareByToken('expiredToken');
$this->assertSame($share, $res);
}
public function testCheckPasswordNoLinkShare() {
$share = $this->getMock('\OCP\Share\IShare');
$share->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_USER);