diff options
52 files changed, 989 insertions, 139 deletions
diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 415ec2a9be5..9475dc53fc0 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -172,7 +172,7 @@ this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 28); } this.delegateEvents(); - this.$el.find('textarea').on('keyup input change', this._onTypeComment); + this.$el.find('textarea').on('keydown input change', this._onTypeComment); }, _formatItem: function(commentModel) { @@ -273,7 +273,7 @@ // spawn form $comment.after($formRow); $formRow.data('commentEl', $comment); - $formRow.find('textarea').on('keyup input change', this._onTypeComment); + $formRow.find('textarea').on('keydown input change', this._onTypeComment); // copy avatar element from original to avoid flickering $formRow.find('.avatar').replaceWith($comment.find('.avatar').clone()); @@ -301,6 +301,11 @@ var limitExceeded = (len > this._commentMaxLength); $field.toggleClass('error', limitExceeded); $submitButton.prop('disabled', limitExceeded); + + //submits form on ctrl+Enter or cmd+Enter + if (ev.keyCode === 13 && (ev.ctrlKey || ev.metaKey)) { + $submitButton.click(); + } }, _onClickComment: function(ev) { diff --git a/apps/dav/appinfo/application.php b/apps/dav/appinfo/application.php index c3811a40845..328f86c877f 100644 --- a/apps/dav/appinfo/application.php +++ b/apps/dav/appinfo/application.php @@ -153,11 +153,6 @@ class Application extends App { return $this->getContainer()->query('SyncService'); } - public function setupCron() { - $jl = $this->getContainer()->getServer()->getJobList(); - $jl->add(new SyncJob()); - } - public function generateBirthdays() { try { /** @var BirthdayService $migration */ diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index bb447c9a426..e2688e2f923 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -16,4 +16,7 @@ <dependencies> <owncloud min-version="9.1" max-version="9.1" /> </dependencies> + <background-jobs> + <job>OCA\DAV\CardDAV\Sync\SyncJob</job> + </background-jobs> </info> diff --git a/apps/dav/appinfo/install.php b/apps/dav/appinfo/install.php index fbd41d25f49..dbb23022b38 100644 --- a/apps/dav/appinfo/install.php +++ b/apps/dav/appinfo/install.php @@ -22,5 +22,4 @@ use OCA\Dav\AppInfo\Application; $app = new Application(); -$app->setupCron(); $app->generateBirthdays(); diff --git a/apps/dav/appinfo/update.php b/apps/dav/appinfo/update.php index fbd41d25f49..dbb23022b38 100644 --- a/apps/dav/appinfo/update.php +++ b/apps/dav/appinfo/update.php @@ -22,5 +22,4 @@ use OCA\Dav\AppInfo\Application; $app = new Application(); -$app->setupCron(); $app->generateBirthdays(); diff --git a/apps/dav/lib/connector/sabre/principal.php b/apps/dav/lib/connector/sabre/principal.php index 18f28a916f1..787bcdf469b 100644 --- a/apps/dav/lib/connector/sabre/principal.php +++ b/apps/dav/lib/connector/sabre/principal.php @@ -196,6 +196,14 @@ class Principal implements BackendInterface { * @return string */ function findByUri($uri, $principalPrefix) { + if (substr($uri, 0, 7) === 'mailto:') { + $email = substr($uri, 7); + $users = $this->userManager->getByEmail($email); + if (count($users) === 1) { + return $this->principalPrefix . '/' . $users[0]->getUID(); + } + } + return ''; } diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index 73e24c9a292..edaa7ac8552 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -84,6 +84,9 @@ class Server { // acl $acl = new DavAclPlugin(); + $acl->principalCollectionSet = [ + 'principals/users', 'principals/groups' + ]; $acl->defaultUsernamePath = 'principals/users'; $this->server->addPlugin($acl); diff --git a/apps/dav/tests/unit/connector/sabre/principal.php b/apps/dav/tests/unit/connector/sabre/principal.php index 1747885240a..75076e9618b 100644 --- a/apps/dav/tests/unit/connector/sabre/principal.php +++ b/apps/dav/tests/unit/connector/sabre/principal.php @@ -255,4 +255,19 @@ class Principal extends TestCase { public function testSearchPrincipals() { $this->assertSame([], $this->connector->searchPrincipals('principals/users', [])); } + + public function testFindByUri() { + $fooUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $fooUser + ->expects($this->exactly(1)) + ->method('getUID') + ->will($this->returnValue('foo')); + + $this->userManager->expects($this->once())->method('getByEmail')->willReturn([ + $fooUser + ]); + $ret = $this->connector->findByUri('mailto:foo@bar.net', 'principals/users'); + $this->assertSame('principals/users/foo', $ret); + } } diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php index 68098530017..af762845326 100644 --- a/apps/files_sharing/api/share20ocs.php +++ b/apps/files_sharing/api/share20ocs.php @@ -28,11 +28,12 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\Files\IRootFolder; +use OCP\Lock\LockedException; use OCP\Share; use OCP\Share\IManager; - use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\Exceptions\GenericShareException; +use OCP\Lock\ILockingProvider; /** * Class Share20OCS @@ -205,12 +206,21 @@ class Share20OCS { return new \OC_OCS_Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); } + try { + $share->getNode()->lock(ILockingProvider::LOCK_SHARED); + } catch (LockedException $e) { + return new \OC_OCS_Result(null, 404, 'could not delete share'); + } + if (!$this->canAccessShare($share)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Could not delete share')); } $this->shareManager->deleteShare($share); + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); + return new \OC_OCS_Result(); } @@ -233,12 +243,18 @@ class Share20OCS { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); try { $path = $userFolder->get($path); - } catch (\OCP\Files\NotFoundException $e) { + } catch (NotFoundException $e) { return new \OC_OCS_Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist')); } $share->setNode($path); + try { + $share->getNode()->lock(ILockingProvider::LOCK_SHARED); + } catch (LockedException $e) { + return new \OC_OCS_Result(null, 404, 'Could not create share'); + } + // Parse permissions (if available) $permissions = $this->request->getParam('permissions', null); if ($permissions === null) { @@ -248,6 +264,7 @@ class Share20OCS { } if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, 'invalid permissions'); } @@ -275,17 +292,20 @@ class Share20OCS { if ($shareType === \OCP\Share::SHARE_TYPE_USER) { // Valid user is required to share if ($shareWith === null || !$this->userManager->userExists($shareWith)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Please specify a valid user')); } $share->setSharedWith($shareWith); $share->setPermissions($permissions); } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { if (!$this->shareManager->allowGroupSharing()) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Group sharing is disabled by the administrator')); } // Valid group is required to share if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Please specify a valid group')); } $share->setSharedWith($shareWith); @@ -293,6 +313,7 @@ class Share20OCS { } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { //Can we even share links? if (!$this->shareManager->shareApiAllowLinks()) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Public link sharing is disabled by the administrator')); } @@ -302,6 +323,7 @@ class Share20OCS { */ $existingShares = $this->shareManager->getSharesBy($this->currentUser->getUID(), \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0); if (!empty($existingShares)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result($this->formatShare($existingShares[0])); } @@ -309,11 +331,13 @@ class Share20OCS { if ($publicUpload === 'true') { // Check if public upload is allowed if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 403, $this->l->t('Public upload disabled by the administrator')); } // Public upload can only be set for folders if ($path instanceof \OCP\Files\File) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Public upload is only possible for publicly shared folders')); } @@ -341,18 +365,21 @@ class Share20OCS { $expireDate = $this->parseDate($expireDate); $share->setExpirationDate($expireDate); } catch (\Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Invalid date, date format must be YYYY-MM-DD')); } } } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 403, $this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType])); } $share->setSharedWith($shareWith); $share->setPermissions($permissions); } else { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $this->l->t('Unknown share type')); } @@ -363,13 +390,18 @@ class Share20OCS { $share = $this->shareManager->createShare($share); } catch (GenericShareException $e) { $code = $e->getCode() === 0 ? 403 : $e->getCode(); + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, $code, $e->getHint()); }catch (\Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 403, $e->getMessage()); } - $share = $this->formatShare($share); - return new \OC_OCS_Result($share); + $output = $this->formatShare($share); + + $share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); + + return new \OC_OCS_Result($output); } /** @@ -454,17 +486,28 @@ class Share20OCS { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); try { $path = $userFolder->get($path); + $path->lock(ILockingProvider::LOCK_SHARED); } catch (\OCP\Files\NotFoundException $e) { return new \OC_OCS_Result(null, 404, $this->l->t('Wrong path, file/folder doesn\'t exist')); + } catch (LockedException $e) { + return new \OC_OCS_Result(null, 404, $this->l->t('Could not lock path')); } } if ($sharedWithMe === 'true') { - return $this->getSharedWithMe($path); + $result = $this->getSharedWithMe($path); + if ($path !== null) { + $path->unlock(ILockingProvider::LOCK_SHARED); + } + return $result; } if ($subfiles === 'true') { - return $this->getSharesInDir($path); + $result = $this->getSharesInDir($path); + if ($path !== null) { + $path->unlock(ILockingProvider::LOCK_SHARED); + } + return $result; } if ($reshares === 'true') { @@ -494,6 +537,10 @@ class Share20OCS { } } + if ($path !== null) { + $path->unlock(ILockingProvider::LOCK_SHARED); + } + return new \OC_OCS_Result($formatted); } @@ -512,7 +559,10 @@ class Share20OCS { return new \OC_OCS_Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); } + $share->getNode()->lock(\OCP\Lock\ILockingProvider::LOCK_SHARED); + if (!$this->canAccessShare($share)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Wrong share ID, share doesn\'t exist')); } @@ -526,6 +576,7 @@ class Share20OCS { */ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, 'Wrong or no update parameter given'); } @@ -543,15 +594,18 @@ class Share20OCS { if ($newPermissions !== null && $newPermissions !== \OCP\Constants::PERMISSION_READ && $newPermissions !== (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $this->l->t('Can\'t change permissions for public share links')); } if ($newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)) { if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 403, $this->l->t('Public upload disabled by the administrator')); } if (!($share->getNode() instanceof \OCP\Files\Folder)) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $this->l->t('Public upload is only possible for publicly shared folders')); } } @@ -566,6 +620,7 @@ class Share20OCS { try { $expireDate = $this->parseDate($expireDate); } catch (\Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $e->getMessage()); } $share->setExpirationDate($expireDate); @@ -580,6 +635,7 @@ class Share20OCS { } else { // For other shares only permissions is valid. if ($permissions === null) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $this->l->t('Wrong or no update parameter given')); } else { $permissions = (int)$permissions; @@ -599,6 +655,7 @@ class Share20OCS { } if ($share->getPermissions() & ~$maxPermissions) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 404, $this->l->t('Cannot increase permissions')); } } @@ -608,9 +665,12 @@ class Share20OCS { try { $share = $this->shareManager->updateShare($share); } catch (\Exception $e) { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new \OC_OCS_Result(null, 400, $e->getMessage()); } + $share->getNode()->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED); + return new \OC_OCS_Result($this->formatShare($share)); } diff --git a/apps/files_sharing/tests/api/share20ocstest.php b/apps/files_sharing/tests/api/share20ocstest.php index 56c350aa99a..ffb74da2af7 100644 --- a/apps/files_sharing/tests/api/share20ocstest.php +++ b/apps/files_sharing/tests/api/share20ocstest.php @@ -29,6 +29,7 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\Files\IRootFolder; +use OCP\Lock\LockedException; /** * Class Share20OCSTest @@ -137,8 +138,11 @@ class Share20OCSTest extends \Test\TestCase { } public function testDeleteShare() { + $node = $this->getMock('\OCP\Files\File'); + $share = $this->newShare(); - $share->setSharedBy($this->currentUser->getUID()); + $share->setSharedBy($this->currentUser->getUID()) + ->setNode($node); $this->shareManager ->expects($this->once()) ->method('getShareById') @@ -149,10 +153,45 @@ class Share20OCSTest extends \Test\TestCase { ->method('deleteShare') ->with($share); + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $node->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $expected = new \OC_OCS_Result(); $this->assertEquals($expected, $this->ocs->deleteShare(42)); } + public function testDeleteShareLocked() { + $node = $this->getMock('\OCP\Files\File'); + + $share = $this->newShare(); + $share->setSharedBy($this->currentUser->getUID()) + ->setNode($node); + $this->shareManager + ->expects($this->once()) + ->method('getShareById') + ->with('ocinternal:42') + ->willReturn($share); + $this->shareManager + ->expects($this->never()) + ->method('deleteShare') + ->with($share); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED) + ->will($this->throwException(new LockedException('mypath'))); + $node->expects($this->never()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + + $expected = new \OC_OCS_Result(null, 404, 'could not delete share'); + $this->assertEquals($expected, $this->ocs->deleteShare(42)); + } + /* * FIXME: Enable once we have a federated Share Provider @@ -526,7 +565,7 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareInvalidPermissions() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); $this->request @@ -548,6 +587,10 @@ class Share20OCSTest extends \Test\TestCase { ->with('valid-path') ->willReturn($path); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $expected = new \OC_OCS_Result(null, 404, 'invalid permissions'); $result = $this->ocs->createShare(); @@ -557,7 +600,7 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareUserNoShareWith() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); $this->request @@ -585,6 +628,10 @@ class Share20OCSTest extends \Test\TestCase { ->with('valid-path') ->willReturn($path); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $expected = new \OC_OCS_Result(null, 404, 'Please specify a valid user'); $result = $this->ocs->createShare(); @@ -594,7 +641,7 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareUserNoValidShareWith() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); $this->request @@ -625,6 +672,10 @@ class Share20OCSTest extends \Test\TestCase { $expected = new \OC_OCS_Result(null, 404, 'Please specify a valid user'); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $result = $this->ocs->createShare(); $this->assertEquals($expected->getMeta(), $result->getMeta()); @@ -632,9 +683,8 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareUser() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); - $this->shareManager->method('createShare')->will($this->returnArgument(0)); $ocs = $this->getMockBuilder('OCA\Files_Sharing\API\Share20OCS') ->setConstructorArgs([ @@ -677,15 +727,26 @@ class Share20OCSTest extends \Test\TestCase { $this->userManager->method('userExists')->with('validUser')->willReturn(true); - $share->method('setNode')->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('validUser'); - $share->method('setSharedBy')->with('currentUser'); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $path->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + + $this->shareManager->method('createShare') + ->with($this->callback(function (\OCP\Share\IShare $share) use ($path) { + return $share->getNode() === $path && + $share->getPermissions() === ( + \OCP\Constants::PERMISSION_ALL & + ~\OCP\Constants::PERMISSION_DELETE & + ~\OCP\Constants::PERMISSION_CREATE + ) && + $share->getShareType() === \OCP\Share::SHARE_TYPE_USER && + $share->getSharedWith() === 'validUser' && + $share->getSharedBy() === 'currentUser'; + })) + ->will($this->returnArgument(0)); $expected = new \OC_OCS_Result(); $result = $ocs->createShare(); @@ -695,7 +756,7 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareGroupNoValidShareWith() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); $this->shareManager->method('createShare')->will($this->returnArgument(0)); @@ -727,6 +788,10 @@ class Share20OCSTest extends \Test\TestCase { $expected = new \OC_OCS_Result(null, 404, 'Please specify a valid user'); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $result = $this->ocs->createShare(); $this->assertEquals($expected->getMeta(), $result->getMeta()); @@ -734,9 +799,8 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareGroup() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); - $this->shareManager->method('createShare')->will($this->returnArgument(0)); $ocs = $this->getMockBuilder('OCA\Files_Sharing\API\Share20OCS') ->setConstructorArgs([ @@ -783,11 +847,22 @@ class Share20OCSTest extends \Test\TestCase { ->method('allowGroupSharing') ->willReturn(true); - $share->method('setNode')->with($path); - $share->method('setPermissions')->with(\OCP\Constants::PERMISSION_ALL); - $share->method('setShareType')->with(\OCP\Share::SHARE_TYPE_GROUP); - $share->method('setSharedWith')->with('validGroup'); - $share->method('setSharedBy')->with('currentUser'); + $path->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $path->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + + $this->shareManager->method('createShare') + ->with($this->callback(function (\OCP\Share\IShare $share) use ($path) { + return $share->getNode() === $path && + $share->getPermissions() === \OCP\Constants::PERMISSION_ALL && + $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && + $share->getSharedWith() === 'validGroup' && + $share->getSharedBy() === 'currentUser'; + })) + ->will($this->returnArgument(0)); $expected = new \OC_OCS_Result(); $result = $ocs->createShare(); @@ -797,7 +872,7 @@ class Share20OCSTest extends \Test\TestCase { } public function testCreateShareGroupNotAllowed() { - $share = $this->getMock('\OCP\Share\IShare'); + $share = $this->newShare(); $this->shareManager->method('newShare')->willReturn($share); $this->request @@ -832,9 +907,8 @@ class Share20OCSTest extends \Test\TestCase { ->method('allowGroupSharing') ->willReturn(false); - $share->method('setNode')->with($path); - $expected = new \OC_OCS_Result(null, 404, 'Group sharing is disabled by the administrator'); + $result = $this->ocs->createShare(); $this->assertEquals($expected->getMeta(), $result->getMeta()); @@ -1154,7 +1228,13 @@ class Share20OCSTest extends \Test\TestCase { } public function testUpdateShareCantAccess() { - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\Folder'); + $share = $this->newShare(); + $share->setNode($node); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); @@ -1166,10 +1246,16 @@ class Share20OCSTest extends \Test\TestCase { } public function testUpdateNoParametersLink() { - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\Folder'); + $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) ->setSharedBy($this->currentUser->getUID()) - ->setShareType(\OCP\Share::SHARE_TYPE_LINK); + ->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setNode($node); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); @@ -1181,10 +1267,16 @@ class Share20OCSTest extends \Test\TestCase { } public function testUpdateNoParametersOther() { - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\Folder'); + $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) ->setSharedBy($this->currentUser->getUID()) - ->setShareType(\OCP\Share::SHARE_TYPE_GROUP); + ->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setNode($node); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); @@ -1198,13 +1290,22 @@ class Share20OCSTest extends \Test\TestCase { public function testUpdateLinkShareClear() { $ocs = $this->mockFormatShare(); - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\Folder'); + $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) ->setSharedBy($this->currentUser->getUID()) ->setShareType(\OCP\Share::SHARE_TYPE_LINK) ->setPassword('password') ->setExpirationDate(new \DateTime()) - ->setPermissions(\OCP\Constants::PERMISSION_ALL); + ->setPermissions(\OCP\Constants::PERMISSION_ALL) + ->setNode($node); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $node->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); $this->request ->method('getParam') @@ -1364,13 +1465,22 @@ class Share20OCSTest extends \Test\TestCase { $date = new \DateTime('2000-01-01'); $date->setTime(0,0,0); - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\File'); + $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) ->setSharedBy($this->currentUser->getUID()) ->setShareType(\OCP\Share::SHARE_TYPE_LINK) ->setPassword('password') ->setExpirationDate($date) - ->setPermissions(\OCP\Constants::PERMISSION_ALL); + ->setPermissions(\OCP\Constants::PERMISSION_ALL) + ->setNode($node); + + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $node->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); $this->request ->method('getParam') @@ -1398,13 +1508,15 @@ class Share20OCSTest extends \Test\TestCase { public function testUpdateLinkShareExpireDateDoesNotChangeOther() { $ocs = $this->mockFormatShare(); - $share = \OC::$server->getShareManager()->newShare(); + $node = $this->getMock('\OCP\Files\File'); + $share = $this->newShare(); $share->setPermissions(\OCP\Constants::PERMISSION_ALL) ->setSharedBy($this->currentUser->getUID()) ->setShareType(\OCP\Share::SHARE_TYPE_LINK) ->setPassword('password') ->setExpirationDate(new \DateTime()) - ->setPermissions(\OCP\Constants::PERMISSION_ALL); + ->setPermissions(\OCP\Constants::PERMISSION_ALL) + ->setNode($node); $this->request ->method('getParam') @@ -1412,6 +1524,13 @@ class Share20OCSTest extends \Test\TestCase { ['expireDate', null, '2010-12-23'], ])); + $node->expects($this->once()) + ->method('lock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $node->expects($this->once()) + ->method('unlock') + ->with(\OCP\Lock\ILockingProvider::LOCK_SHARED); + $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); $this->shareManager->expects($this->once())->method('updateShare')->with( diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index 08411856e7e..00a362864e5 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -26,10 +26,13 @@ namespace OCA\Provisioning_API\AppInfo; +use OCA\Provisioning_API\Apps; +use OCA\Provisioning_API\Groups; +use OCA\Provisioning_API\Users; use OCP\API; // Users -$users = new \OCA\Provisioning_API\Users( +$users = new Users( \OC::$server->getUserManager(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), @@ -41,6 +44,8 @@ API::register('post', '/cloud/users', [$users, 'addUser'], 'provisioning_api', A API::register('get', '/cloud/users/{userid}', [$users, 'getUser'], 'provisioning_api', API::USER_AUTH); API::register('put', '/cloud/users/{userid}', [$users, 'editUser'], 'provisioning_api', API::USER_AUTH); API::register('delete', '/cloud/users/{userid}', [$users, 'deleteUser'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('put', '/cloud/users/{userid}/enable', [$users, 'enableUser'], 'provisioning_api', API::SUBADMIN_AUTH); +API::register('put', '/cloud/users/{userid}/disable', [$users, 'disableUser'], 'provisioning_api', API::SUBADMIN_AUTH); API::register('get', '/cloud/users/{userid}/groups', [$users, 'getUsersGroups'], 'provisioning_api', API::USER_AUTH); API::register('post', '/cloud/users/{userid}/groups', [$users, 'addToGroup'], 'provisioning_api', API::SUBADMIN_AUTH); API::register('delete', '/cloud/users/{userid}/groups', [$users, 'removeFromGroup'], 'provisioning_api', API::SUBADMIN_AUTH); @@ -49,7 +54,7 @@ API::register('delete', '/cloud/users/{userid}/subadmins', [$users, 'removeSubAd API::register('get', '/cloud/users/{userid}/subadmins', [$users, 'getUserSubAdminGroups'], 'provisioning_api', API::ADMIN_AUTH); // Groups -$groups = new \OCA\Provisioning_API\Groups( +$groups = new Groups( \OC::$server->getGroupManager(), \OC::$server->getUserSession(), \OC::$server->getRequest() @@ -61,7 +66,7 @@ API::register('delete', '/cloud/groups/{groupid}', [$groups, 'deleteGroup'], 'pr API::register('get', '/cloud/groups/{groupid}/subadmins', [$groups, 'getSubAdminsOfGroup'], 'provisioning_api', API::ADMIN_AUTH); // Apps -$apps = new \OCA\Provisioning_API\Apps( +$apps = new Apps( \OC::$server->getAppManager(), \OC::$server->getOcsClient() ); diff --git a/apps/provisioning_api/lib/users.php b/apps/provisioning_api/lib/users.php index 68c89e41f6f..2749372c393 100644 --- a/apps/provisioning_api/lib/users.php +++ b/apps/provisioning_api/lib/users.php @@ -31,32 +31,36 @@ namespace OCA\Provisioning_API; use \OC_OCS_Result; use \OC_Helper; use OCP\Files\NotFoundException; +use OCP\IConfig; +use OCP\IGroupManager; use OCP\ILogger; +use OCP\IUserManager; +use OCP\IUserSession; class Users { - /** @var \OCP\IUserManager */ + /** @var IUserManager */ private $userManager; - /** @var \OCP\IConfig */ + /** @var IConfig */ private $config; - /** @var \OCP\IGroupManager */ + /** @var IGroupManager */ private $groupManager; - /** @var \OCP\IUserSession */ + /** @var IUserSession */ private $userSession; /** @var ILogger */ private $logger; /** - * @param \OCP\IUserManager $userManager - * @param \OCP\IConfig $config - * @param \OCP\IGroupManager $groupManager - * @param \OCP\IUserSession $userSession + * @param IUserManager $userManager + * @param IConfig $config + * @param IGroupManager $groupManager + * @param IUserSession $userSession * @param ILogger $logger */ - public function __construct(\OCP\IUserManager $userManager, - \OCP\IConfig $config, - \OCP\IGroupManager $groupManager, - \OCP\IUserSession $userSession, + public function __construct(IUserManager $userManager, + IConfig $config, + IGroupManager $groupManager, + IUserSession $userSession, ILogger $logger) { $this->userManager = $userManager; $this->config = $config; @@ -333,6 +337,50 @@ class Users { * @param array $parameters * @return OC_OCS_Result */ + public function disableUser($parameters) { + return $this->setEnabled($parameters, false); + } + + /** + * @param array $parameters + * @return OC_OCS_Result + */ + public function enableUser($parameters) { + return $this->setEnabled($parameters, true); + } + + /** + * @param array $parameters + * @param bool $value + * @return OC_OCS_Result + */ + private function setEnabled($parameters, $value) { + // Check if user is logged in + $currentLoggedInUser = $this->userSession->getUser(); + if ($currentLoggedInUser === null) { + return new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED); + } + + $targetUser = $this->userManager->get($parameters['userid']); + if($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) { + return new OC_OCS_Result(null, 101); + } + + // If not permitted + $subAdminManager = $this->groupManager->getSubAdmin(); + if(!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) { + return new OC_OCS_Result(null, 997); + } + + // enable/disable the user now + $targetUser->setEnabled($value); + return new OC_OCS_Result(null, 100); + } + + /** + * @param array $parameters + * @return OC_OCS_Result + */ public function getUsersGroups($parameters) { // Check if user is logged in $loggedInUser = $this->userSession->getUser(); diff --git a/apps/provisioning_api/tests/userstest.php b/apps/provisioning_api/tests/userstest.php index 020071bcfa1..8f463ec8b88 100644 --- a/apps/provisioning_api/tests/userstest.php +++ b/apps/provisioning_api/tests/userstest.php @@ -58,8 +58,8 @@ class UsersTest extends OriginalTest { parent::tearDown(); } - protected function setup() { - parent::setup(); + protected function setUp() { + parent::setUp(); $this->userManager = $this->getMock('\OCP\IUserManager'); $this->config = $this->getMock('\OCP\IConfig'); @@ -540,7 +540,7 @@ class UsersTest extends OriginalTest { ->expects($this->once()) ->method('isSubAdminOfGroup') ->with($loggedInUser, $existingGroup) - ->wilLReturn(false); + ->willReturn(false); $this->groupManager ->expects($this->once()) ->method('getSubAdmin') @@ -642,7 +642,7 @@ class UsersTest extends OriginalTest { [$loggedInUser, $existingGroup1], [$loggedInUser, $existingGroup2] ) - ->wilLReturn(true); + ->willReturn(true); $expected = new \OC_OCS_Result(null, 100); @@ -2295,4 +2295,60 @@ class UsersTest extends OriginalTest { $expected = new \OC_OCS_Result(null, 102, 'Unknown error occurred'); $this->assertEquals($expected, $this->api->getUserSubAdminGroups(['userid' => 'RequestedUser'])); } + + public function testEnableUser() { + $targetUser = $this->getMock('\OCP\IUser'); + $targetUser->expects($this->once()) + ->method('setEnabled') + ->with(true); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('RequestedUser') + ->will($this->returnValue($targetUser)); + $loggedInUser = $this->getMock('\OCP\IUser'); + $loggedInUser + ->expects($this->exactly(2)) + ->method('getUID') + ->will($this->returnValue('admin')); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($loggedInUser)); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $expected = new \OC_OCS_Result(null, 100); + $this->assertEquals($expected, $this->api->enableUser(['userid' => 'RequestedUser'])); + } + + public function testDisableUser() { + $targetUser = $this->getMock('\OCP\IUser'); + $targetUser->expects($this->once()) + ->method('setEnabled') + ->with(false); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('RequestedUser') + ->will($this->returnValue($targetUser)); + $loggedInUser = $this->getMock('\OCP\IUser'); + $loggedInUser + ->expects($this->exactly(2)) + ->method('getUID') + ->will($this->returnValue('admin')); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($loggedInUser)); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $expected = new \OC_OCS_Result(null, 100); + $this->assertEquals($expected, $this->api->disableUser(['userid' => 'RequestedUser'])); + } } diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php index feeb850ae7d..6cf57514483 100644 --- a/build/integration/features/bootstrap/Provisioning.php +++ b/build/integration/features/bootstrap/Provisioning.php @@ -35,7 +35,6 @@ trait Provisioning { } $this->userExists($user); PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); - } /** @@ -231,6 +230,20 @@ trait Provisioning { } /** + * @When /^assure user "([^"]*)" is disabled$/ + */ + public function assureUserIsDisabled($user) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/disable"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options)); + } + + /** * @When /^Deleting the user "([^"]*)"$/ * @param string $user */ @@ -364,6 +377,25 @@ trait Provisioning { } /** + * @Given /^Assure user "([^"]*)" is subadmin of group "([^"]*)"$/ + * @param string $user + * @param string $group + */ + public function assureUserIsSubadminOfGroup($user, $group) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/subadmins"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + $options['body'] = [ + 'groupid' => $group + ]; + $this->response = $client->send($client->createRequest("POST", $fullUrl, $options)); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** * @Given /^user "([^"]*)" is not a subadmin of group "([^"]*)"$/ * @param string $user * @param string $group @@ -529,6 +561,38 @@ trait Provisioning { } /** + * @Then /^user "([^"]*)" is disabled$/ + * @param string $user + */ + public function userIsDisabled($user) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + PHPUnit_Framework_Assert::assertEquals("false", $this->response->xml()->data[0]->enabled); + } + + /** + * @Then /^user "([^"]*)" is enabled$/ + * @param string $user + */ + public function userIsEnabled($user) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + PHPUnit_Framework_Assert::assertEquals("true", $this->response->xml()->data[0]->enabled); + } + + /** * @Given user :user has a quota of :quota * @param string $user * @param string $quota @@ -588,4 +652,5 @@ trait Provisioning { } $this->usingServer($previousServer); } + } diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php index 79c447ac573..cd9584ad186 100644 --- a/build/integration/features/bootstrap/WebDav.php +++ b/build/integration/features/bootstrap/WebDav.php @@ -424,5 +424,15 @@ trait WebDav { } + /** + * @Given /^Downloading file "([^"]*)" as "([^"]*)"$/ + */ + public function downloadingFileAs($fileName, $user) { + try { + $this->response = $this->makeDavRequest($user, 'GET', $fileName, []); + } catch (\GuzzleHttp\Exception\ServerException $ex) { + $this->response = $ex->getResponse(); + } + } } diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index 8c32c04523c..d44903c2743 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -315,3 +315,197 @@ Feature: provisioning Then the OCS status code should be "100" And the HTTP status code should be "200" And app "files_external" is disabled + + Scenario: disable an user + Given As an "admin" + And user "user1" exists + When sending "PUT" to "/cloud/users/user1/disable" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And user "user1" is disabled + + Scenario: enable an user + Given As an "admin" + And user "user1" exists + And assure user "user1" is disabled + When sending "PUT" to "/cloud/users/user1/enable" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And user "user1" is enabled + + Scenario: Subadmin should be able to enable or disable an user in their group + Given As an "admin" + And user "subadmin" exists + And user "user1" exists + And group "new-group" exists + And user "subadmin" belongs to group "new-group" + And user "user1" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + And As an "subadmin" + When sending "PUT" to "/cloud/users/user1/disable" + Then the OCS status code should be "100" + Then the HTTP status code should be "200" + And As an "admin" + And user "user1" is disabled + + Scenario: Subadmin should not be able to enable or disable an user not in their group + Given As an "admin" + And user "subadmin" exists + And user "user1" exists + And group "new-group" exists + And group "another-group" exists + And user "subadmin" belongs to group "new-group" + And user "user1" belongs to group "another-group" + And Assure user "subadmin" is subadmin of group "new-group" + And As an "subadmin" + When sending "PUT" to "/cloud/users/user1/disable" + Then the OCS status code should be "997" + Then the HTTP status code should be "401" + And As an "admin" + And user "user1" is enabled + + Scenario: Subadmins should not be able to disable users that have admin permissions in their group + Given As an "admin" + And user "another-admin" exists + And user "subadmin" exists + And group "new-group" exists + And user "another-admin" belongs to group "admin" + And user "subadmin" belongs to group "new-group" + And user "another-admin" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + And As an "subadmin" + When sending "PUT" to "/cloud/users/another-admin/disable" + Then the OCS status code should be "997" + Then the HTTP status code should be "401" + And As an "admin" + And user "another-admin" is enabled + + Scenario: Admin can disable another admin user + Given As an "admin" + And user "another-admin" exists + And user "another-admin" belongs to group "admin" + When sending "PUT" to "/cloud/users/another-admin/disable" + Then the OCS status code should be "100" + Then the HTTP status code should be "200" + And user "another-admin" is disabled + + Scenario: Admin can enable another admin user + Given As an "admin" + And user "another-admin" exists + And user "another-admin" belongs to group "admin" + And assure user "another-admin" is disabled + When sending "PUT" to "/cloud/users/another-admin/enable" + Then the OCS status code should be "100" + Then the HTTP status code should be "200" + And user "another-admin" is enabled + + Scenario: Admin can disable subadmins in the same group + Given As an "admin" + And user "subadmin" exists + And group "new-group" exists + And user "subadmin" belongs to group "new-group" + And user "admin" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + When sending "PUT" to "/cloud/users/subadmin/disable" + Then the OCS status code should be "100" + Then the HTTP status code should be "200" + And user "subadmin" is disabled + + Scenario: Admin can enable subadmins in the same group + Given As an "admin" + And user "subadmin" exists + And group "new-group" exists + And user "subadmin" belongs to group "new-group" + And user "admin" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + And assure user "another-admin" is disabled + When sending "PUT" to "/cloud/users/subadmin/disable" + Then the OCS status code should be "100" + Then the HTTP status code should be "200" + And user "subadmin" is disabled + + Scenario: Admin user cannot disable himself + Given As an "admin" + And user "another-admin" exists + And user "another-admin" belongs to group "admin" + And As an "another-admin" + When sending "PUT" to "/cloud/users/another-admin/disable" + Then the OCS status code should be "101" + And the HTTP status code should be "200" + And As an "admin" + And user "another-admin" is enabled + + Scenario:Admin user cannot enable himself + Given As an "admin" + And user "another-admin" exists + And user "another-admin" belongs to group "admin" + And assure user "another-admin" is disabled + And As an "another-admin" + When sending "PUT" to "/cloud/users/another-admin/enable" + And As an "admin" + Then user "another-admin" is disabled + + Scenario: disable an user with a regular user + Given As an "admin" + And user "user1" exists + And user "user2" exists + And As an "user1" + When sending "PUT" to "/cloud/users/user2/disable" + Then the OCS status code should be "997" + And the HTTP status code should be "401" + And As an "admin" + And user "user2" is enabled + + Scenario: enable an user with a regular user + Given As an "admin" + And user "user1" exists + And user "user2" exists + And assure user "user2" is disabled + And As an "user1" + When sending "PUT" to "/cloud/users/user2/enable" + Then the OCS status code should be "997" + And the HTTP status code should be "401" + And As an "admin" + And user "user2" is disabled + + Scenario: Subadmin should not be able to disable himself + Given As an "admin" + And user "subadmin" exists + And group "new-group" exists + And user "subadmin" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + And As an "subadmin" + When sending "PUT" to "/cloud/users/subadmin/disable" + Then the OCS status code should be "101" + Then the HTTP status code should be "200" + And As an "admin" + And user "subadmin" is enabled + + Scenario: Subadmin should not be able to enable himself + Given As an "admin" + And user "subadmin" exists + And group "new-group" exists + And user "subadmin" belongs to group "new-group" + And Assure user "subadmin" is subadmin of group "new-group" + And assure user "subadmin" is disabled + And As an "subadmin" + When sending "PUT" to "/cloud/users/subadmin/enabled" + And As an "admin" + And user "subadmin" is disabled + + Scenario: Making a web request with an enabled user + Given As an "admin" + And user "user0" exists + And As an "user0" + When sending "GET" to "/index.php/apps/files" + Then the HTTP status code should be "200" + + Scenario: Making a web request with a disabled user + Given As an "admin" + And user "user0" exists + And assure user "user0" is disabled + And As an "user0" + When sending "GET" to "/index.php/apps/files" + Then the OCS status code should be "999" + And the HTTP status code should be "200" + diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature index d67afede953..79d7617f024 100644 --- a/build/integration/features/sharing-v1.feature +++ b/build/integration/features/sharing-v1.feature @@ -696,3 +696,16 @@ Feature: sharing Then user "user2" should see following elements | /foo/ | | /foo%20(2)/ | + + Scenario: Creating a new share with a disabled user + Given As an "admin" + And user "user0" exists + And user "user1" exists + And assure user "user0" is disabled + And As an "user0" + When sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | user1 | + | shareType | 0 | + Then the OCS status code should be "997" + And the HTTP status code should be "401" diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature index abdc63935e9..f4d40615fa7 100644 --- a/build/integration/features/webdav-related.feature +++ b/build/integration/features/webdav-related.feature @@ -286,3 +286,11 @@ Feature: webdav-related When As an "user0" And Downloading file "/files/user0/myChunkedFile.txt" Then Downloaded content should be "AAAAABBBBBCCCCC" + + Scenario: A disabled user cannot use webdav + Given user "userToBeDisabled" exists + And As an "admin" + And assure user "userToBeDisabled" is disabled + When Downloading file "/welcome.txt" as "userToBeDisabled" + Then the HTTP status code should be "503" + diff --git a/core/Command/Log/Manage.php b/core/Command/Log/Manage.php index 1d65d7ed0d8..aeaaca8aa0e 100644 --- a/core/Command/Log/Manage.php +++ b/core/Command/Log/Manage.php @@ -115,7 +115,7 @@ class Manage extends Command { * @throws \InvalidArgumentException */ protected function validateBackend($backend) { - if (!class_exists('OC_Log_'.$backend)) { + if (!class_exists('OC\\Log\\'.ucfirst($backend))) { throw new \InvalidArgumentException('Invalid backend'); } } diff --git a/core/Command/User/Disable.php b/core/Command/User/Disable.php new file mode 100644 index 00000000000..018f11190d6 --- /dev/null +++ b/core/Command/User/Disable.php @@ -0,0 +1,64 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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\Core\Command\User; + +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; + +class Disable extends Command { + /** @var IUserManager */ + protected $userManager; + + /** + * @param IUserManager $userManager + */ + public function __construct(IUserManager $userManager) { + $this->userManager = $userManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('user:disable') + ->setDescription('disables the specified user') + ->addArgument( + 'uid', + InputArgument::REQUIRED, + 'the username' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $user = $this->userManager->get($input->getArgument('uid')); + if (is_null($user)) { + $output->writeln('<error>User does not exist</error>'); + return; + } + + $user->setEnabled(false); + $output->writeln('<info>The specified user is disabled</info>'); + } +} diff --git a/core/Command/User/Enable.php b/core/Command/User/Enable.php new file mode 100644 index 00000000000..ffe2e40d654 --- /dev/null +++ b/core/Command/User/Enable.php @@ -0,0 +1,64 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @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\Core\Command\User; + +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputArgument; + +class Enable extends Command { + /** @var IUserManager */ + protected $userManager; + + /** + * @param IUserManager $userManager + */ + public function __construct(IUserManager $userManager) { + $this->userManager = $userManager; + parent::__construct(); + } + + protected function configure() { + $this + ->setName('user:enable') + ->setDescription('enables the specified user') + ->addArgument( + 'uid', + InputArgument::REQUIRED, + 'the username' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $user = $this->userManager->get($input->getArgument('uid')); + if (is_null($user)) { + $output->writeln('<error>User does not exist</error>'); + return; + } + + $user->setEnabled(true); + $output->writeln('<info>The specified user is enabled</info>'); + } +} diff --git a/core/register_command.php b/core/register_command.php index 798497d97d2..3dab37ce5a6 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -121,6 +121,8 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); $application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\Disable(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\Enable(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\User\LastSeen(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\User\Report(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); diff --git a/core/templates/login.php b/core/templates/login.php index 8405bac6890..86c186928cc 100644 --- a/core/templates/login.php +++ b/core/templates/login.php @@ -40,11 +40,11 @@ script('core', [ </div> <p class="grouptop"> <input type="text" name="user" id="user" - placeholder="<?php p($l->t('Username')); ?>" + placeholder="<?php p($l->t('Username or email')); ?>" value="<?php p($_['loginName']); ?>" <?php p($_['user_autofocus'] ? 'autofocus' : ''); ?> autocomplete="on" autocapitalize="off" autocorrect="off" required> - <label for="user" class="infield"><?php p($l->t('Username')); ?></label> + <label for="user" class="infield"><?php p($l->t('Username or email')); ?></label> </p> <p class="groupbottom"> diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index 21422d40603..e9456550206 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -89,6 +89,9 @@ class InfoParser { if (!array_key_exists('uninstall', $array['repair-steps'])) { $array['repair-steps']['uninstall'] = []; } + if (!array_key_exists('background-jobs', $array)) { + $array['background-jobs'] = []; + } if (array_key_exists('documentation', $array) && is_array($array['documentation'])) { foreach ($array['documentation'] as $key => $url) { @@ -128,6 +131,9 @@ class InfoParser { if (isset($array['repair-steps']['uninstall']['step']) && is_array($array['repair-steps']['uninstall']['step'])) { $array['repair-steps']['uninstall'] = $array['repair-steps']['uninstall']['step']; } + if (isset($array['background-jobs']['job']) && is_array($array['background-jobs']['job'])) { + $array['background-jobs'] = $array['background-jobs']['job']; + } return $array; } @@ -147,10 +153,7 @@ class InfoParser { if (!isset($array[$element])) { $array[$element] = ""; } - /** - * @var \SimpleXMLElement $node - */ - + /** @var \SimpleXMLElement $node */ // Has attributes if ($attributes = $node->attributes()) { $data = [ diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 8ecc92e134b..edc6306b8f1 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -133,6 +133,8 @@ class Installer { } } + \OC_App::setupBackgroundJobs($info['background-jobs']); + //run appinfo/install.php if((!isset($data['noinstall']) or $data['noinstall']==false)) { self::includeAppScript($basedir . '/appinfo/install.php'); @@ -569,6 +571,7 @@ class Installer { if (is_null($info)) { return false; } + \OC_App::setupBackgroundJobs($info['background-jobs']); OC_App::executeRepairSteps($app, $info['repair-steps']['install']); diff --git a/lib/private/Lock/AbstractLockingProvider.php b/lib/private/Lock/AbstractLockingProvider.php index 1886fbea082..ff9f99a9630 100644 --- a/lib/private/Lock/AbstractLockingProvider.php +++ b/lib/private/Lock/AbstractLockingProvider.php @@ -77,6 +77,9 @@ abstract class AbstractLockingProvider implements ILockingProvider { if ($type === self::LOCK_SHARED) { if (isset($this->acquiredLocks['shared'][$path]) and $this->acquiredLocks['shared'][$path] > 0) { $this->acquiredLocks['shared'][$path]--; + if ($this->acquiredLocks['shared'][$path] === 0) { + unset($this->acquiredLocks['shared'][$path]); + } } } else if ($type === self::LOCK_EXCLUSIVE) { unset($this->acquiredLocks['exclusive'][$path]); diff --git a/lib/private/Log.php b/lib/private/Log.php index d82346bbcf0..9248070c067 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -73,7 +73,7 @@ class Log implements ILogger { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if($logger === null) { - $this->logger = 'OC_Log_'.ucfirst($this->config->getValue('log_type', 'owncloud')); + $this->logger = 'OC\\Log\\'.ucfirst($this->config->getValue('log_type', 'owncloud')); call_user_func(array($this->logger, 'init')); } else { $this->logger = $logger; diff --git a/lib/private/log/errorhandler.php b/lib/private/Log/ErrorHandler.php index 8899bcfcb03..8899bcfcb03 100644 --- a/lib/private/log/errorhandler.php +++ b/lib/private/Log/ErrorHandler.php diff --git a/lib/private/log/errorlog.php b/lib/private/Log/Errorlog.php index ad3605136d0..37498c36aba 100644 --- a/lib/private/log/errorlog.php +++ b/lib/private/Log/Errorlog.php @@ -23,7 +23,9 @@ * THE SOFTWARE. */ -class OC_Log_Errorlog { +namespace OC\Log; + +class Errorlog { /** diff --git a/lib/private/log/owncloud.php b/lib/private/Log/Owncloud.php index 9c106299e4c..13997a0d552 100644 --- a/lib/private/log/owncloud.php +++ b/lib/private/Log/Owncloud.php @@ -27,13 +27,15 @@ * */ +namespace OC\Log; + /** * logging utilities * * Log is saved at data/owncloud.log (on default) */ -class OC_Log_Owncloud { +class Owncloud { static protected $logFile; /** @@ -41,7 +43,7 @@ class OC_Log_Owncloud { */ public static function init() { $systemConfig = \OC::$server->getSystemConfig(); - $defaultLogFile = $systemConfig->getValue("datadirectory", OC::$SERVERROOT.'/data').'/owncloud.log'; + $defaultLogFile = $systemConfig->getValue("datadirectory", \OC::$SERVERROOT.'/data').'/owncloud.log'; self::$logFile = $systemConfig->getValue("logfile", $defaultLogFile); /** @@ -72,13 +74,13 @@ class OC_Log_Owncloud { $format = $config->getValue('logdateformat', 'c'); $logTimeZone = $config->getValue( "logtimezone", 'UTC' ); try { - $timezone = new DateTimeZone($logTimeZone); - } catch (Exception $e) { - $timezone = new DateTimeZone('UTC'); + $timezone = new \DateTimeZone($logTimeZone); + } catch (\Exception $e) { + $timezone = new \DateTimeZone('UTC'); } - $time = DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); + $time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); if ($time === false) { - $time = new DateTime(null, $timezone); + $time = new \DateTime(null, $timezone); } else { // apply timezone if $time is created from UNIX timestamp $time->setTimezone($timezone); diff --git a/lib/private/log/rotate.php b/lib/private/Log/Rotate.php index 458661c82d0..458661c82d0 100644 --- a/lib/private/log/rotate.php +++ b/lib/private/Log/Rotate.php diff --git a/lib/private/log/syslog.php b/lib/private/Log/Syslog.php index 96cf463d042..115103f26d6 100644 --- a/lib/private/log/syslog.php +++ b/lib/private/Log/Syslog.php @@ -21,7 +21,9 @@ * */ -class OC_Log_Syslog { +namespace OC\Log; + +class Syslog { static protected $levels = array( \OCP\Util::DEBUG => LOG_DEBUG, \OCP\Util::INFO => LOG_INFO, diff --git a/lib/private/Server.php b/lib/private/Server.php index a98ddc36b58..bbe6b88876f 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -329,7 +329,7 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerService('Logger', function (Server $c) { $logClass = $c->query('AllConfig')->getSystemValue('log_type', 'owncloud'); - $logger = 'OC_Log_' . ucfirst($logClass); + $logger = 'OC\\Log\\' . ucfirst($logClass); call_user_func(array($logger, 'init')); return new Log($logger); @@ -837,8 +837,7 @@ class Server extends ServerContainer implements IServerContainer { } /** - * For internal use only - * + * @internal For internal use only * @return \OC\SystemConfig */ public function getSystemConfig() { @@ -1180,7 +1179,7 @@ class Server extends ServerContainer implements IServerContainer { return $this->query('MountManager'); } - /* + /** * Get the MimeTypeDetector * * @return \OCP\Files\IMimeTypeDetector diff --git a/lib/private/share/constants.php b/lib/private/Share/Constants.php index e60eb98832b..e60eb98832b 100644 --- a/lib/private/share/constants.php +++ b/lib/private/Share/Constants.php diff --git a/lib/private/share/helper.php b/lib/private/Share/Helper.php index f9581e48e62..f9581e48e62 100644 --- a/lib/private/share/helper.php +++ b/lib/private/Share/Helper.php diff --git a/lib/private/share/mailnotifications.php b/lib/private/Share/MailNotifications.php index f71651e71fc..f71651e71fc 100644 --- a/lib/private/share/mailnotifications.php +++ b/lib/private/Share/MailNotifications.php diff --git a/lib/private/share/searchresultsorter.php b/lib/private/Share/SearchResultSorter.php index 6d5542146e7..6d5542146e7 100644 --- a/lib/private/share/searchresultsorter.php +++ b/lib/private/Share/SearchResultSorter.php diff --git a/lib/private/share/share.php b/lib/private/Share/Share.php index 5b61f418a4d..5b61f418a4d 100644 --- a/lib/private/share/share.php +++ b/lib/private/Share/Share.php diff --git a/lib/private/legacy/api.php b/lib/private/legacy/api.php index bab879c95f8..702b9df1927 100644 --- a/lib/private/legacy/api.php +++ b/lib/private/legacy/api.php @@ -356,7 +356,11 @@ class OC_API { if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) ) { $authUser = $_SERVER['PHP_AUTH_USER']; $authPw = $_SERVER['PHP_AUTH_PW']; - $return = OC_User::login($authUser, $authPw); + try { + $return = OC_User::login($authUser, $authPw); + } catch (\OC\User\LoginException $e) { + return false; + } if ($return === true) { self::$logoutRequired = true; diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index fe590e4159e..dab4c1a70c7 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -1190,6 +1190,7 @@ class OC_App { self::loadApp($appId, false); include $appPath . '/appinfo/update.php'; } + self::setupBackgroundJobs($appData['background-jobs']); //set remote/public handlers if (array_key_exists('ocsid', $appData)) { @@ -1240,6 +1241,13 @@ class OC_App { $r->run(); } + public static function setupBackgroundJobs(array $jobs) { + $queue = \OC::$server->getJobList(); + foreach ($jobs as $job) { + $queue->add($job); + } + } + /** * @param string $appId * @param string[] $steps diff --git a/lib/private/legacy/user.php b/lib/private/legacy/user.php index 11c35daa0de..18a4c369d5e 100644 --- a/lib/private/legacy/user.php +++ b/lib/private/legacy/user.php @@ -63,8 +63,6 @@ class OC_User { return OC::$server->getUserSession(); } - private static $_backends = array(); - private static $_usedBackends = array(); private static $_setupedBackends = array(); @@ -105,7 +103,7 @@ class OC_User { break; default: \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', \OCP\Util::DEBUG); - $className = 'OC_USER_' . strToUpper($backend); + $className = 'OC_USER_' . strtoupper($backend); self::$_usedBackends[$backend] = new $className(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; @@ -152,14 +150,22 @@ class OC_User { /** * Try to login a user * - * @param string $loginname The login name of the user to log in + * @param string $loginName The login name of the user to log in * @param string $password The password of the user * @return boolean|null * * Log in a user and regenerate a new session - if the password is ok */ - public static function login($loginname, $password) { - $result = self::getUserSession()->login($loginname, $password); + public static function login($loginName, $password) { + + $result = self::getUserSession()->login($loginName, $password); + if (!$result) { + $users = \OC::$server->getUserManager()->getByEmail($loginName); + // we only allow login by email if unique + if (count($users) === 1) { + $result = self::getUserSession()->login($users[0]->getUID(), $password); + } + } if ($result) { // Refresh the token \OC::$server->getCsrfTokenManager()->refreshToken(); @@ -175,6 +181,7 @@ class OC_User { /** * Try to login a user using the magic cookie (remember login) * + * @deprecated use \OCP\IUserSession::loginWithCookie() * @param string $uid The username of the user to log in * @param string $token * @return bool @@ -241,6 +248,8 @@ class OC_User { /** * Sets user id for session and triggers emit + * + * @param string $uid */ public static function setUserId($uid) { $userSession = \OC::$server->getUserSession(); @@ -296,14 +305,11 @@ class OC_User { /** * Check if the user is logged in, considers also the HTTP basic credentials * + * @deprecated use \OC::$server->getUserSession()->isLoggedIn() * @return bool */ public static function isLoggedIn() { - if (\OC::$server->getSession()->get('user_id') !== null && self::$incognitoMode === false) { - return self::userExists(\OC::$server->getSession()->get('user_id')); - } - - return false; + return \OC::$server->getUserSession()->isLoggedIn(); } /** diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 22a05090b96..fd273055ae1 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -48,11 +48,21 @@ * */ +use OC\Cache\CappedMemoryCache; + /** * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend { - private $cache = array(); + /** @var CappedMemoryCache */ + private $cache; + + /** + * OC_User_Database constructor. + */ + public function __construct() { + $this->cache = new CappedMemoryCache(); + } /** * Create a new user diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php index 4371be134aa..37a3e5ba134 100644 --- a/lib/private/user/manager.php +++ b/lib/private/user/manager.php @@ -33,6 +33,7 @@ namespace OC\User; use OC\Hooks\PublicEmitter; +use OCP\IUser; use OCP\IUserBackend; use OCP\IUserManager; use OCP\IConfig; @@ -354,4 +355,17 @@ class Manager extends PublicEmitter implements IUserManager { } while (count($users) >= $limit); } } + + /** + * @param string $email + * @return IUser[] + * @since 9.1.0 + */ + public function getByEmail($email) { + $userIds = $this->config->getUsersForUserValue('settings', 'email', $email); + + return array_map(function($uid) { + return $this->get($uid); + }, $userIds); + } } diff --git a/lib/private/user/session.php b/lib/private/user/session.php index 5402c5cf74f..c7f8a6920de 100644 --- a/lib/private/user/session.php +++ b/lib/private/user/session.php @@ -32,6 +32,8 @@ namespace OC\User; use OC\Hooks\Emitter; +use OCP\ISession; +use OCP\IUserManager; use OCP\IUserSession; /** @@ -53,26 +55,20 @@ use OCP\IUserSession; * @package OC\User */ class Session implements IUserSession, Emitter { - /** - * @var \OC\User\Manager $manager - */ + /** @var \OC\User\Manager $manager */ private $manager; - /** - * @var \OC\Session\Session $session - */ + /** @var \OC\Session\Session $session */ private $session; - /** - * @var \OC\User\User $activeUser - */ + /** @var \OC\User\User $activeUser */ protected $activeUser; /** - * @param \OCP\IUserManager $manager - * @param \OCP\ISession $session + * @param IUserManager $manager + * @param ISession $session */ - public function __construct(\OCP\IUserManager $manager, \OCP\ISession $session) { + public function __construct(IUserManager $manager, ISession $session) { $this->manager = $manager; $this->session = $session; } @@ -107,7 +103,7 @@ class Session implements IUserSession, Emitter { /** * get the session object * - * @return \OCP\ISession + * @return ISession */ public function getSession() { return $this->session; @@ -116,10 +112,10 @@ class Session implements IUserSession, Emitter { /** * set the session object * - * @param \OCP\ISession $session + * @param ISession $session */ - public function setSession(\OCP\ISession $session) { - if ($this->session instanceof \OCP\ISession) { + public function setSession(ISession $session) { + if ($this->session instanceof ISession) { $this->session->close(); } $this->session = $session; @@ -170,7 +166,12 @@ class Session implements IUserSession, Emitter { * @return bool if logged in */ public function isLoggedIn() { - return $this->getUser() !== null; + $user = $this->getUser(); + if (is_null($user)) { + return false; + } + + return $user->isEnabled(); } /** @@ -226,15 +227,18 @@ class Session implements IUserSession, Emitter { if ($this->isLoggedIn()) { return true; } else { - throw new LoginException('Login canceled by app'); + // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory + $message = \OC::$server->getL10N('lib')->t('Login canceled by app'); + throw new LoginException($message); } } else { - return false; + // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory + $message = \OC::$server->getL10N('lib')->t('User disabled'); + throw new LoginException($message); } } - } else { - return false; } + return false; } /** diff --git a/lib/private/user/user.php b/lib/private/user/user.php index 3199790dba0..36680436769 100644 --- a/lib/private/user/user.php +++ b/lib/private/user/user.php @@ -417,5 +417,4 @@ class User implements IUser { $this->emitter->emit('\OC\User', 'changeUser', array($this, $feature, $value)); } } - } diff --git a/lib/public/IServerContainer.php b/lib/public/IServerContainer.php index de48daeef88..65754f993f8 100644 --- a/lib/public/IServerContainer.php +++ b/lib/public/IServerContainer.php @@ -519,4 +519,16 @@ interface IServerContainer { * @since 9.0.0 */ public function getContentSecurityPolicyManager(); + + /** + * @return \OCP\IDateTimeZone + * @since 8.0.0 + */ + public function getDateTimeZone(); + + /** + * @return \OCP\IDateTimeFormatter + * @since 8.0.0 + */ + public function getDateTimeFormatter(); } diff --git a/lib/public/iuser.php b/lib/public/iuser.php index b0dd8dc35b2..16617a2f2f6 100644 --- a/lib/public/iuser.php +++ b/lib/public/iuser.php @@ -32,6 +32,7 @@ namespace OCP; * @since 8.0.0 */ interface IUser { + /** * get the user id * diff --git a/lib/public/iusermanager.php b/lib/public/iusermanager.php index 6442938a99b..00c0bbc8721 100644 --- a/lib/public/iusermanager.php +++ b/lib/public/iusermanager.php @@ -142,4 +142,11 @@ interface IUserManager { * @since 9.0.0 */ public function callForAllUsers (\Closure $callback, $search = ''); + + /** + * @param string $email + * @return IUser[] + * @since 9.1.0 + */ + public function getByEmail($email); } diff --git a/settings/Controller/LogSettingsController.php b/settings/Controller/LogSettingsController.php index c0c9ee04ca3..70a2b752359 100644 --- a/settings/Controller/LogSettingsController.php +++ b/settings/Controller/LogSettingsController.php @@ -102,7 +102,7 @@ class LogSettingsController extends Controller { * @return StreamResponse */ public function download() { - $resp = new StreamResponse(\OC_Log_Owncloud::getLogFilePath()); + $resp = new StreamResponse(\OC\Log\Owncloud::getLogFilePath()); $resp->addHeader('Content-Disposition', 'attachment; filename="owncloud.log"'); return $resp; } diff --git a/tests/data/app/expected-info.json b/tests/data/app/expected-info.json index cef7a7fdaba..81de5341efa 100644 --- a/tests/data/app/expected-info.json +++ b/tests/data/app/expected-info.json @@ -74,5 +74,6 @@ "post-migration": [], "live-migration": [], "uninstall": [] - } + }, + "background-jobs": [] } diff --git a/tests/lib/log/owncloud.php b/tests/lib/log/owncloud.php index adecc49768c..e19063a83f5 100644 --- a/tests/lib/log/owncloud.php +++ b/tests/lib/log/owncloud.php @@ -15,12 +15,17 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ +namespace Test\Log; + +use OC\Log\Owncloud; +use Test\TestCase; + /** - * Class Test_Log_Owncloud + * Class OwncloudTest * * @group DB */ -class Test_Log_Owncloud extends Test\TestCase +class OwncloudTest extends TestCase { private $restore_logfile; private $restore_logdateformat; @@ -32,7 +37,7 @@ class Test_Log_Owncloud extends Test\TestCase $this->restore_logdateformat = $config->getSystemValue('logdateformat'); $config->setSystemValue("logfile", $config->getSystemValue('datadirectory') . "/logtest"); - OC_Log_Owncloud::init(); + Owncloud::init(); } protected function tearDown() { $config = \OC::$server->getConfig(); @@ -46,7 +51,7 @@ class Test_Log_Owncloud extends Test\TestCase } else { $config->deleteSystemValue("restore_logdateformat"); } - OC_Log_Owncloud::init(); + Owncloud::init(); parent::tearDown(); } @@ -57,7 +62,7 @@ class Test_Log_Owncloud extends Test\TestCase # set format & write log line $config->setSystemValue('logdateformat', 'u'); - OC_Log_Owncloud::write('test', 'message', \OCP\Util::ERROR); + Owncloud::write('test', 'message', \OCP\Util::ERROR); # read log line $handle = @fopen($config->getSystemValue('logfile'), 'r'); diff --git a/tests/lib/user/session.php b/tests/lib/user/session.php index 1c042dec9f0..5a8ea57cb86 100644 --- a/tests/lib/user/session.php +++ b/tests/lib/user/session.php @@ -11,19 +11,25 @@ namespace Test\User; use OC\Session\Memory; use OC\User\User; +use OCP\ISession; +use OCP\IUserManager; +use OCP\UserInterface; +use Test\TestCase; /** * @group DB * @package Test\User */ -class Session extends \Test\TestCase { +class Session extends TestCase { public function testGetUser() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->once()) ->method('get') ->with('user_id') ->will($this->returnValue('foo')); + /** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */ $backend = $this->getMock('\Test\Util\User\Dummy'); $backend->expects($this->once()) ->method('userExists') @@ -39,12 +45,14 @@ class Session extends \Test\TestCase { } public function testIsLoggedIn() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->once()) ->method('get') ->with('user_id') ->will($this->returnValue('foo')); + /** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */ $backend = $this->getMock('\Test\Util\User\Dummy'); $backend->expects($this->once()) ->method('userExists') @@ -60,12 +68,14 @@ class Session extends \Test\TestCase { } public function testNotLoggedIn() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->once()) ->method('get') ->with('user_id') ->will($this->returnValue(null)); + /** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */ $backend = $this->getMock('\Test\Util\User\Dummy'); $backend->expects($this->never()) ->method('userExists'); @@ -79,15 +89,18 @@ class Session extends \Test\TestCase { } public function testSetUser() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->once()) ->method('set') ->with('user_id', 'foo'); + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager'); $backend = $this->getMock('\Test\Util\User\Dummy'); + /** @var User | \PHPUnit_Framework_MockObject_MockObject $user */ $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); $user->expects($this->once()) ->method('getUID') @@ -98,6 +111,7 @@ class Session extends \Test\TestCase { } public function testLoginValidPasswordEnabled() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->once()) ->method('regenerateId'); @@ -126,12 +140,13 @@ class Session extends \Test\TestCase { unset($managerMethods[$i]); } } + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager', $managerMethods, array()); $backend = $this->getMock('\Test\Util\User\Dummy'); $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); - $user->expects($this->once()) + $user->expects($this->exactly(2)) ->method('isEnabled') ->will($this->returnValue(true)); $user->expects($this->any()) @@ -150,7 +165,12 @@ class Session extends \Test\TestCase { $this->assertEquals($user, $userSession->getUser()); } + /** + * @expectedException \OC\User\LoginException + * @expectedExceptionMessage User disabled + */ public function testLoginValidPasswordDisabled() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->never()) ->method('set'); @@ -167,6 +187,7 @@ class Session extends \Test\TestCase { unset($managerMethods[$i]); } } + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager', $managerMethods, array()); $backend = $this->getMock('\Test\Util\User\Dummy'); @@ -188,6 +209,7 @@ class Session extends \Test\TestCase { } public function testLoginInvalidPassword() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->never()) ->method('set'); @@ -204,6 +226,7 @@ class Session extends \Test\TestCase { unset($managerMethods[$i]); } } + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager', $managerMethods, array()); $backend = $this->getMock('\Test\Util\User\Dummy'); @@ -224,16 +247,16 @@ class Session extends \Test\TestCase { } public function testLoginNonExisting() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->never()) ->method('set'); $session->expects($this->once()) ->method('regenerateId'); + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager'); - $backend = $this->getMock('\Test\Util\User\Dummy'); - $manager->expects($this->once()) ->method('checkPassword') ->with('foo', 'bar') @@ -244,6 +267,7 @@ class Session extends \Test\TestCase { } public function testRememberLoginValidToken() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->exactly(1)) ->method('set') @@ -290,6 +314,7 @@ class Session extends \Test\TestCase { $token = 'goodToken'; \OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time()); + /** @var \OC\User\Session $userSession */ $userSession = $this->getMock( '\OC\User\Session', //override, otherwise tests will fail because of setcookie() @@ -303,6 +328,7 @@ class Session extends \Test\TestCase { } public function testRememberLoginInvalidToken() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->never()) ->method('set'); @@ -319,6 +345,7 @@ class Session extends \Test\TestCase { unset($managerMethods[$i]); } } + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager', $managerMethods, array()); $backend = $this->getMock('\Test\Util\User\Dummy'); @@ -347,6 +374,7 @@ class Session extends \Test\TestCase { } public function testRememberLoginInvalidUser() { + /** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */ $session = $this->getMock('\OC\Session\Memory', array(), array('')); $session->expects($this->never()) ->method('set'); @@ -363,6 +391,7 @@ class Session extends \Test\TestCase { unset($managerMethods[$i]); } } + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMock('\OC\User\Manager', $managerMethods, array()); $backend = $this->getMock('\Test\Util\User\Dummy'); @@ -395,6 +424,7 @@ class Session extends \Test\TestCase { 'bar' => new User('bar', null) ); + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */ $manager = $this->getMockBuilder('\OC\User\Manager') ->disableOriginalConstructor() ->getMock(); |