diff options
-rw-r--r-- | apps/dav/lib/connector/sabre/exceptionloggerplugin.php | 3 | ||||
-rw-r--r-- | apps/dav/lib/connector/sabre/filesreportplugin.php | 2 | ||||
-rw-r--r-- | apps/dav/lib/server.php | 6 | ||||
-rw-r--r-- | apps/dav/lib/systemtag/systemtagnode.php | 1 | ||||
-rw-r--r-- | apps/dav/lib/systemtag/systemtagplugin.php | 29 | ||||
-rw-r--r-- | apps/dav/tests/unit/connector/sabre/filesreportplugin.php | 16 | ||||
-rw-r--r-- | apps/dav/tests/unit/systemtag/systemtagplugin.php | 255 | ||||
-rw-r--r-- | apps/files_sharing/css/public.css | 6 | ||||
-rwxr-xr-x | autotest.sh | 4 | ||||
-rw-r--r-- | build/integration/config/behat.yml | 2 | ||||
-rw-r--r-- | build/integration/features/bootstrap/TagsContext.php | 482 | ||||
-rw-r--r-- | build/integration/features/tags.feature | 370 | ||||
-rw-r--r-- | lib/private/avatarmanager.php | 2 | ||||
-rw-r--r-- | lib/private/preview/movie.php | 4 | ||||
-rw-r--r-- | lib/public/encryption/iencryptionmodule.php | 18 | ||||
-rw-r--r-- | lib/public/iavatarmanager.php | 2 | ||||
-rw-r--r-- | settings/controller/userscontroller.php | 6 | ||||
-rw-r--r-- | tests/settings/controller/userscontrollertest.php | 26 |
18 files changed, 1210 insertions, 24 deletions
diff --git a/apps/dav/lib/connector/sabre/exceptionloggerplugin.php b/apps/dav/lib/connector/sabre/exceptionloggerplugin.php index b514a917556..1052c56e968 100644 --- a/apps/dav/lib/connector/sabre/exceptionloggerplugin.php +++ b/apps/dav/lib/connector/sabre/exceptionloggerplugin.php @@ -94,6 +94,8 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { $message = "HTTP/1.1 {$ex->getHTTPCode()} $message"; } + $user = \OC_User::getUser(); + $exception = [ 'Message' => $message, 'Exception' => $exceptionClass, @@ -101,6 +103,7 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { 'Trace' => $ex->getTraceAsString(), 'File' => $ex->getFile(), 'Line' => $ex->getLine(), + 'User' => $user, ]; $this->logger->log($level, 'Exception: ' . json_encode($exception), ['app' => $this->appName]); } diff --git a/apps/dav/lib/connector/sabre/filesreportplugin.php b/apps/dav/lib/connector/sabre/filesreportplugin.php index 141b684360e..121924ce9a7 100644 --- a/apps/dav/lib/connector/sabre/filesreportplugin.php +++ b/apps/dav/lib/connector/sabre/filesreportplugin.php @@ -209,7 +209,7 @@ class FilesReportPlugin extends ServerPlugin { * * @throws TagNotFoundException whenever a tag was not found */ - public function processFilterRules($filterRules) { + protected function processFilterRules($filterRules) { $ns = '{' . $this::NS_OWNCLOUD . '}'; $resultFileIds = null; $systemTagIds = []; diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index fd18d0d21ac..74be318fe5e 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -93,7 +93,11 @@ class Server { $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin()); // system tags plugins - $this->server->addPlugin(new \OCA\DAV\SystemTag\SystemTagPlugin(\OC::$server->getSystemTagManager())); + $this->server->addPlugin(new \OCA\DAV\SystemTag\SystemTagPlugin( + \OC::$server->getSystemTagManager(), + \OC::$server->getGroupManager(), + \OC::$server->getUserSession() + )); // comments plugin $this->server->addPlugin(new \OCA\DAV\Comments\CommentsPlugin( diff --git a/apps/dav/lib/systemtag/systemtagnode.php b/apps/dav/lib/systemtag/systemtagnode.php index ecdb39a762c..7a47a752ad0 100644 --- a/apps/dav/lib/systemtag/systemtagnode.php +++ b/apps/dav/lib/systemtag/systemtagnode.php @@ -103,6 +103,7 @@ class SystemTagNode implements \Sabre\DAV\INode { * @param bool $userVisible user visible * @param bool $userAssignable user assignable * @throws NotFound whenever the given tag id does not exist + * @throws Forbidden whenever there is no permission to update said tag * @throws Conflict whenever a tag already exists with the given attributes */ public function update($name, $userVisible, $userAssignable) { diff --git a/apps/dav/lib/systemtag/systemtagplugin.php b/apps/dav/lib/systemtag/systemtagplugin.php index 3348b431c47..7da24ba7cf8 100644 --- a/apps/dav/lib/systemtag/systemtagplugin.php +++ b/apps/dav/lib/systemtag/systemtagplugin.php @@ -21,6 +21,8 @@ */ namespace OCA\DAV\SystemTag; +use OCP\IGroupManager; +use OCP\IUserSession; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; @@ -61,12 +63,26 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin { protected $tagManager; /** - * System tags plugin - * + * @var IUserSession + */ + protected $userSession; + + /** + * @var IGroupManager + */ + protected $groupManager; + + /** * @param ISystemTagManager $tagManager tag manager + * @param IGroupManager $groupManager + * @param IUserSession $userSession */ - public function __construct(ISystemTagManager $tagManager) { + public function __construct(ISystemTagManager $tagManager, + IGroupManager $groupManager, + IUserSession $userSession) { $this->tagManager = $tagManager; + $this->userSession = $userSession; + $this->groupManager = $groupManager; } /** @@ -163,6 +179,13 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin { if (isset($data['userAssignable'])) { $userAssignable = (bool)$data['userAssignable']; } + + if($userVisible === false || $userAssignable === false) { + if(!$this->userSession->isLoggedIn() || !$this->groupManager->isAdmin($this->userSession->getUser()->getUID())) { + throw new BadRequest('Not sufficient permissions'); + } + } + try { return $this->tagManager->createTag($tagName, $userVisible, $userAssignable); } catch (TagAlreadyExistsException $e) { diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php index b528e2d2427..83af45d3bcd 100644 --- a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php @@ -376,7 +376,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], ]; - $this->assertEquals(['111', '222'], $this->plugin->processFilterRules($rules)); + $this->assertEquals(['111', '222'], $this->invokePrivate($this->plugin, 'processFilterRules', [$rules])); } public function testProcessFilterRulesAndCondition() { @@ -400,7 +400,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } public function testProcessFilterRulesAndConditionWithOneEmptyResult() { @@ -424,7 +424,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } public function testProcessFilterRulesAndConditionWithFirstEmptyResult() { @@ -448,7 +448,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } public function testProcessFilterRulesAndConditionWithEmptyMidResult() { @@ -475,7 +475,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'], ]; - $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } public function testProcessFilterRulesInvisibleTagAsAdmin() { @@ -517,7 +517,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } /** @@ -554,7 +554,7 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->plugin->processFilterRules($rules); + $this->invokePrivate($this->plugin, 'processFilterRules', [$rules]); } public function testProcessFilterRulesVisibleTagAsUser() { @@ -597,6 +597,6 @@ class FilesReportPlugin extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); } } diff --git a/apps/dav/tests/unit/systemtag/systemtagplugin.php b/apps/dav/tests/unit/systemtag/systemtagplugin.php index b026451701f..b945223e668 100644 --- a/apps/dav/tests/unit/systemtag/systemtagplugin.php +++ b/apps/dav/tests/unit/systemtag/systemtagplugin.php @@ -22,6 +22,8 @@ namespace OCA\DAV\Tests\Unit\SystemTag; use OC\SystemTag\SystemTag; +use OCP\IGroupManager; +use OCP\IUserSession; use OCP\SystemTag\TagAlreadyExistsException; class SystemTagPlugin extends \Test\TestCase { @@ -47,6 +49,16 @@ class SystemTagPlugin extends \Test\TestCase { private $tagManager; /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var IUserSession + */ + private $userSession; + + /** * @var \OCA\DAV\SystemTag\SystemTagPlugin */ private $plugin; @@ -60,8 +72,14 @@ class SystemTagPlugin extends \Test\TestCase { $this->server = new \Sabre\DAV\Server($this->tree); $this->tagManager = $this->getMock('\OCP\SystemTag\ISystemTagManager'); + $this->groupManager = $this->getMock('\OCP\IGroupManager'); + $this->userSession = $this->getMock('\OCP\IUserSession'); - $this->plugin = new \OCA\DAV\SystemTag\SystemTagPlugin($this->tagManager); + $this->plugin = new \OCA\DAV\SystemTag\SystemTagPlugin( + $this->tagManager, + $this->groupManager, + $this->userSession + ); $this->plugin->initialize($this->server); } @@ -153,7 +171,204 @@ class SystemTagPlugin extends \Test\TestCase { $this->assertEquals(200, $result[self::USERVISIBLE_PROPERTYNAME]); } + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Not sufficient permissions + */ + public function testCreateNotAssignableTagAsRegularUser() { + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('admin'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('admin') + ->willReturn(false); + + $requestData = json_encode([ + 'name' => 'Test', + 'userVisible' => true, + 'userAssignable' => false, + ]); + + $node = $this->getMockBuilder('\OCA\DAV\SystemTag\SystemTagsByIdCollection') + ->disableOriginalConstructor() + ->getMock(); + $this->tagManager->expects($this->never()) + ->method('createTag'); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/systemtags') + ->will($this->returnValue($node)); + + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('/systemtags')); + + $request->expects($this->once()) + ->method('getBodyAsString') + ->will($this->returnValue($requestData)); + + $request->expects($this->once()) + ->method('getHeader') + ->with('Content-Type') + ->will($this->returnValue('application/json')); + + $this->plugin->httpPost($request, $response); + } + + /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Not sufficient permissions + */ + public function testCreateInvisibleTagAsRegularUser() { + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('admin'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('admin') + ->willReturn(false); + + $requestData = json_encode([ + 'name' => 'Test', + 'userVisible' => false, + 'userAssignable' => true, + ]); + + $node = $this->getMockBuilder('\OCA\DAV\SystemTag\SystemTagsByIdCollection') + ->disableOriginalConstructor() + ->getMock(); + $this->tagManager->expects($this->never()) + ->method('createTag'); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/systemtags') + ->will($this->returnValue($node)); + + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('/systemtags')); + + $request->expects($this->once()) + ->method('getBodyAsString') + ->will($this->returnValue($requestData)); + + $request->expects($this->once()) + ->method('getHeader') + ->with('Content-Type') + ->will($this->returnValue('application/json')); + + $this->plugin->httpPost($request, $response); + } + + public function testCreateTagInByIdCollectionAsRegularUser() { + $systemTag = new SystemTag(1, 'Test', true, false); + + $requestData = json_encode([ + 'name' => 'Test', + 'userVisible' => true, + 'userAssignable' => true, + ]); + + $node = $this->getMockBuilder('\OCA\DAV\SystemTag\SystemTagsByIdCollection') + ->disableOriginalConstructor() + ->getMock(); + $this->tagManager->expects($this->once()) + ->method('createTag') + ->with('Test', true, true) + ->will($this->returnValue($systemTag)); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/systemtags') + ->will($this->returnValue($node)); + + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('/systemtags')); + + $request->expects($this->once()) + ->method('getBodyAsString') + ->will($this->returnValue($requestData)); + + $request->expects($this->once()) + ->method('getHeader') + ->with('Content-Type') + ->will($this->returnValue('application/json')); + + $request->expects($this->once()) + ->method('getUrl') + ->will($this->returnValue('http://example.com/dav/systemtags')); + + $response->expects($this->once()) + ->method('setHeader') + ->with('Content-Location', 'http://example.com/dav/systemtags/1'); + + $this->plugin->httpPost($request, $response); + } + public function testCreateTagInByIdCollection() { + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('admin'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('admin') + ->willReturn(true); + $systemTag = new SystemTag(1, 'Test', true, false); $requestData = json_encode([ @@ -214,6 +429,24 @@ class SystemTagPlugin extends \Test\TestCase { } public function testCreateTagInMappingCollection() { + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('admin'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('admin') + ->willReturn(true); + $systemTag = new SystemTag(1, 'Test', true, false); $requestData = json_encode([ @@ -307,9 +540,27 @@ class SystemTagPlugin extends \Test\TestCase { /** * @dataProvider nodeClassProvider - * @expectedException Sabre\DAV\Exception\Conflict + * @expectedException \Sabre\DAV\Exception\Conflict */ public function testCreateTagConflict($nodeClass) { + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->willReturn('admin'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + $this->groupManager + ->expects($this->once()) + ->method('isAdmin') + ->with('admin') + ->willReturn(true); + $requestData = json_encode([ 'name' => 'Test', 'userVisible' => true, diff --git a/apps/files_sharing/css/public.css b/apps/files_sharing/css/public.css index bd8e98e966d..d09947dab26 100644 --- a/apps/files_sharing/css/public.css +++ b/apps/files_sharing/css/public.css @@ -1,7 +1,11 @@ #content { height: initial; min-height: calc(100vh - 120px); - overflow: hidden; +} + +/* force layout to make sure the content element's height matches its contents' height */ +.ie #content { + display: inline-block; } #preview { diff --git a/autotest.sh b/autotest.sh index 61c21dc0ba2..d8d75817712 100755 --- a/autotest.sh +++ b/autotest.sh @@ -13,8 +13,6 @@ # @copyright 2012-2015 Thomas Müller thomas.mueller@tmit.eu # -set -e - #$EXECUTOR_NUMBER is set by Jenkins and allows us to run autotest in parallel DATABASENAME=oc_autotest$EXECUTOR_NUMBER DATABASEUSER=oc_autotest$EXECUTOR_NUMBER @@ -34,6 +32,8 @@ fi PHP=$(which "$PHP_EXE") PHPUNIT=$(which phpunit) +set -e + _XDEBUG_CONFIG=$XDEBUG_CONFIG unset XDEBUG_CONFIG diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml index a87d4fe44c6..a1f9d610c68 100644 --- a/build/integration/config/behat.yml +++ b/build/integration/config/behat.yml @@ -14,6 +14,8 @@ default: regular_user_password: 123456 - CommentsContext: baseUrl: http://localhost:8080 + - TagsContext: + baseUrl: http://localhost:8080 federation: paths: - %paths.base%/../federation_features diff --git a/build/integration/features/bootstrap/TagsContext.php b/build/integration/features/bootstrap/TagsContext.php new file mode 100644 index 00000000000..5e1f62ba838 --- /dev/null +++ b/build/integration/features/bootstrap/TagsContext.php @@ -0,0 +1,482 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +require __DIR__ . '/../../vendor/autoload.php'; + +use Behat\Gherkin\Node\TableNode; +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +class TagsContext implements \Behat\Behat\Context\Context { + /** @var string */ + private $baseUrl; + /** @var Client */ + private $client; + /** @var ResponseInterface */ + private $response; + + /** + * @param string $baseUrl + */ + public function __construct($baseUrl) { + $this->baseUrl = $baseUrl; + + // in case of ci deployment we take the server url from the environment + $testServerUrl = getenv('TEST_SERVER_URL'); + if ($testServerUrl !== false) { + $this->baseUrl = substr($testServerUrl, 0, -5); + } + } + + /** @BeforeScenario */ + public function tearUpScenario() { + $this->client = new Client(); + } + + /** @AfterScenario */ + public function tearDownScenario() { + $user = 'admin'; + $tags = $this->requestTagsForUser($user); + foreach($tags as $tagId => $tag) { + $this->response = $this->client->delete( + $this->baseUrl . '/remote.php/dav/systemtags/'.$tagId, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + } + try { + $this->client->delete( + $this->baseUrl . '/remote.php/webdav/myFileToTag.txt', + [ + 'auth' => [ + 'user0', + '123456', + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + } catch (\GuzzleHttp\Exception\ClientException $e) {} + } + + /** + * @param string $userName + * @return string + */ + private function getPasswordForUser($userName) { + if($userName === 'admin') { + return 'admin'; + } + return '123456'; + } + + /** + * @When :user creates a :type tag with name :name + */ + public function createsATagWithName($user, $type, $name) { + $userVisible = 'true'; + $userAssignable = 'true'; + switch ($type) { + case 'normal': + break; + case 'not user-assignable': + $userAssignable = 'false'; + break; + case 'not user-visible': + $userVisible = 'false'; + break; + default: + throw new \Exception('Unsupported type'); + } + + try { + $this->response = $this->client->post( + $this->baseUrl . '/remote.php/dav/systemtags/', + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => '{"name":"'.$name.'","userVisible":'.$userVisible.',"userAssignable":'.$userAssignable.'}', + ] + ); + } catch (\GuzzleHttp\Exception\ClientException $e){ + $this->response = $e->getResponse(); + } + } + + /** + * @Then The response should have a status code :statusCode + */ + public function theResponseShouldHaveAStatusCode($statusCode) { + if((int)$statusCode !== $this->response->getStatusCode()) { + throw new \Exception("Expected $statusCode, got ".$this->response->getStatusCode()); + } + } + + /** + * Returns all tags for a given user + * + * @param string $user + * @return array + */ + private function requestTagsForUser($user) { + try { + $request = $this->client->createRequest( + 'PROPFIND', + $this->baseUrl . '/remote.php/dav/systemtags/', + [ + 'body' => '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:prop> + <oc:id /> + <oc:display-name /> + <oc:user-visible /> + <oc:user-assignable /> + </d:prop> +</d:propfind>', + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + $this->response = $this->client->send($request); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + + $tags = []; + $service = new Sabre\Xml\Service(); + $parsed = $service->parse($this->response->getBody()->getContents()); + foreach($parsed as $entry) { + $singleEntry = $entry['value'][1]['value'][0]['value']; + if(empty($singleEntry[0]['value'])) { + continue; + } + + $tags[$singleEntry[0]['value']] = [ + 'display-name' => $singleEntry[1]['value'], + 'user-visible' => $singleEntry[2]['value'], + 'user-assignable' => $singleEntry[3]['value'], + ]; + } + + return $tags; + } + + /** + * @Then The following tags should exist for :user + */ + public function theFollowingTagsShouldExistFor($user, TableNode $table) { + $tags = $this->requestTagsForUser($user); + + if(count($table->getRows()) !== count($tags)) { + throw new \Exception( + sprintf( + "Expected %s tags, got %s.", + count($table->getRows()), + count($tags) + ) + ); + } + + foreach($table->getRowsHash() as $rowDisplayName => $row) { + foreach($tags as $key => $tag) { + if( + $tag['display-name'] === $rowDisplayName && + $tag['user-visible'] === $row[0] && + $tag['user-assignable'] === $row[1] + ) { + unset($tags[$key]); + } + } + } + if(count($tags) !== 0) { + throw new \Exception('Not expected response'); + } + } + + /** + * @Then :count tags should exist for :user + */ + public function tagsShouldExistFor($count, $user) { + if((int)$count !== count($this->requestTagsForUser($user))) { + throw new \Exception("Expected $count tags, got ".count($this->requestTagsForUser($user))); + } + } + + /** + * @param string $name + * @return int + */ + private function findTagIdByName($name) { + $tags = $this->requestTagsForUser('admin'); + $tagId = 0; + foreach($tags as $id => $tag) { + if($tag['display-name'] === $name) { + $tagId = $id; + break; + } + } + return (int)$tagId; + } + + /** + * @When :user edits the tag with name :oldNmae and sets its name to :newName + */ + public function editsTheTagWithNameAndSetsItsNameTo($user, $oldName, $newName) { + $tagId = $this->findTagIdByName($oldName); + if($tagId === 0) { + throw new \Exception('Could not find tag to rename'); + } + + try { + $request = $this->client->createRequest( + 'PROPPATCH', + $this->baseUrl . '/remote.php/dav/systemtags/' . $tagId, + [ + 'body' => '<?xml version="1.0"?> +<d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:set> + <d:prop> + <oc:display-name>' . $newName . '</oc:display-name> + </d:prop> + </d:set> +</d:propertyupdate>', + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + ] + ); + $this->response = $this->client->send($request); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @When :user deletes the tag with name :name + */ + public function deletesTheTagWithName($user, $name) { + $tagId = $this->findTagIdByName($name); + try { + $this->response = $this->client->delete( + $this->baseUrl . '/remote.php/dav/systemtags/' . $tagId, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @param string $path + * @param string $user + * @return int + */ + private function getFileIdForPath($path, $user) { + $url = $this->baseUrl.'/remote.php/webdav/'.$path; + $credentials = base64_encode($user .':'.$this->getPasswordForUser($user)); + $context = stream_context_create(array( + 'http' => array( + 'method' => 'PROPFIND', + 'header' => "Authorization: Basic $credentials\r\nContent-Type: application/x-www-form-urlencoded", + 'content' => '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:prop> + <oc:fileid /> + </d:prop> +</d:propfind>' + ) + )); + $response = file_get_contents($url, false, $context); + preg_match_all('/\<oc:fileid\>(.*)\<\/oc:fileid\>/', $response, $matches); + return (int)$matches[1][0]; + } + + /** + * @When :taggingUser adds the tag :tagName to :fileName shared by :sharingUser + */ + public function addsTheTagToSharedBy($taggingUser, $tagName, $fileName, $sharingUser) { + $fileId = $this->getFileIdForPath($fileName, $sharingUser); + $tagId = $this->findTagIdByName($tagName); + + try { + $this->response = $this->client->put( + $this->baseUrl.'/remote.php/dav/systemtags-relations/files/'.$fileId.'/'.$tagId, + [ + 'auth' => [ + $taggingUser, + $this->getPasswordForUser($taggingUser), + ] + ] + ); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @Then :fileName shared by :sharingUser has the following tags + */ + public function sharedByHasTheFollowingTags($fileName, $sharingUser, TableNode $table) { + $loadedExpectedTags = $table->getTable(); + $expectedTags = []; + foreach($loadedExpectedTags as $expected) { + $expectedTags[] = $expected[0]; + } + + // Get the real tags + $request = $this->client->createRequest( + 'PROPFIND', + $this->baseUrl.'/remote.php/dav/systemtags-relations/files/'.$this->getFileIdForPath($fileName, $sharingUser), + [ + 'auth' => [ + $sharingUser, + $this->getPasswordForUser($sharingUser), + ], + 'body' => '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:prop> + <oc:id /> + <oc:display-name /> + <oc:user-visible /> + <oc:user-assignable /> + </d:prop> +</d:propfind>', + ] + ); + $response = $this->client->send($request)->getBody()->getContents(); + preg_match_all('/\<oc:display-name\>(.*)\<\/oc:display-name\>/', $response, $realTags); + + foreach($expectedTags as $key => $row) { + foreach($realTags as $tag) { + if($tag[0] === $row) { + unset($expectedTags[$key]); + } + } + } + + if(count($expectedTags) !== 0) { + throw new \Exception('Not all tags found.'); + } + } + + /** + * @Then :fileName shared by :sharingUser has the following tags for :user + */ + public function sharedByHasTheFollowingTagsFor($fileName, $sharingUser, $user, TableNode $table) { + $loadedExpectedTags = $table->getTable(); + $expectedTags = []; + foreach($loadedExpectedTags as $expected) { + $expectedTags[] = $expected[0]; + } + + // Get the real tags + try { + $request = $this->client->createRequest( + 'PROPFIND', + $this->baseUrl . '/remote.php/dav/systemtags-relations/files/' . $this->getFileIdForPath($fileName, $sharingUser), + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + 'body' => '<?xml version="1.0"?> +<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"> + <d:prop> + <oc:id /> + <oc:display-name /> + <oc:user-visible /> + <oc:user-assignable /> + </d:prop> +</d:propfind>', + ] + ); + $this->response = $this->client->send($request)->getBody()->getContents(); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + preg_match_all('/\<oc:display-name\>(.*)\<\/oc:display-name\>/', $this->response, $realTags); + $realTags = array_filter($realTags); + $expectedTags = array_filter($expectedTags); + + foreach($expectedTags as $key => $row) { + foreach($realTags as $tag) { + foreach($tag as $index => $foo) { + if($tag[$index] === $row) { + unset($expectedTags[$key]); + } + } + } + } + + if(count($expectedTags) !== 0) { + throw new \Exception('Not all tags found.'); + } + } + + /** + * @When :user removes the tag :tagName from :fileName shared by :shareUser + */ + public function removesTheTagFromSharedBy($user, $tagName, $fileName, $shareUser) { + $tagId = $this->findTagIdByName($tagName); + $fileId = $this->getFileIdForPath($fileName, $shareUser); + + try { + $this->response = $this->client->delete( + $this->baseUrl.'/remote.php/dav/systemtags-relations/files/'.$fileId.'/'.$tagId, + [ + 'auth' => [ + $user, + $this->getPasswordForUser($user), + ], + ] + ); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } +} diff --git a/build/integration/features/tags.feature b/build/integration/features/tags.feature new file mode 100644 index 00000000000..286fb62bf42 --- /dev/null +++ b/build/integration/features/tags.feature @@ -0,0 +1,370 @@ +Feature: tags + + Scenario: Creating a normal tag as regular user should work + Given user "user0" exists + When "user0" creates a "normal" tag with name "MySuperAwesomeTagName" + Then The response should have a status code "201" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|true|true| + And The following tags should exist for "user0" + |MySuperAwesomeTagName|true|true| + + Scenario: Creating a not user-assignable tag as regular user should fail + Given user "user0" exists + When "user0" creates a "not user-assignable" tag with name "MySuperAwesomeTagName" + Then The response should have a status code "400" + And "0" tags should exist for "admin" + + Scenario: Creating a not user-visible tag as regular user should fail + Given user "user0" exists + When "user0" creates a "not user-visible" tag with name "MySuperAwesomeTagName" + Then The response should have a status code "400" + And "0" tags should exist for "admin" + + Scenario: Renaming a normal tag as regular user should work + Given user "user0" exists + Given "admin" creates a "normal" tag with name "MySuperAwesomeTagName" + When "user0" edits the tag with name "MySuperAwesomeTagName" and sets its name to "AnotherTagName" + Then The response should have a status code "207" + And The following tags should exist for "admin" + |AnotherTagName|true|true| + + Scenario: Renaming a not user-assignable tag as regular user should fail + Given user "user0" exists + Given "admin" creates a "not user-assignable" tag with name "MySuperAwesomeTagName" + When "user0" edits the tag with name "MySuperAwesomeTagName" and sets its name to "AnotherTagName" + Then The response should have a status code "403" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|true|false| + + Scenario: Renaming a not user-visible tag as regular user should fail + Given user "user0" exists + Given "admin" creates a "not user-visible" tag with name "MySuperAwesomeTagName" + When "user0" edits the tag with name "MySuperAwesomeTagName" and sets its name to "AnotherTagName" + Then The response should have a status code "404" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|false|true| + + Scenario: Deleting a normal tag as regular user should work + Given user "user0" exists + Given "admin" creates a "normal" tag with name "MySuperAwesomeTagName" + When "user0" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "204" + And "0" tags should exist for "admin" + + Scenario: Deleting a not user-assignable tag as regular user should fail + Given user "user0" exists + Given "admin" creates a "not user-assignable" tag with name "MySuperAwesomeTagName" + When "user0" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "403" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|true|false| + + Scenario: Deleting a not user-visible tag as regular user should fail + Given user "user0" exists + Given "admin" creates a "not user-visible" tag with name "MySuperAwesomeTagName" + When "user0" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "404" + And The following tags should exist for "admin" + |MySuperAwesomeTagName|false|true| + + Scenario: Deleting a not user-assignable tag as admin should work + Given "admin" creates a "not user-assignable" tag with name "MySuperAwesomeTagName" + When "admin" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "204" + And "0" tags should exist for "admin" + + Scenario: Deleting a not user-visible tag as admin should work + Given "admin" creates a "not user-visible" tag with name "MySuperAwesomeTagName" + When "admin" deletes the tag with name "MySuperAwesomeTagName" + Then The response should have a status code "204" + And "0" tags should exist for "admin" + + Scenario: Assigning a normal tag to a file shared by someone else as regular user should work + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MySuperAwesomeTagName" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + When "user1" adds the tag "MySuperAwesomeTagName" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "201" + And "/myFileToTag.txt" shared by "user0" has the following tags + |MySuperAwesomeTagName| + + Scenario: Assigning a normal tag to a file belonging to someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + When "user1" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "404" + And "/myFileToTag.txt" shared by "user0" has the following tags + |MyFirstTag| + + Scenario: Assigning a not user-assignable tag to a file shared by someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "not user-assignable" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + When "user1" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "403" + And "/myFileToTag.txt" shared by "user0" has the following tags + |MyFirstTag| + + Scenario: Assigning a not user-visible tag to a file shared by someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "not user-visible" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + When "user1" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "412" + And "/myFileToTag.txt" shared by "user0" has the following tags + |MyFirstTag| + + Scenario: Assigning a not user-visible tag to a file shared by someone else as admin user should work + Given user "user0" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "not user-visible" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + When "admin" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "201" + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MyFirstTag| + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MyFirstTag| + + Scenario: Assigning a not user-assignable tag to a file shared by someone else as admin user should worj + Given user "user0" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "not user-assignable" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + When "admin" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "201" + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MyFirstTag| + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MyFirstTag| + |MySecondTag| + + Scenario: Unassigning a normal tag from a file shared by someone else as regular user should work + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "user1" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "204" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MySecondTag| + + Scenario: Unassigning a normal tag from a file unshared by someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "user1" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "404" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MyFirstTag| + |MySecondTag| + + Scenario: Unassigning a not user-visible tag from a file shared by someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-visible" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "user1" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "404" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MyFirstTag| + |MySecondTag| + + Scenario: Unassigning a not user-visible tag from a file shared by someone else as admin should work + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-visible" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "admin" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "204" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MySecondTag| + + Scenario: Unassigning a not user-visible tag from a file unshared by someone else should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-visible" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Given As "user0" remove all shares from the file named "/myFileToTag.txt" + When "admin" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "404" + + Scenario: Unassigning a not user-assignable tag from a file shared by someone else as regular user should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-assignable" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "user1" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "403" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MyFirstTag| + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MyFirstTag| + |MySecondTag| + + Scenario: Unassigning a not user-assignable tag from a file shared by someone else as admin should work + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-assignable" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + When "admin" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "204" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MySecondTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "admin" + |MySecondTag| + + Scenario: Unassigning a not user-assignable tag from a file unshared by someone else should fail + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "not user-assignable" tag with name "MyFirstTag" + Given "admin" creates a "normal" tag with name "MySecondTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | user1 | + | shareType | 0 | + Given As "user0" sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | myFileToTag.txt | + | shareWith | admin | + | shareType | 0 | + Given "admin" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + Given "user0" adds the tag "MySecondTag" to "/myFileToTag.txt" shared by "user0" + Given As "user0" remove all shares from the file named "/myFileToTag.txt" + When "admin" removes the tag "MyFirstTag" from "/myFileToTag.txt" shared by "user0" + Then The response should have a status code "404" + + Scenario: Overwriting existing normal tags should fail + Given user "user0" exists + Given "user0" creates a "normal" tag with name "MyFirstTag" + When "user0" creates a "normal" tag with name "MyFirstTag" + Then The response should have a status code "409" + + Scenario: Overwriting existing not user-assignable tags should fail + Given "admin" creates a "not user-assignable" tag with name "MyFirstTag" + When "admin" creates a "not user-assignable" tag with name "MyFirstTag" + Then The response should have a status code "409" + + Scenario: Overwriting existing not user-visible tags should fail + Given "admin" creates a "not user-visible" tag with name "MyFirstTag" + When "admin" creates a "not user-visible" tag with name "MyFirstTag" + Then The response should have a status code "409" + + Scenario: Getting tags only works with access to the file + Given user "user0" exists + Given user "user1" exists + Given "admin" creates a "normal" tag with name "MyFirstTag" + Given user "user0" uploads file "data/textfile.txt" to "/myFileToTag.txt" + When "user0" adds the tag "MyFirstTag" to "/myFileToTag.txt" shared by "user0" + And "/myFileToTag.txt" shared by "user0" has the following tags for "user0" + |MyFirstTag| + And "/myFileToTag.txt" shared by "user0" has the following tags for "user1" + || + And The response should have a status code "404" diff --git a/lib/private/avatarmanager.php b/lib/private/avatarmanager.php index 21f88b1fd3f..b2d3e6eb3dd 100644 --- a/lib/private/avatarmanager.php +++ b/lib/private/avatarmanager.php @@ -27,6 +27,7 @@ namespace OC; use OCP\Files\Folder; +use OCP\Files\NotFoundException; use OCP\IAvatarManager; use OCP\IUserManager; use OCP\Files\IRootFolder; @@ -68,6 +69,7 @@ class AvatarManager implements IAvatarManager { * @param string $userId the ownCloud user id * @return \OCP\IAvatar * @throws \Exception In case the username is potentially dangerous + * @throws NotFoundException In case there is no user folder yet */ public function getAvatar($userId) { $user = $this->userManager->get($userId); diff --git a/lib/private/preview/movie.php b/lib/private/preview/movie.php index ee56f017229..43a8d674fc9 100644 --- a/lib/private/preview/movie.php +++ b/lib/private/preview/movie.php @@ -83,9 +83,9 @@ class Movie extends Provider { $tmpPath = \OC::$server->getTempManager()->getTemporaryFile(); if (self::$avconvBinary) { - $cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) . + $cmd = self::$avconvBinary . ' -y -ss ' . escapeshellarg($second) . ' -i ' . escapeshellarg($absPath) . - ' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . + ' -an -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . ' > /dev/null 2>&1'; } else { $cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) . diff --git a/lib/public/encryption/iencryptionmodule.php b/lib/public/encryption/iencryptionmodule.php index 45e0b79c2a9..df30dd57cee 100644 --- a/lib/public/encryption/iencryptionmodule.php +++ b/lib/public/encryption/iencryptionmodule.php @@ -71,29 +71,41 @@ interface IEncryptionModule { * buffer. * * @param string $path to the file + * @param string $position id of the last block (looks like "<Number>end") + * * @return string remained data which should be written to the file in case * of a write operation + * * @since 8.1.0 + * @since 9.0.0 parameter $position added */ - public function end($path); + public function end($path, $position); /** * encrypt data * * @param string $data you want to encrypt + * @param string $position position of the block we want to encrypt (starts with '0') + * * @return mixed encrypted data + * * @since 8.1.0 + * @since 9.0.0 parameter $position added */ - public function encrypt($data); + public function encrypt($data, $position); /** * decrypt data * * @param string $data you want to decrypt + * @param string $position position of the block we want to decrypt + * * @return mixed decrypted data + * * @since 8.1.0 + * @since 9.0.0 parameter $position added */ - public function decrypt($data); + public function decrypt($data, $position); /** * update encrypted file, e.g. give additional users access to the file diff --git a/lib/public/iavatarmanager.php b/lib/public/iavatarmanager.php index 264c4fcf051..cb63ccaf6fd 100644 --- a/lib/public/iavatarmanager.php +++ b/lib/public/iavatarmanager.php @@ -36,6 +36,8 @@ interface IAvatarManager { * @see \OCP\IAvatar * @param string $user the ownCloud user id * @return \OCP\IAvatar + * @throws \Exception In case the username is potentially dangerous + * @throws \OCP\Files\NotFoundException In case there is no user folder yet * @since 6.0.0 */ public function getAvatar($user); diff --git a/settings/controller/userscontroller.php b/settings/controller/userscontroller.php index 3e5455751ab..0abcabed11c 100644 --- a/settings/controller/userscontroller.php +++ b/settings/controller/userscontroller.php @@ -176,7 +176,11 @@ class UsersController extends Controller { $avatarAvailable = false; if ($this->config->getSystemValue('enable_avatars', true) === true) { - $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists(); + try { + $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists(); + } catch (\Exception $e) { + //No avatar yet + } } return [ diff --git a/tests/settings/controller/userscontrollertest.php b/tests/settings/controller/userscontrollertest.php index 947540fa67b..6f07f34ba8d 100644 --- a/tests/settings/controller/userscontrollertest.php +++ b/tests/settings/controller/userscontrollertest.php @@ -1696,6 +1696,32 @@ class UsersControllerTest extends \Test\TestCase { $this->assertEquals($expectedResult, $result); } + public function testNoAvatar() { + $this->container['IsAdmin'] = true; + + list($user, $expectedResult) = $this->mockUser(); + + $subadmin = $this->getMockBuilder('\OC\SubAdmin') + ->disableOriginalConstructor() + ->getMock(); + $subadmin->expects($this->once()) + ->method('getSubAdminsGroups') + ->with($user) + ->will($this->returnValue([])); + $this->container['GroupManager'] + ->expects($this->any()) + ->method('getSubAdmin') + ->will($this->returnValue($subadmin)); + + $this->container['OCP\\IAvatarManager'] + ->method('getAvatar') + ->will($this->throwException(new \OCP\Files\NotFoundException())); + $expectedResult['isAvatarAvailable'] = false; + + $result = self::invokePrivate($this->container['UsersController'], 'formatUserForIndex', [$user]); + $this->assertEquals($expectedResult, $result); + } + /** * @return array */ |