Merge pull request #20773 from owncloud/share2.0_create
[Sharing 2.0] create share
This commit is contained in:
commit
601457d221
|
@ -29,13 +29,18 @@ class OCSShareWrapper {
|
|||
return new Share20OCS(
|
||||
new \OC\Share20\Manager(
|
||||
\OC::$server->getLogger(),
|
||||
\OC::$server->getAppConfig(),
|
||||
\OC::$server->getConfig(),
|
||||
new \OC\Share20\DefaultShareProvider(
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
\OC::$server->getUserManager(),
|
||||
\OC::$server->getGroupManager(),
|
||||
\OC::$server->getRootFolder()
|
||||
)
|
||||
),
|
||||
\OC::$server->getSecureRandom(),
|
||||
\OC::$server->getHasher(),
|
||||
\OC::$server->getMountManager(),
|
||||
\OC::$server->getGroupManager(),
|
||||
\OC::$server->getL10N('core')
|
||||
),
|
||||
\OC::$server->getGroupManager(),
|
||||
\OC::$server->getUserManager(),
|
||||
|
@ -49,8 +54,8 @@ class OCSShareWrapper {
|
|||
return \OCA\Files_Sharing\API\Local::getAllShares($params);
|
||||
}
|
||||
|
||||
public function createShare($params) {
|
||||
return \OCA\Files_Sharing\API\Local::createShare($params);
|
||||
public function createShare() {
|
||||
return $this->getShare20OCS()->createShare();
|
||||
}
|
||||
|
||||
public function getShare($params) {
|
||||
|
|
|
@ -25,7 +25,6 @@ use OC\Share20\IShare;
|
|||
use OCP\IGroupManager;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IRequest;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Files\IRootFolder;
|
||||
|
@ -98,7 +97,7 @@ class Share20OCS {
|
|||
$result['item_type'] = 'file';
|
||||
}
|
||||
$result['storage_id'] = $path->getStorage()->getId();
|
||||
$result['storage'] = \OC\Files\Cache\Storage::getNumericStorageId($path->getStorage()->getId());
|
||||
$result['storage'] = $path->getStorage()->getCache()->getNumericStorageId();
|
||||
$result['item_source'] = $path->getId();
|
||||
$result['file_source'] = $path->getId();
|
||||
$result['file_parent'] = $path->getParent()->getId();
|
||||
|
@ -191,6 +190,130 @@ class Share20OCS {
|
|||
return new \OC_OCS_Result();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OC_OCS_Result
|
||||
*/
|
||||
public function createShare() {
|
||||
$share = $this->shareManager->newShare();
|
||||
|
||||
// Verify path
|
||||
$path = $this->request->getParam('path', null);
|
||||
if ($path === null) {
|
||||
return new \OC_OCS_Result(null, 404, 'please specify a file or folder path');
|
||||
}
|
||||
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
|
||||
try {
|
||||
$path = $userFolder->get($path);
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
return new \OC_OCS_Result(null, 404, 'wrong path, file/folder doesn\'t exist');
|
||||
}
|
||||
|
||||
$share->setPath($path);
|
||||
|
||||
// Parse permissions (if available)
|
||||
$permissions = $this->request->getParam('permissions', null);
|
||||
if ($permissions === null) {
|
||||
$permissions = \OCP\Constants::PERMISSION_ALL;
|
||||
} else {
|
||||
$permissions = (int)$permissions;
|
||||
}
|
||||
|
||||
if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) {
|
||||
return new \OC_OCS_Result(null, 404, 'invalid permissions');
|
||||
}
|
||||
|
||||
// Shares always require read permissions
|
||||
$permissions |= \OCP\Constants::PERMISSION_READ;
|
||||
|
||||
if ($path instanceof \OCP\Files\File) {
|
||||
// Single file shares should never have delete or create permissions
|
||||
$permissions &= ~\OCP\Constants::PERMISSION_DELETE;
|
||||
$permissions &= ~\OCP\Constants::PERMISSION_CREATE;
|
||||
}
|
||||
|
||||
$shareWith = $this->request->getParam('shareWith', null);
|
||||
$shareType = (int)$this->request->getParam('shareType', '-1');
|
||||
|
||||
if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
|
||||
// Valid user is required to share
|
||||
if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
|
||||
return new \OC_OCS_Result(null, 404, 'please specify a valid user');
|
||||
}
|
||||
$share->setSharedWith($this->userManager->get($shareWith));
|
||||
$share->setPermissions($permissions);
|
||||
} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
// Valid group is required to share
|
||||
if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
|
||||
return new \OC_OCS_Result(null, 404, 'please specify a valid group');
|
||||
}
|
||||
$share->setSharedWith($this->groupManager->get($shareWith));
|
||||
$share->setPermissions($permissions);
|
||||
} else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
|
||||
//Can we even share links?
|
||||
if (!$this->shareManager->shareApiAllowLinks()) {
|
||||
return new \OC_OCS_Result(null, 404, 'public link sharing is disabled by the administrator');
|
||||
}
|
||||
|
||||
$publicUpload = $this->request->getParam('publicUpload', null);
|
||||
if ($publicUpload === 'true') {
|
||||
// Check if public upload is allowed
|
||||
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
|
||||
return new \OC_OCS_Result(null, 403, '"public upload disabled by the administrator');
|
||||
}
|
||||
|
||||
// Public upload can only be set for folders
|
||||
if ($path instanceof \OCP\Files\File) {
|
||||
return new \OC_OCS_Result(null, 404, '"public upload is only possible for public shared folders');
|
||||
}
|
||||
|
||||
$share->setPermissions(
|
||||
\OCP\Constants::PERMISSION_READ |
|
||||
\OCP\Constants::PERMISSION_CREATE |
|
||||
\OCP\Constants::PERMISSION_UPDATE
|
||||
);
|
||||
} else {
|
||||
$share->setPermissions(\OCP\Constants::PERMISSION_READ);
|
||||
}
|
||||
|
||||
// Set password
|
||||
$share->setPassword($this->request->getParam('password', null));
|
||||
|
||||
//Expire date
|
||||
$expireDate = $this->request->getParam('expireDate', null);
|
||||
|
||||
if ($expireDate !== null) {
|
||||
try {
|
||||
$expireDate = $this->parseDate($expireDate);
|
||||
$share->setExpirationDate($expireDate);
|
||||
} catch (\Exception $e) {
|
||||
return new \OC_OCS_Result(null, 404, 'Invalid Date. Format must be YYYY-MM-DD.');
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
|
||||
//fixme Remote shares are handled by old code path for now
|
||||
return \OCA\Files_Sharing\API\Local::createShare([]);
|
||||
} else {
|
||||
return new \OC_OCS_Result(null, 400, "unknown share type");
|
||||
}
|
||||
|
||||
$share->setShareType($shareType);
|
||||
$share->setSharedBy($this->currentUser);
|
||||
|
||||
try {
|
||||
$share = $this->shareManager->createShare($share);
|
||||
} catch (\OC\HintException $e) {
|
||||
$code = $e->getCode() === 0 ? 403 : $e->getCode();
|
||||
return new \OC_OCS_Result(null, $code, $e->getHint());
|
||||
}catch (\Exception $e) {
|
||||
return new \OC_OCS_Result(null, 403, $e->getMessage());
|
||||
}
|
||||
|
||||
$share = $this->formatShare($share);
|
||||
return new \OC_OCS_Result($share);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IShare $share
|
||||
* @return bool
|
||||
|
@ -216,4 +339,30 @@ class Share20OCS {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the passed date is valid ISO 8601
|
||||
* So YYYY-MM-DD
|
||||
* If not throw an exception
|
||||
*
|
||||
* @param string $expireDate
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return \DateTime
|
||||
*/
|
||||
private function parseDate($expireDate) {
|
||||
try {
|
||||
$date = new \DateTime($expireDate);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
|
||||
}
|
||||
|
||||
if ($date === false) {
|
||||
throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
|
||||
}
|
||||
|
||||
$date->setTime(0,0,0);
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
$this->rootFolder = $this->getMock('OCP\Files\IRootFolder');
|
||||
$this->urlGenerator = $this->getMock('OCP\IURLGenerator');
|
||||
$this->currentUser = $this->getMock('OCP\IUser');
|
||||
$this->currentUser->method('getUID')->willReturn('currentUser');
|
||||
|
||||
$this->ocs = new Share20OCS(
|
||||
$this->shareManager,
|
||||
|
@ -171,8 +172,18 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
$group = $this->getMock('OCP\IGroup');
|
||||
$group->method('getGID')->willReturn('groupId');
|
||||
|
||||
$storage = $this->getMock('OCP\Files\Storage');
|
||||
$cache = $this->getMockBuilder('OC\Files\Cache\Cache')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$cache->method('getNumericStorageId')->willReturn(101);
|
||||
|
||||
$storage = $this->getMockBuilder('OC\Files\Storage\Storage')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$storage->method('getId')->willReturn('STORAGE');
|
||||
$storage->method('getCache')->willReturn($cache);
|
||||
|
||||
|
||||
|
||||
$parentFolder = $this->getMock('OCP\Files\Folder');
|
||||
$parentFolder->method('getId')->willReturn(3);
|
||||
|
@ -223,7 +234,7 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
'parent' => 6,
|
||||
'storage_id' => 'STORAGE',
|
||||
'path' => 'file',
|
||||
'storage' => null, // HACK around static function
|
||||
'storage' => 101,
|
||||
'mail_send' => 0,
|
||||
];
|
||||
$data[] = [$share, $expected];
|
||||
|
@ -262,7 +273,7 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
'parent' => 6,
|
||||
'storage_id' => 'STORAGE',
|
||||
'path' => 'folder',
|
||||
'storage' => null, // HACK around static function
|
||||
'storage' => 101,
|
||||
'mail_send' => 0,
|
||||
];
|
||||
$data[] = [$share, $expected];
|
||||
|
@ -304,7 +315,7 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
'parent' => 6,
|
||||
'storage_id' => 'STORAGE',
|
||||
'path' => 'folder',
|
||||
'storage' => null, // HACK around static function
|
||||
'storage' => 101,
|
||||
'mail_send' => 0,
|
||||
'url' => 'url',
|
||||
];
|
||||
|
@ -391,4 +402,280 @@ class Share20OCSTest extends \Test\TestCase {
|
|||
$share->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_LINK);
|
||||
$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
|
||||
}
|
||||
|
||||
public function testCreateShareNoPath() {
|
||||
$expected = new \OC_OCS_Result(null, 404, 'please specify a file or folder path');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareInvalidPath() {
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'invalid-path'],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('invalid-path')
|
||||
->will($this->throwException(new \OCP\Files\NotFoundException()));
|
||||
|
||||
$expected = new \OC_OCS_Result(null, 404, 'wrong path, file/folder doesn\'t exist');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareInvalidPermissions() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, 32],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$expected = new \OC_OCS_Result(null, 404, 'invalid permissions');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareUserNoShareWith() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, \OCP\Constants::PERMISSION_ALL],
|
||||
['shareType', $this->any(), \OCP\Share::SHARE_TYPE_USER],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$expected = new \OC_OCS_Result(null, 404, 'please specify a valid user');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareUserNoValidShareWith() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, \OCP\Constants::PERMISSION_ALL],
|
||||
['shareType', $this->any(), \OCP\Share::SHARE_TYPE_USER],
|
||||
['shareWith', $this->any(), 'invalidUser'],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$expected = new \OC_OCS_Result(null, 404, 'please specify a valid user');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareUser() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$ocs = $this->getMockBuilder('OCA\Files_Sharing\API\Share20OCS')
|
||||
->setConstructorArgs([
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
$this->userManager,
|
||||
$this->request,
|
||||
$this->rootFolder,
|
||||
$this->urlGenerator,
|
||||
$this->currentUser
|
||||
])->setMethods(['formatShare'])
|
||||
->getMock();
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, \OCP\Constants::PERMISSION_ALL],
|
||||
['shareType', $this->any(), \OCP\Share::SHARE_TYPE_USER],
|
||||
['shareWith', null, 'validUser'],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$user = $this->getMock('\OCP\IUser');
|
||||
$this->userManager->method('userExists')->with('validUser')->willReturn(true);
|
||||
$this->userManager->method('get')->with('validUser')->willReturn($user);
|
||||
|
||||
$share->method('setPath')->with($path);
|
||||
$share->method('setPermissions')
|
||||
->with(
|
||||
\OCP\Constants::PERMISSION_ALL &
|
||||
~\OCP\Constants::PERMISSION_DELETE &
|
||||
~\OCP\Constants::PERMISSION_CREATE);
|
||||
$share->method('setShareType')->with(\OCP\Share::SHARE_TYPE_USER);
|
||||
$share->method('setSharedWith')->with($user);
|
||||
$share->method('setSharedBy')->with($this->currentUser);
|
||||
|
||||
$expected = new \OC_OCS_Result();
|
||||
$result = $ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareGroupNoValidShareWith() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, \OCP\Constants::PERMISSION_ALL],
|
||||
['shareType', $this->any(), \OCP\Share::SHARE_TYPE_GROUP],
|
||||
['shareWith', $this->any(), 'invalidGroup'],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$expected = new \OC_OCS_Result(null, 404, 'please specify a valid user');
|
||||
|
||||
$result = $this->ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
|
||||
public function testCreateShareGroup() {
|
||||
$share = $this->getMock('\OC\Share20\IShare');
|
||||
$this->shareManager->method('newShare')->willReturn($share);
|
||||
|
||||
$ocs = $this->getMockBuilder('OCA\Files_Sharing\API\Share20OCS')
|
||||
->setConstructorArgs([
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
$this->userManager,
|
||||
$this->request,
|
||||
$this->rootFolder,
|
||||
$this->urlGenerator,
|
||||
$this->currentUser
|
||||
])->setMethods(['formatShare'])
|
||||
->getMock();
|
||||
|
||||
$this->request
|
||||
->method('getParam')
|
||||
->will($this->returnValueMap([
|
||||
['path', null, 'valid-path'],
|
||||
['permissions', null, \OCP\Constants::PERMISSION_ALL],
|
||||
['shareType', '-1', \OCP\Share::SHARE_TYPE_GROUP],
|
||||
['shareWith', null, 'validGroup'],
|
||||
]));
|
||||
|
||||
$userFolder = $this->getMock('\OCP\Files\Folder');
|
||||
$this->rootFolder->expects($this->once())
|
||||
->method('getUserFolder')
|
||||
->with('currentUser')
|
||||
->willReturn($userFolder);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\Folder');
|
||||
$userFolder->expects($this->once())
|
||||
->method('get')
|
||||
->with('valid-path')
|
||||
->willReturn($path);
|
||||
|
||||
$group = $this->getMock('\OCP\IGroup');
|
||||
$this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
|
||||
$this->groupManager->method('get')->with('validGroup')->willReturn($group);
|
||||
|
||||
$share->method('setPath')->with($path);
|
||||
$share->method('setPermissions')->with(\OCP\Constants::PERMISSION_ALL);
|
||||
$share->method('setShareType')->with(\OCP\Share::SHARE_TYPE_GROUP);
|
||||
$share->method('setSharedWith')->with($group);
|
||||
$share->method('setSharedBy')->with($this->currentUser);
|
||||
|
||||
$expected = new \OC_OCS_Result();
|
||||
$result = $ocs->createShare();
|
||||
|
||||
$this->assertEquals($expected->getMeta(), $result->getMeta());
|
||||
$this->assertEquals($expected->getData(), $result->getData());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,13 @@ trait Provisioning {
|
|||
} elseif ($this->currentServer === 'REMOTE') {
|
||||
$this->createdRemoteUsers[$user] = $user;
|
||||
}
|
||||
|
||||
|
||||
//Quick hack to login once with the current user
|
||||
$options2 = [
|
||||
'auth' => [$user, '123456'],
|
||||
];
|
||||
$url = $fullUrl.'/'.$user;
|
||||
$client->send($client->createRequest('GET', $url, $options2));
|
||||
}
|
||||
|
||||
public function createUser($user) {
|
||||
|
|
|
@ -231,20 +231,23 @@ Feature: sharing
|
|||
And User "user2" should be included in the response
|
||||
And User "user3" should not be included in the response
|
||||
|
||||
Scenario: getting all shares of a file with reshares
|
||||
Given user "user0" exists
|
||||
And user "user1" exists
|
||||
And user "user2" exists
|
||||
And user "user3" exists
|
||||
And file "textfile0.txt" of user "user0" is shared with user "user1"
|
||||
And file "textfile0.txt" of user "user1" is shared with user "user2"
|
||||
And As an "user0"
|
||||
When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt"
|
||||
Then the OCS status code should be "100"
|
||||
And the HTTP status code should be "200"
|
||||
And User "user1" should be included in the response
|
||||
And User "user2" should be included in the response
|
||||
And User "user3" should not be included in the response
|
||||
# Skip this test for now. Since the new shares do not create reshares
|
||||
# TODO enable when getshares is updated
|
||||
#
|
||||
# Scenario: getting all shares of a file with reshares
|
||||
# Given user "user0" exists
|
||||
# And user "user1" exists
|
||||
# And user "user2" exists
|
||||
# And user "user3" exists
|
||||
# And file "textfile0.txt" of user "user0" is shared with user "user1"
|
||||
# And file "textfile0.txt" of user "user1" is shared with user "user2"
|
||||
# And As an "user0"
|
||||
# When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt"
|
||||
# Then the OCS status code should be "100"
|
||||
# And the HTTP status code should be "200"
|
||||
# And User "user1" should be included in the response
|
||||
# And User "user2" should be included in the response
|
||||
# And User "user3" should not be included in the response
|
||||
|
||||
Scenario: getting share info of a share
|
||||
Given user "user0" exists
|
||||
|
@ -263,7 +266,7 @@ Feature: sharing
|
|||
| file_source | A_NUMBER |
|
||||
| file_target | /textfile0.txt |
|
||||
| path | /textfile0.txt |
|
||||
| permissions | 23 |
|
||||
| permissions | 19 |
|
||||
| stime | A_NUMBER |
|
||||
| storage | A_NUMBER |
|
||||
| mail_send | 0 |
|
||||
|
@ -326,7 +329,7 @@ Feature: sharing
|
|||
| permissions | 8 |
|
||||
And As an "user1"
|
||||
When creating a share with
|
||||
| path | /textfile0. (2).txt |
|
||||
| path | /textfile0 (2).txt |
|
||||
| shareType | 0 |
|
||||
| shareWith | user2 |
|
||||
| permissions | 31 |
|
||||
|
@ -346,7 +349,7 @@ Feature: sharing
|
|||
| permissions | 16 |
|
||||
And As an "user1"
|
||||
When creating a share with
|
||||
| path | /textfile0. (2).txt |
|
||||
| path | /textfile0 (2).txt |
|
||||
| shareType | 0 |
|
||||
| shareWith | user2 |
|
||||
| permissions | 31 |
|
||||
|
|
|
@ -684,7 +684,8 @@
|
|||
</field>
|
||||
|
||||
<!-- Foreign Key users::uid -->
|
||||
<!-- This is the initiator of the share -->
|
||||
<!-- This is the owner of the share
|
||||
which does not have to be the initiator of the share -->
|
||||
<field>
|
||||
<name>uid_owner</name>
|
||||
<type>text</type>
|
||||
|
@ -694,18 +695,17 @@
|
|||
</field>
|
||||
|
||||
<!-- Foreign Key users::uid -->
|
||||
<!-- This is the owner of the file, this can be
|
||||
different from the initiator of the share.
|
||||
The naming is subobtimal but prevents huge
|
||||
migration steps -->
|
||||
<!-- This is the initiator of the share -->
|
||||
<field>
|
||||
<name>uid_fileowner</name>
|
||||
<name>uid_initiator</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
|
||||
|
||||
<!-- Foreign Key share::id or NULL -->
|
||||
<field>
|
||||
<name>parent</name>
|
||||
|
|
|
@ -64,11 +64,90 @@ class DefaultShareProvider implements IShareProvider {
|
|||
|
||||
/**
|
||||
* Share a path
|
||||
*
|
||||
*
|
||||
* @param IShare $share
|
||||
* @return IShare The share object
|
||||
* @throws ShareNotFound
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function create(IShare $share) {
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
|
||||
$qb->insert('share');
|
||||
$qb->setValue('share_type', $qb->createNamedParameter($share->getShareType()));
|
||||
|
||||
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
|
||||
//Set the UID of the user we share with
|
||||
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()->getUID()));
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
//Set the GID of the group we share with
|
||||
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()->getGID()));
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
|
||||
//Set the token of the share
|
||||
$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
|
||||
|
||||
//If a password is set store it
|
||||
if ($share->getPassword() !== null) {
|
||||
$qb->setValue('share_with', $qb->createNamedParameter($share->getPassword()));
|
||||
}
|
||||
|
||||
//If an expiration date is set store it
|
||||
if ($share->getExpirationDate() !== null) {
|
||||
$qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime'));
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('invalid share type!');
|
||||
}
|
||||
|
||||
// Set what is shares
|
||||
$qb->setValue('item_type', $qb->createParameter('itemType'));
|
||||
if ($share->getPath() instanceof \OCP\Files\File) {
|
||||
$qb->setParameter('itemType', 'file');
|
||||
} else {
|
||||
$qb->setParameter('itemType', 'folder');
|
||||
}
|
||||
|
||||
// Set the file id
|
||||
$qb->setValue('item_source', $qb->createNamedParameter($share->getPath()->getId()));
|
||||
$qb->setValue('file_source', $qb->createNamedParameter($share->getPath()->getId()));
|
||||
|
||||
// set the permissions
|
||||
$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
|
||||
|
||||
// Set who created this share
|
||||
$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()->getUID()));
|
||||
|
||||
// Set who is the owner of this file/folder (and this the owner of the share)
|
||||
$qb->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()->getUID()));
|
||||
|
||||
// Set the file target
|
||||
$qb->setValue('file_target', $qb->createNamedParameter($share->getTarget()));
|
||||
|
||||
// Set the time this share was created
|
||||
$qb->setValue('stime', $qb->createNamedParameter(time()));
|
||||
|
||||
// insert the data and fetch the id of the share
|
||||
$this->dbConn->beginTransaction();
|
||||
$qb->execute();
|
||||
$id = $this->dbConn->lastInsertId('*PREFIX*share');
|
||||
$this->dbConn->commit();
|
||||
|
||||
// Now fetch the inserted share and create a complete share object
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from('*PREFIX*share')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
|
||||
|
||||
$cursor = $qb->execute();
|
||||
$data = $cursor->fetch();
|
||||
$cursor->closeCursor();
|
||||
|
||||
if ($data === false) {
|
||||
throw new ShareNotFound();
|
||||
}
|
||||
|
||||
$share = $this->createShare($data);
|
||||
return $share;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,11 +249,29 @@ class DefaultShareProvider implements IShareProvider {
|
|||
/**
|
||||
* Get shares for a given path
|
||||
*
|
||||
* @param \OCP\IUser $user
|
||||
* @param \OCP\Files\Node $path
|
||||
* @return IShare[]
|
||||
*/
|
||||
public function getSharesByPath(IUser $user, Node $path) {
|
||||
public function getSharesByPath(Node $path) {
|
||||
$qb = $this->dbConn->getQueryBuilder();
|
||||
|
||||
$cursor = $qb->select('*')
|
||||
->from('share')
|
||||
->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)),
|
||||
$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP))
|
||||
)
|
||||
)->execute();
|
||||
|
||||
$shares = [];
|
||||
while($data = $cursor->fetch()) {
|
||||
$shares[] = $this->createShare($data);
|
||||
}
|
||||
$cursor->closeCursor();
|
||||
|
||||
return $shares;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,16 +320,21 @@ class DefaultShareProvider implements IShareProvider {
|
|||
$share->setSharedWith($data['share_with']);
|
||||
}
|
||||
|
||||
$share->setSharedBy($this->userManager->get($data['uid_owner']));
|
||||
if ($data['uid_initiator'] === null) {
|
||||
//OLD SHARE
|
||||
$share->setSharedBy($this->userManager->get($data['uid_owner']));
|
||||
$folder = $this->rootFolder->getUserFolder($share->getSharedBy()->getUID());
|
||||
$path = $folder->getById((int)$data['file_source'])[0];
|
||||
|
||||
// TODO: getById can return an array. How to handle this properly??
|
||||
$folder = $this->rootFolder->getUserFolder($share->getSharedBy()->getUID());
|
||||
$path = $folder->getById((int)$data['file_source'])[0];
|
||||
$owner = $path->getOwner();
|
||||
$share->setShareOwner($owner);
|
||||
} else {
|
||||
//New share!
|
||||
$share->setSharedBy($this->userManager->get($data['uid_initiator']));
|
||||
$share->setShareOwner($this->userManager->get($data['uid_owner']));
|
||||
}
|
||||
|
||||
$owner = $path->getOwner();
|
||||
$share->setShareOwner($owner);
|
||||
|
||||
$path = $this->rootFolder->getUserFolder($owner->getUID())->getById((int)$data['file_source'])[0];
|
||||
$path = $this->rootFolder->getUserFolder($share->getShareOwner()->getUID())->getById((int)$data['file_source'])[0];
|
||||
$share->setPath($path);
|
||||
|
||||
if ($data['expiration'] !== null) {
|
||||
|
|
|
@ -101,7 +101,7 @@ interface IShare {
|
|||
* @param \DateTime $expireDate
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setExpirationDate(\DateTime $expireDate);
|
||||
public function setExpirationDate($expireDate);
|
||||
|
||||
/**
|
||||
* Get the share expiration date
|
||||
|
@ -110,6 +110,14 @@ interface IShare {
|
|||
*/
|
||||
public function getExpirationDate();
|
||||
|
||||
/**
|
||||
* Set the sharer of the path
|
||||
*
|
||||
* @param IUser|string $sharedBy
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setSharedBy($sharedBy);
|
||||
|
||||
/**
|
||||
* Get share sharer
|
||||
*
|
||||
|
@ -117,6 +125,15 @@ interface IShare {
|
|||
*/
|
||||
public function getSharedBy();
|
||||
|
||||
/**
|
||||
* Set the original share owner (who owns the path)
|
||||
*
|
||||
* @param IUser|string
|
||||
*
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setShareOwner($shareOwner);
|
||||
|
||||
/**
|
||||
* Get the original share owner (who owns the path)
|
||||
*
|
||||
|
@ -140,6 +157,14 @@ interface IShare {
|
|||
*/
|
||||
public function getPassword();
|
||||
|
||||
/**
|
||||
* Set the token
|
||||
*
|
||||
* @param string $token
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setToken($token);
|
||||
|
||||
/**
|
||||
* Get the token
|
||||
*
|
||||
|
@ -154,6 +179,14 @@ interface IShare {
|
|||
*/
|
||||
public function getParent();
|
||||
|
||||
/**
|
||||
* Set the target of this share
|
||||
*
|
||||
* @param string $target
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setTarget($target);
|
||||
|
||||
/**
|
||||
* Get the target of this share
|
||||
*
|
||||
|
|
|
@ -81,11 +81,10 @@ interface IShareProvider {
|
|||
/**
|
||||
* Get shares for a given path
|
||||
*
|
||||
* @param \OCP\IUser $user
|
||||
* @param \OCP\Files\Node $path
|
||||
* @return IShare[]
|
||||
*/
|
||||
public function getSharesByPath(\OCP\IUser $user, \OCP\Files\Node $path);
|
||||
public function getSharesByPath(\OCP\Files\Node $path);
|
||||
|
||||
/**
|
||||
* Get shared with the given user
|
||||
|
|
|
@ -21,53 +21,458 @@
|
|||
namespace OC\Share20;
|
||||
|
||||
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\Security\IHasher;
|
||||
use OCP\Files\Mount\IMountManager;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\IUser;
|
||||
|
||||
use OC\Share20\Exception\ShareNotFound;
|
||||
use OC\HintException;
|
||||
|
||||
/**
|
||||
* This class is the communication hub for all sharing related operations.
|
||||
*/
|
||||
class Manager {
|
||||
|
||||
/**
|
||||
* @var IShareProvider[]
|
||||
*/
|
||||
/** @var IShareProvider[] */
|
||||
private $defaultProvider;
|
||||
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var IAppConfig */
|
||||
private $appConfig;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var ISecureRandom */
|
||||
private $secureRandom;
|
||||
|
||||
/** @var IHasher */
|
||||
private $hasher;
|
||||
|
||||
/** @var IMountManager */
|
||||
private $mountManager;
|
||||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/**
|
||||
* Manager constructor.
|
||||
*
|
||||
* @param ILogger $logger
|
||||
* @param IAppConfig $appConfig
|
||||
* @param IConfig $config
|
||||
* @param IShareProvider $defaultProvider
|
||||
* @param ISecureRandom $secureRandom
|
||||
* @param IHasher $hasher
|
||||
* @param IMountManager $mountManager
|
||||
* @param IGroupManager $groupManager
|
||||
* @param IL10N $l
|
||||
*/
|
||||
public function __construct(
|
||||
ILogger $logger,
|
||||
IAppConfig $appConfig,
|
||||
IShareProvider $defaultProvider
|
||||
IConfig $config,
|
||||
IShareProvider $defaultProvider,
|
||||
ISecureRandom $secureRandom,
|
||||
IHasher $hasher,
|
||||
IMountManager $mountManager,
|
||||
IGroupManager $groupManager,
|
||||
IL10N $l
|
||||
) {
|
||||
$this->logger = $logger;
|
||||
$this->appConfig = $appConfig;
|
||||
$this->config = $config;
|
||||
$this->secureRandom = $secureRandom;
|
||||
$this->hasher = $hasher;
|
||||
$this->mountManager = $mountManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->l = $l;
|
||||
|
||||
// TEMP SOLUTION JUST TO GET STARTED
|
||||
$this->defaultProvider = $defaultProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if a password meets all requirements
|
||||
*
|
||||
* @param string $password
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function verifyPassword($password) {
|
||||
if ($password === null) {
|
||||
// No password is set, check if this is allowed.
|
||||
if ($this->shareApiLinkEnforcePassword()) {
|
||||
throw new \InvalidArgumentException('Passwords are enforced for link shares');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Let others verify the password
|
||||
$accepted = true;
|
||||
$message = '';
|
||||
\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
|
||||
'password' => $password,
|
||||
'accepted' => &$accepted,
|
||||
'message' => &$message
|
||||
]);
|
||||
|
||||
if (!$accepted) {
|
||||
throw new \Exception($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for generic requirements before creating a share
|
||||
*
|
||||
* @param IShare $share
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function generalCreateChecks(IShare $share) {
|
||||
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
|
||||
// We expect a valid user as sharedWith for user shares
|
||||
if (!($share->getSharedWith() instanceof \OCP\IUser)) {
|
||||
throw new \InvalidArgumentException('SharedWith should be an IUser');
|
||||
}
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
// We expect a valid group as sharedWith for group shares
|
||||
if (!($share->getSharedWith() instanceof \OCP\IGroup)) {
|
||||
throw new \InvalidArgumentException('SharedWith should be an IGroup');
|
||||
}
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
|
||||
if ($share->getSharedWith() !== null) {
|
||||
throw new \InvalidArgumentException('SharedWith should be empty');
|
||||
}
|
||||
} else {
|
||||
// We can't handle other types yet
|
||||
throw new \InvalidArgumentException('unkown share type');
|
||||
}
|
||||
|
||||
// Verify the initiator of the share is et
|
||||
if ($share->getSharedBy() === null) {
|
||||
throw new \InvalidArgumentException('SharedBy should be set');
|
||||
}
|
||||
|
||||
// Cannot share with yourself
|
||||
if ($share->getSharedWith() === $share->getSharedBy()) {
|
||||
throw new \InvalidArgumentException('Can\'t share with yourself');
|
||||
}
|
||||
|
||||
// The path should be set
|
||||
if ($share->getPath() === null) {
|
||||
throw new \InvalidArgumentException('Path should be set');
|
||||
}
|
||||
// And it should be a file or a folder
|
||||
if (!($share->getPath() instanceof \OCP\Files\File) &&
|
||||
!($share->getPath() instanceof \OCP\Files\Folder)) {
|
||||
throw new \InvalidArgumentException('Path should be either a file or a folder');
|
||||
}
|
||||
|
||||
// Check if we actually have share permissions
|
||||
if (!$share->getPath()->isShareable()) {
|
||||
$message_t = $this->l->t('You are not allowed to share %s', [$share->getPath()->getPath()]);
|
||||
throw new HintException($message_t, $message_t, 404);
|
||||
}
|
||||
|
||||
// Permissions should be set
|
||||
if ($share->getPermissions() === null) {
|
||||
throw new \InvalidArgumentException('A share requires permissions');
|
||||
}
|
||||
|
||||
// Check that we do not share with more permissions than we have
|
||||
if ($share->getPermissions() & ~$share->getPath()->getPermissions()) {
|
||||
$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getPath()->getPath()]);
|
||||
throw new HintException($message_t, $message_t, 404);
|
||||
}
|
||||
|
||||
// Check that read permissions are always set
|
||||
if (($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
|
||||
throw new \InvalidArgumentException('Shares need at least read permissions');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if the expiration date fits the system settings
|
||||
*
|
||||
* @param \DateTime $expireDate The current expiration date (can be null)
|
||||
* @return \DateTime|null The expiration date or null if $expireDate was null and it is not required
|
||||
* @throws \OC\HintException
|
||||
*/
|
||||
protected function validateExpiredate($expireDate) {
|
||||
|
||||
if ($expireDate !== null) {
|
||||
//Make sure the expiration date is a date
|
||||
$expireDate->setTime(0, 0, 0);
|
||||
|
||||
$date = new \DateTime();
|
||||
$date->setTime(0, 0, 0);
|
||||
if ($date >= $expireDate) {
|
||||
$message = $this->l->t('Expiration date is in the past');
|
||||
throw new \OC\HintException($message, $message, 404);
|
||||
}
|
||||
}
|
||||
|
||||
// If we enforce the expiration date check that is does not exceed
|
||||
if ($this->shareApiLinkDefaultExpireDateEnforced()) {
|
||||
if ($expireDate === null) {
|
||||
throw new \InvalidArgumentException('Expiration date is enforced');
|
||||
}
|
||||
|
||||
$date = new \DateTime();
|
||||
$date->setTime(0, 0, 0);
|
||||
$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
|
||||
if ($date < $expireDate) {
|
||||
$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
|
||||
throw new \OC\HintException($message, $message, 404);
|
||||
}
|
||||
|
||||
return $expireDate;
|
||||
}
|
||||
|
||||
// If expiredate is empty set a default one if there is a default
|
||||
if ($expireDate === null && $this->shareApiLinkDefaultExpireDate()) {
|
||||
$date = new \DateTime();
|
||||
$date->setTime(0,0,0);
|
||||
$date->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
|
||||
return $date;
|
||||
}
|
||||
|
||||
return $expireDate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for pre share requirements for use shares
|
||||
*
|
||||
* @param IShare $share
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function userCreateChecks(IShare $share) {
|
||||
// Check if we can share with group members only
|
||||
if ($this->shareWithGroupMembersOnly()) {
|
||||
// Verify we can share with this user
|
||||
$groups = array_intersect(
|
||||
$this->groupManager->getUserGroupIds($share->getSharedBy()),
|
||||
$this->groupManager->getUserGroupIds($share->getSharedWith())
|
||||
);
|
||||
if (empty($groups)) {
|
||||
throw new \Exception('Only sharing with group members is allowed');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Could be costly, fix
|
||||
*
|
||||
* Also this is not what we want in the future.. then we want to squash identical shares.
|
||||
*/
|
||||
$existingShares = $this->defaultProvider->getSharesByPath($share->getPath());
|
||||
foreach($existingShares as $existingShare) {
|
||||
// Identical share already existst
|
||||
if ($existingShare->getSharedWith() === $share->getSharedWith()) {
|
||||
throw new \Exception('Path already shared with this user');
|
||||
}
|
||||
|
||||
// The share is already shared with this user via a group share
|
||||
if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP &&
|
||||
$existingShare->getSharedWith()->inGroup($share->getSharedWith()) &&
|
||||
$existingShare->getShareOwner() !== $share->getShareOwner()) {
|
||||
throw new \Exception('Path already shared with this user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for pre share requirements for group shares
|
||||
*
|
||||
* @param IShare $share
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function groupCreateChecks(IShare $share) {
|
||||
// Verify if the user can share with this group
|
||||
if ($this->shareWithGroupMembersOnly()) {
|
||||
if (!$share->getSharedWith()->inGroup($share->getSharedBy())) {
|
||||
throw new \Exception('Only sharing within your own groups is allowed');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Could be costly, fix
|
||||
*
|
||||
* Also this is not what we want in the future.. then we want to squash identical shares.
|
||||
*/
|
||||
$existingShares = $this->defaultProvider->getSharesByPath($share->getPath());
|
||||
foreach($existingShares as $existingShare) {
|
||||
if ($existingShare->getSharedWith() === $share->getSharedWith()) {
|
||||
throw new \Exception('Path already shared with this group');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for pre share requirements for link shares
|
||||
*
|
||||
* @param IShare $share
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function linkCreateChecks(IShare $share) {
|
||||
// Are link shares allowed?
|
||||
if (!$this->shareApiAllowLinks()) {
|
||||
throw new \Exception('Link sharing not allowed');
|
||||
}
|
||||
|
||||
// Link shares by definition can't have share permissions
|
||||
if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
|
||||
throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
|
||||
}
|
||||
|
||||
// We don't allow deletion on link shares
|
||||
if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
|
||||
throw new \InvalidArgumentException('Link shares can\'t have delete permissions');
|
||||
}
|
||||
|
||||
// Check if public upload is allowed
|
||||
if (!$this->shareApiLinkAllowPublicUpload() &&
|
||||
($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE))) {
|
||||
throw new \InvalidArgumentException('Public upload not allowed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|Folder $path
|
||||
*/
|
||||
protected function pathCreateChecks($path) {
|
||||
// Make sure that we do not share a path that contains a shared mountpoint
|
||||
if ($path instanceof \OCP\Files\Folder) {
|
||||
$mounts = $this->mountManager->findIn($path->getPath());
|
||||
foreach($mounts as $mount) {
|
||||
if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
|
||||
throw new \InvalidArgumentException('Path contains files shared with you');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user that is sharing can actually share
|
||||
*
|
||||
* @param IShare $share
|
||||
* @return bool
|
||||
*/
|
||||
protected function canShare(IShare $share) {
|
||||
if (!$this->shareApiEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isSharingDisabledForUser($share->getSharedBy())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Share a path
|
||||
*
|
||||
* @param Share $share
|
||||
* @param IShare $share
|
||||
* @return Share The share object
|
||||
* @throws \Exception
|
||||
*
|
||||
* TODO: handle link share permissions or check them
|
||||
*/
|
||||
public function createShare(Share $share) {
|
||||
public function createShare(IShare $share) {
|
||||
if (!$this->canShare($share)) {
|
||||
throw new \Exception('The Share API is disabled');
|
||||
}
|
||||
|
||||
$this->generalCreateChecks($share);
|
||||
|
||||
//Verify share type
|
||||
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
|
||||
$this->userCreateChecks($share);
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
|
||||
$this->groupCreateChecks($share);
|
||||
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
|
||||
$this->linkCreateChecks($share);
|
||||
|
||||
/*
|
||||
* For now ignore a set token.
|
||||
*/
|
||||
$share->setToken(
|
||||
$this->secureRandom->generate(
|
||||
\OC\Share\Constants::TOKEN_LENGTH,
|
||||
\OCP\Security\ISecureRandom::CHAR_LOWER.
|
||||
\OCP\Security\ISecureRandom::CHAR_UPPER.
|
||||
\OCP\Security\ISecureRandom::CHAR_DIGITS
|
||||
)
|
||||
);
|
||||
|
||||
//Verify the expiration date
|
||||
$share->setExpirationDate($this->validateExpiredate($share->getExpirationDate()));
|
||||
|
||||
//Verify the password
|
||||
$this->verifyPassword($share->getPassword());
|
||||
|
||||
// If a password is set. Hash it!
|
||||
if ($share->getPassword() !== null) {
|
||||
$share->setPassword($this->hasher->hash($share->getPassword()));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify if there are any issues with the path
|
||||
$this->pathCreateChecks($share->getPath());
|
||||
|
||||
// On creation of a share the owner is always the owner of the path
|
||||
$share->setShareOwner($share->getPath()->getOwner());
|
||||
|
||||
// Generate the target
|
||||
$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getPath()->getName();
|
||||
$target = \OC\Files\Filesystem::normalizePath($target);
|
||||
$share->setTarget($target);
|
||||
|
||||
// Pre share hook
|
||||
$run = true;
|
||||
$error = '';
|
||||
$preHookData = [
|
||||
'itemType' => $share->getPath() instanceof \OCP\Files\File ? 'file' : 'folder',
|
||||
'itemSource' => $share->getPath()->getId(),
|
||||
'shareType' => $share->getShareType(),
|
||||
'uidOwner' => $share->getSharedBy()->getUID(),
|
||||
'permissions' => $share->getPermissions(),
|
||||
'fileSource' => $share->getPath()->getId(),
|
||||
'expiration' => $share->getExpirationDate(),
|
||||
'token' => $share->getToken(),
|
||||
'run' => &$run,
|
||||
'error' => &$error
|
||||
];
|
||||
\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
|
||||
|
||||
if ($run === false) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
$share = $this->defaultProvider->create($share);
|
||||
|
||||
// Post share hook
|
||||
$postHookData = [
|
||||
'itemType' => $share->getPath() instanceof \OCP\Files\File ? 'file' : 'folder',
|
||||
'itemSource' => $share->getPath()->getId(),
|
||||
'shareType' => $share->getShareType(),
|
||||
'uidOwner' => $share->getSharedBy()->getUID(),
|
||||
'permissions' => $share->getPermissions(),
|
||||
'fileSource' => $share->getPath()->getId(),
|
||||
'expiration' => $share->getExpirationDate(),
|
||||
'token' => $share->getToken(),
|
||||
'id' => $share->getId(),
|
||||
];
|
||||
\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
|
||||
|
||||
return $share;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,4 +656,115 @@ class Manager {
|
|||
*/
|
||||
public function getAccessList(\OCP\Files\Node $path) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new share
|
||||
* @return IShare;
|
||||
*/
|
||||
public function newShare() {
|
||||
return new \OC\Share20\Share();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the share API enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiEnabled() {
|
||||
return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is public link sharing enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiAllowLinks() {
|
||||
return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is password on public link requires
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiLinkEnforcePassword() {
|
||||
return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is default expire date enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiLinkDefaultExpireDate() {
|
||||
return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is default expire date enforced
|
||||
*`
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiLinkDefaultExpireDateEnforced() {
|
||||
return $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of default expire days
|
||||
*shareApiLinkAllowPublicUpload
|
||||
* @return int
|
||||
*/
|
||||
public function shareApiLinkDefaultExpireDays() {
|
||||
return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow public upload on link shares
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shareApiLinkAllowPublicUpload() {
|
||||
return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
|
||||
}
|
||||
|
||||
/**
|
||||
* check if user can only share with group members
|
||||
* @return bool
|
||||
*/
|
||||
public function shareWithGroupMembersOnly() {
|
||||
return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copied from \OC_Util::isSharingDisabledForUser
|
||||
*
|
||||
* TODO: Deprecate fuction from OC_Util
|
||||
*
|
||||
* @param IUser $user
|
||||
* @return bool
|
||||
*/
|
||||
public function isSharingDisabledForUser($user) {
|
||||
if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
|
||||
$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
|
||||
$excludedGroups = json_decode($groupsList);
|
||||
if (is_null($excludedGroups)) {
|
||||
$excludedGroups = explode(',', $groupsList);
|
||||
$newValue = json_encode($excludedGroups);
|
||||
$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
|
||||
}
|
||||
$usersGroups = $this->groupManager->getUserGroupIds($user);
|
||||
if (!empty($usersGroups)) {
|
||||
$remainingGroups = array_diff($usersGroups, $excludedGroups);
|
||||
// if the user is only in groups which are disabled for sharing then
|
||||
// sharing is also disabled for the user
|
||||
if (empty($remainingGroups)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ class Share implements IShare {
|
|||
* @param \DateTime $expireDate
|
||||
* @return Share The modified object
|
||||
*/
|
||||
public function setExpirationDate(\DateTime $expireDate) {
|
||||
public function setExpirationDate($expireDate) {
|
||||
//TODO checks
|
||||
|
||||
$this->expireDate = $expireDate;
|
||||
|
|
|
@ -26,6 +26,12 @@ use OCP\IGroupManager;
|
|||
use OCP\Files\IRootFolder;
|
||||
use OC\Share20\DefaultShareProvider;
|
||||
|
||||
/**
|
||||
* Class DefaultShareProviderTest
|
||||
*
|
||||
* @package Test\Share20
|
||||
* @group DB
|
||||
*/
|
||||
class DefaultShareProviderTest extends \Test\TestCase {
|
||||
|
||||
/** @var IDBConnection */
|
||||
|
@ -533,4 +539,186 @@ class DefaultShareProviderTest extends \Test\TestCase {
|
|||
$this->assertEquals(null, $children[1]->getExpirationDate());
|
||||
$this->assertEquals('myTarget2', $children[1]->getTarget());
|
||||
}
|
||||
|
||||
public function testCreateUserShare() {
|
||||
$share = new \OC\Share20\Share();
|
||||
|
||||
$sharedWith = $this->getMock('OCP\IUser');
|
||||
$sharedWith->method('getUID')->willReturn('sharedWith');
|
||||
$sharedBy = $this->getMock('OCP\IUser');
|
||||
$sharedBy->method('getUID')->willReturn('sharedBy');
|
||||
$shareOwner = $this->getMock('OCP\IUser');
|
||||
$shareOwner->method('getUID')->WillReturn('shareOwner');
|
||||
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->will($this->returnValueMap([
|
||||
['sharedWith', $sharedWith],
|
||||
['sharedBy', $sharedBy],
|
||||
['shareOwner', $shareOwner],
|
||||
]));
|
||||
|
||||
$path = $this->getMock('\OCP\Files\File');
|
||||
$path->method('getId')->willReturn(100);
|
||||
$path->method('getOwner')->willReturn($shareOwner);
|
||||
|
||||
$ownerFolder = $this->getMock('OCP\Files\Folder');
|
||||
$userFolder = $this->getMock('OCP\Files\Folder');
|
||||
$this->rootFolder
|
||||
->method('getUserFolder')
|
||||
->will($this->returnValueMap([
|
||||
['sharedBy', $userFolder],
|
||||
['shareOwner', $ownerFolder],
|
||||
]));
|
||||
|
||||
$userFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
$ownerFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
|
||||
$share->setShareType(\OCP\Share::SHARE_TYPE_USER);
|
||||
$share->setSharedWith($sharedWith);
|
||||
$share->setSharedBy($sharedBy);
|
||||
$share->setShareOwner($shareOwner);
|
||||
$share->setPath($path);
|
||||
$share->setPermissions(1);
|
||||
$share->setTarget('/target');
|
||||
|
||||
$share2 = $this->provider->create($share);
|
||||
|
||||
$this->assertNotNull($share2->getId());
|
||||
$this->assertSame(\OCP\Share::SHARE_TYPE_USER, $share2->getShareType());
|
||||
$this->assertSame($sharedWith, $share2->getSharedWith());
|
||||
$this->assertSame($sharedBy, $share2->getSharedBy());
|
||||
$this->assertSame($shareOwner, $share2->getShareOwner());
|
||||
$this->assertSame(1, $share2->getPermissions());
|
||||
$this->assertSame('/target', $share2->getTarget());
|
||||
$this->assertLessThanOrEqual(time(), $share2->getSharetime());
|
||||
$this->assertSame($path, $share2->getPath());
|
||||
}
|
||||
|
||||
public function testCreateGroupShare() {
|
||||
$share = new \OC\Share20\Share();
|
||||
|
||||
$sharedWith = $this->getMock('OCP\IGroup');
|
||||
$sharedWith->method('getGID')->willReturn('sharedWith');
|
||||
$sharedBy = $this->getMock('OCP\IUser');
|
||||
$sharedBy->method('getUID')->willReturn('sharedBy');
|
||||
$shareOwner = $this->getMock('OCP\IUser');
|
||||
$shareOwner->method('getUID')->WillReturn('shareOwner');
|
||||
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->will($this->returnValueMap([
|
||||
['sharedBy', $sharedBy],
|
||||
['shareOwner', $shareOwner],
|
||||
]));
|
||||
$this->groupManager
|
||||
->method('get')
|
||||
->with('sharedWith')
|
||||
->willReturn($sharedWith);
|
||||
|
||||
$path = $this->getMock('\OCP\Files\Folder');
|
||||
$path->method('getId')->willReturn(100);
|
||||
$path->method('getOwner')->willReturn($shareOwner);
|
||||
|
||||
$ownerFolder = $this->getMock('OCP\Files\Folder');
|
||||
$userFolder = $this->getMock('OCP\Files\Folder');
|
||||
$this->rootFolder
|
||||
->method('getUserFolder')
|
||||
->will($this->returnValueMap([
|
||||
['sharedBy', $userFolder],
|
||||
['shareOwner', $ownerFolder],
|
||||
]));
|
||||
|
||||
$userFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
$ownerFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
|
||||
$share->setShareType(\OCP\Share::SHARE_TYPE_GROUP);
|
||||
$share->setSharedWith($sharedWith);
|
||||
$share->setSharedBy($sharedBy);
|
||||
$share->setShareOwner($shareOwner);
|
||||
$share->setPath($path);
|
||||
$share->setPermissions(1);
|
||||
$share->setTarget('/target');
|
||||
|
||||
$share2 = $this->provider->create($share);
|
||||
|
||||
$this->assertNotNull($share2->getId());
|
||||
$this->assertSame(\OCP\Share::SHARE_TYPE_GROUP, $share2->getShareType());
|
||||
$this->assertSame($sharedWith, $share2->getSharedWith());
|
||||
$this->assertSame($sharedBy, $share2->getSharedBy());
|
||||
$this->assertSame($shareOwner, $share2->getShareOwner());
|
||||
$this->assertSame(1, $share2->getPermissions());
|
||||
$this->assertSame('/target', $share2->getTarget());
|
||||
$this->assertLessThanOrEqual(time(), $share2->getSharetime());
|
||||
$this->assertSame($path, $share2->getPath());
|
||||
}
|
||||
|
||||
public function testCreateLinkShare() {
|
||||
$share = new \OC\Share20\Share();
|
||||
|
||||
$sharedBy = $this->getMock('OCP\IUser');
|
||||
$sharedBy->method('getUID')->willReturn('sharedBy');
|
||||
$shareOwner = $this->getMock('OCP\IUser');
|
||||
$shareOwner->method('getUID')->WillReturn('shareOwner');
|
||||
|
||||
$this->userManager
|
||||
->method('get')
|
||||
->will($this->returnValueMap([
|
||||
['sharedBy', $sharedBy],
|
||||
['shareOwner', $shareOwner],
|
||||
]));
|
||||
|
||||
$path = $this->getMock('\OCP\Files\Folder');
|
||||
$path->method('getId')->willReturn(100);
|
||||
$path->method('getOwner')->willReturn($shareOwner);
|
||||
|
||||
$ownerFolder = $this->getMock('OCP\Files\Folder');
|
||||
$userFolder = $this->getMock('OCP\Files\Folder');
|
||||
$this->rootFolder
|
||||
->method('getUserFolder')
|
||||
->will($this->returnValueMap([
|
||||
['sharedBy', $userFolder],
|
||||
['shareOwner', $ownerFolder],
|
||||
]));
|
||||
|
||||
$userFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
$ownerFolder->method('getById')
|
||||
->with(100)
|
||||
->willReturn([$path]);
|
||||
|
||||
$share->setShareType(\OCP\Share::SHARE_TYPE_LINK);
|
||||
$share->setSharedBy($sharedBy);
|
||||
$share->setShareOwner($shareOwner);
|
||||
$share->setPath($path);
|
||||
$share->setPermissions(1);
|
||||
$share->setPassword('password');
|
||||
$share->setToken('token');
|
||||
$expireDate = new \DateTime();
|
||||
$share->setExpirationDate($expireDate);
|
||||
$share->setTarget('/target');
|
||||
|
||||
$share2 = $this->provider->create($share);
|
||||
|
||||
$this->assertNotNull($share2->getId());
|
||||
$this->assertSame(\OCP\Share::SHARE_TYPE_LINK, $share2->getShareType());
|
||||
$this->assertSame($sharedBy, $share2->getSharedBy());
|
||||
$this->assertSame($shareOwner, $share2->getShareOwner());
|
||||
$this->assertSame(1, $share2->getPermissions());
|
||||
$this->assertSame('/target', $share2->getTarget());
|
||||
$this->assertLessThanOrEqual(time(), $share2->getSharetime());
|
||||
$this->assertSame($path, $share2->getPath());
|
||||
$this->assertSame('password', $share2->getPassword());
|
||||
$this->assertSame('token', $share2->getToken());
|
||||
$this->assertEquals($expireDate, $share2->getExpirationDate());
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,7 @@
|
|||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version = array(9, 0, 0, 5);
|
||||
$OC_Version = array(9, 0, 0, 6);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '9.0 pre alpha';
|
||||
|
|
Loading…
Reference in New Issue