diff options
author | Sergio BertolÃn <sbertolin@solidgear.es> | 2016-07-28 16:52:08 +0300 |
---|---|---|
committer | Thomas Müller <DeepDiver1975@users.noreply.github.com> | 2016-07-28 16:52:08 +0300 |
commit | 396a4ad9a04520731c9106f5d84d9abca6c63d6f (patch) | |
tree | aa1eee353482ac9a096830bcf606025a70b0f5e3 /build | |
parent | 023a525e1710145ef252be526a4fe230b3f13e84 (diff) |
Stable9 backport integration tests (#25631)
* Bring all tests from master
* Removed not applying tests of webdav
* Removed tests about disabling/enabling users
* Fixed/removed some sharing tests for 9.0
* fixed capabilities tests
* Fixed security headers confusion
* Indentation
* Removed merging shares tests
Diffstat (limited to 'build')
25 files changed, 1491 insertions, 437 deletions
diff --git a/build/integration/capabilities_features/capabilities.feature b/build/integration/capabilities_features/capabilities.feature index 3c1eb025ec7..48da09b698a 100644 --- a/build/integration/capabilities_features/capabilities.feature +++ b/build/integration/capabilities_features/capabilities.feature @@ -16,6 +16,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -35,6 +36,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -73,6 +75,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -92,6 +95,7 @@ Feature: capabilities | files_sharing | resharing | EMPTY | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -111,6 +115,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | EMPTY | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -130,6 +135,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | EMPTY | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -150,6 +156,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -170,6 +177,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -190,6 +198,7 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | @@ -212,6 +221,27 @@ Feature: capabilities | files_sharing | resharing | 1 | | files_sharing | federation@@@outgoing | 1 | | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | + | files | bigfilechunking | 1 | + | files | undelete | 1 | + | files | versioning | 1 | + + Scenario: Changing group sharing allowed + Given As an "admin" + And parameter "shareapi_allow_group_sharing" of app "core" is set to "no" + When sending "GET" to "/cloud/capabilities" + Then the HTTP status code should be "200" + And fields of capabilities match with + | capability | path_to_element | value | + | core | pollinterval | 60 | + | core | webdav-root | remote.php/webdav | + | files_sharing | api_enabled | 1 | + | files_sharing | public@@@enabled | 1 | + | files_sharing | public@@@upload | 1 | + | files_sharing | resharing | 1 | + | files_sharing | federation@@@outgoing | 1 | + | files_sharing | federation@@@incoming | 1 | + | files_sharing | group_sharing | EMPTY | | files | bigfilechunking | 1 | | files | undelete | 1 | | files | versioning | 1 | diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml index 4b5b5b16ef8..aed2d962682 100644 --- a/build/integration/config/behat.yml +++ b/build/integration/config/behat.yml @@ -14,14 +14,14 @@ default: regular_user_password: 123456 - CommentsContext: baseUrl: http://localhost:8080 - - TagsContext: - baseUrl: http://localhost:8080 - CardDavContext: baseUrl: http://localhost:8080 - CalDavContext: baseUrl: http://localhost:8080 - ChecksumsContext: baseUrl: http://localhost:8080 + - TagsContext: + baseUrl: http://localhost:8080 federation: paths: - %paths.base%/../federation_features @@ -42,6 +42,16 @@ default: - admin - admin regular_user_password: 123456 + sharees: + paths: + - %paths.base%/../sharees_features + contexts: + - ShareesContext: + baseUrl: http://localhost:8080/ocs/ + admin: + - admin + - admin + regular_user_password: 123456 diff --git a/build/integration/features/bootstrap/AppConfiguration.php b/build/integration/features/bootstrap/AppConfiguration.php new file mode 100644 index 00000000000..af904a30896 --- /dev/null +++ b/build/integration/features/bootstrap/AppConfiguration.php @@ -0,0 +1,103 @@ +<?php + +use Behat\Behat\Hook\Scope\AfterScenarioScope; +use Behat\Behat\Hook\Scope\BeforeScenarioScope; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + +trait AppConfiguration { + /** @var string */ + private $currentUser = ''; + + /** @var ResponseInterface */ + private $response = null; + + abstract public function sendingTo($verb, $url); + abstract public function sendingToWith($verb, $url, $body); + abstract public function theOCSStatusCodeShouldBe($statusCode); + abstract public function theHTTPStatusCodeShouldBe($statusCode); + + /** + * @Given /^parameter "([^"]*)" of app "([^"]*)" is set to "([^"]*)"$/ + * @param string $parameter + * @param string $app + * @param string $value + */ + public function serverParameterIsSetTo($parameter, $app, $value) { + $user = $this->currentUser; + $this->currentUser = 'admin'; + + $this->modifyServerConfig($app, $parameter, $value); + + $this->currentUser = $user; + } + + /** + * @param string $app + * @param string $parameter + * @param string $value + */ + protected function modifyServerConfig($app, $parameter, $value) { + $body = new \Behat\Gherkin\Node\TableNode([['value', $value]]); + $this->sendingToWith('post', "/apps/testing/api/v1/app/{$app}/{$parameter}", $body); + $this->theHTTPStatusCodeShouldBe('200'); + $this->theOCSStatusCodeShouldBe('100'); + } + + protected function setStatusTestingApp($enabled) { + $this->sendingTo(($enabled ? 'post' : 'delete'), '/cloud/apps/testing'); + $this->theHTTPStatusCodeShouldBe('200'); + $this->theOCSStatusCodeShouldBe('100'); + + $this->sendingTo('get', '/cloud/apps?filter=enabled'); + $this->theHTTPStatusCodeShouldBe('200'); + if ($enabled) { + PHPUnit_Framework_Assert::assertContains('testing', $this->response->getBody()->getContents()); + } else { + PHPUnit_Framework_Assert::assertNotContains('testing', $this->response->getBody()->getContents()); + } + } + + abstract protected function resetAppConfigs(); + + /** + * @BeforeScenario + * + * Enable the testing app before the first scenario of the feature and + * reset the configs before each scenario + * @param BeforeScenarioScope $event + */ + public function prepareParameters(BeforeScenarioScope $event){ + $user = $this->currentUser; + $this->currentUser = 'admin'; + + $scenarios = $event->getFeature()->getScenarios(); + if ($event->getScenario() === reset($scenarios)) { + $this->setStatusTestingApp(true); + } + + $this->resetAppConfigs(); + + $this->currentUser = $user; + } + + /** + * @AfterScenario + * + * Reset the values after the last scenario of the feature and disable the testing app + * @param AfterScenarioScope $event + */ + public function undoChangingParameters(AfterScenarioScope $event) { + $scenarios = $event->getFeature()->getScenarios(); + if ($event->getScenario() === end($scenarios)) { + $user = $this->currentUser; + $this->currentUser = 'admin'; + + $this->resetAppConfigs(); + + $this->setStatusTestingApp(false); + $this->currentUser = $user; + } + } +} diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php index 0084597836f..f693a242e17 100644 --- a/build/integration/features/bootstrap/BasicStructure.php +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -1,13 +1,12 @@ <?php -use Behat\Behat\Context\Context; -use Behat\Behat\Context\SnippetAcceptingContext; use GuzzleHttp\Client; use GuzzleHttp\Message\ResponseInterface; require __DIR__ . '/../../vendor/autoload.php'; trait BasicStructure { + /** @var string */ private $currentUser = ''; @@ -17,6 +16,9 @@ trait BasicStructure { /** @var string */ private $baseUrl = ''; + /** @var int */ + private $apiVersion = 1; + /** @var ResponseInterface */ private $response = null; @@ -24,7 +26,7 @@ trait BasicStructure { private $cookieJar; /** @var string */ - private $requesttoken; + private $requestToken; public function __construct($baseUrl, $admin, $regular_user_password) { @@ -32,8 +34,8 @@ trait BasicStructure { $this->baseUrl = $baseUrl; $this->adminUser = $admin; $this->regularUser = $regular_user_password; - $this->localBaseUrl = substr($this->baseUrl, 0, -4); - $this->remoteBaseUrl = substr($this->baseUrl, 0, -4); + $this->localBaseUrl = $this->baseUrl; + $this->remoteBaseUrl = $this->baseUrl; $this->currentServer = 'LOCAL'; $this->cookieJar = new \GuzzleHttp\Cookie\CookieJar(); @@ -52,29 +54,43 @@ trait BasicStructure { } /** + * @Given /^using api version "([^"]*)"$/ + * @param string $version + */ + public function usingApiVersion($version) { + $this->apiVersion = $version; + } + + /** * @Given /^As an "([^"]*)"$/ + * @param string $user */ public function asAn($user) { $this->currentUser = $user; } /** - * @Given /^Using server "([^"]*)"$/ + * @Given /^Using server "(LOCAL|REMOTE)"$/ + * @param string $server + * @return string Previous used server */ public function usingServer($server) { + $previousServer = $this->currentServer; if ($server === 'LOCAL'){ $this->baseUrl = $this->localBaseUrl; $this->currentServer = 'LOCAL'; - } elseif ($server === 'REMOTE'){ + return $previousServer; + } else { $this->baseUrl = $this->remoteBaseUrl; $this->currentServer = 'REMOTE'; - } else{ - PHPUnit_Framework_Assert::fail("Server can only be LOCAL or REMOTE"); + return $previousServer; } } /** * @When /^sending "([^"]*)" to "([^"]*)"$/ + * @param string $verb + * @param string $url */ public function sendingTo($verb, $url) { $this->sendingToWith($verb, $url, null); @@ -92,6 +108,8 @@ trait BasicStructure { /** * This function is needed to use a vertical fashion in the gherkin tables. + * @param array $arrayOfArrays + * @return array */ public function simplifyArray($arrayOfArrays){ $a = array_map(function($subArray) { return $subArray[0]; }, $arrayOfArrays); @@ -100,6 +118,9 @@ trait BasicStructure { /** * @When /^sending "([^"]*)" to "([^"]*)" with$/ + * @param string $verb + * @param string $url + * @param \Behat\Gherkin\Node\TableNode $body */ public function sendingToWith($verb, $url, $body) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url; @@ -130,6 +151,7 @@ trait BasicStructure { /** * @Then /^the OCS status code should be "([^"]*)"$/ + * @param int $statusCode */ public function theOCSStatusCodeShouldBe($statusCode) { PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response)); @@ -137,6 +159,7 @@ trait BasicStructure { /** * @Then /^the HTTP status code should be "([^"]*)"$/ + * @param int $statusCode */ public function theHTTPStatusCodeShouldBe($statusCode) { PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode()); @@ -146,14 +169,15 @@ trait BasicStructure { * @param ResponseInterface $response */ private function extracRequestTokenFromResponse(ResponseInterface $response) { - $this->requesttoken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89); + $this->requestToken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89); } /** * @Given Logging in using web as :user + * @param string $user */ public function loggingInUsingWebAs($user) { - $loginUrl = substr($this->baseUrl, 0, -5); + $loginUrl = substr($this->baseUrl, 0, -5) . '/login'; // Request a new session and extract CSRF token $client = new Client(); $response = $client->get( @@ -173,7 +197,7 @@ trait BasicStructure { 'body' => [ 'user' => $user, 'password' => $password, - 'requesttoken' => $this->requesttoken, + 'requesttoken' => $this->requestToken, ], 'cookies' => $this->cookieJar, ] @@ -183,6 +207,8 @@ trait BasicStructure { /** * @When Sending a :method to :url with requesttoken + * @param string $method + * @param string $url */ public function sendingAToWithRequesttoken($method, $url) { $baseUrl = substr($this->baseUrl, 0, -5); @@ -195,7 +221,7 @@ trait BasicStructure { 'cookies' => $this->cookieJar, ] ); - $request->addHeader('requesttoken', $this->requesttoken); + $request->addHeader('requesttoken', $this->requestToken); try { $this->response = $client->send($request); } catch (\GuzzleHttp\Exception\ClientException $e) { @@ -205,6 +231,8 @@ trait BasicStructure { /** * @When Sending a :method to :url without requesttoken + * @param string $method + * @param string $url */ public function sendingAToWithoutRequesttoken($method, $url) { $baseUrl = substr($this->baseUrl, 0, -5); @@ -231,6 +259,17 @@ trait BasicStructure { } /** + * @Given User :user modifies text of :filename with text :text + * @param string $user + * @param string $filename + * @param string $text + */ + public function modifyTextOfFile($user, $filename, $text) { + self::removeFile("../../data/$user/files", "$filename"); + file_put_contents("../../data/$user/files" . "$filename", "$text"); + } + + /** * @BeforeSuite */ public static function addFilesToSkeleton(){ @@ -248,7 +287,6 @@ trait BasicStructure { mkdir("../../core/skeleton/PARENT/CHILD", 0777, true); } file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "ownCloud test text file\n"); - } /** @@ -269,8 +307,6 @@ trait BasicStructure { if (is_dir("../../core/skeleton/PARENT")) { rmdir("../../core/skeleton/PARENT"); } - - } } diff --git a/build/integration/features/bootstrap/CalDavContext.php b/build/integration/features/bootstrap/CalDavContext.php index 88711f3aa73..30c50630b3e 100644 --- a/build/integration/features/bootstrap/CalDavContext.php +++ b/build/integration/features/bootstrap/CalDavContext.php @@ -71,6 +71,8 @@ class CalDavContext implements \Behat\Behat\Context\Context { /** * @When :user requests calendar :calendar + * @param string $user + * @param string $calendar */ public function requestsCalendar($user, $calendar) { $davUrl = $this->baseUrl . '/remote.php/dav/calendars/'.$calendar; @@ -93,6 +95,8 @@ class CalDavContext implements \Behat\Behat\Context\Context { /** * @Then The CalDAV HTTP status code should be :code + * @param int $code + * @throws \Exception */ public function theCaldavHttpStatusCodeShouldBe($code) { if((int)$code !== $this->response->getStatusCode()) { @@ -115,6 +119,8 @@ class CalDavContext implements \Behat\Behat\Context\Context { /** * @Then The exception is :message + * @param string $message + * @throws \Exception */ public function theExceptionIs($message) { $result = $this->responseXml['value'][0]['value']; @@ -132,6 +138,8 @@ class CalDavContext implements \Behat\Behat\Context\Context { /** * @Then The error message is :message + * @param string $message + * @throws \Exception */ public function theErrorMessageIs($message) { $result = $this->responseXml['value'][1]['value']; @@ -149,6 +157,8 @@ class CalDavContext implements \Behat\Behat\Context\Context { /** * @Given :user creates a calendar named :name + * @param string $user + * @param string $name */ public function createsACalendarNamed($user, $name) { $davUrl = $this->baseUrl . '/remote.php/dav/calendars/'.$user.'/'.$name; diff --git a/build/integration/features/bootstrap/CapabilitiesContext.php b/build/integration/features/bootstrap/CapabilitiesContext.php index d30984f0db7..57db3555790 100644 --- a/build/integration/features/bootstrap/CapabilitiesContext.php +++ b/build/integration/features/bootstrap/CapabilitiesContext.php @@ -15,20 +15,7 @@ require __DIR__ . '/../../vendor/autoload.php'; class CapabilitiesContext implements Context, SnippetAcceptingContext { use BasicStructure; - use Provisioning; - use Sharing; - - /** - * @Given /^parameter "([^"]*)" of app "([^"]*)" is set to "([^"]*)"$/ - */ - public function serverParameterIsSetTo($parameter, $app, $value){ - $user = $this->currentUser; - $this->currentUser = 'admin'; - - $this->modifyServerConfig($app, $parameter, $value); - - $this->currentUser = $user; - } + use AppConfiguration; /** * @Then /^fields of capabilities match with$/ @@ -39,9 +26,9 @@ class CapabilitiesContext implements Context, SnippetAcceptingContext { foreach ($formData->getHash() as $row) { $path_to_element = explode('@@@', $row['path_to_element']); - $answeredValue = $capabilitiesXML->$row['capability']; + $answeredValue = $capabilitiesXML->{$row['capability']}; for ($i = 0; $i < count($path_to_element); $i++){ - $answeredValue = $answeredValue->$path_to_element[$i]; + $answeredValue = $answeredValue->{$path_to_element[$i]}; } $answeredValue = (string)$answeredValue; PHPUnit_Framework_Assert::assertEquals( @@ -64,71 +51,6 @@ class CapabilitiesContext implements Context, SnippetAcceptingContext { $this->modifyServerConfig('core', 'shareapi_allow_public_notification', 'no'); $this->modifyServerConfig('core', 'shareapi_default_expire_date', 'no'); $this->modifyServerConfig('core', 'shareapi_enforce_expire_date', 'no'); - } - - /** - * @BeforeScenario - * - * Enable the testing app before the first scenario of the feature and - * reset the configs before each scenario - * @param BeforeScenarioScope $event - */ - public function prepareParameters(BeforeScenarioScope $event){ - $user = $this->currentUser; - $this->currentUser = 'admin'; - - $scenarios = $event->getFeature()->getScenarios(); - if ($event->getScenario() === reset($scenarios)) { - $this->setStatusTestingApp(true); - } - - $this->resetAppConfigs(); - - $this->currentUser = $user; - } - - /** - * @AfterScenario - * - * Reset the values after the last scenario of the feature and disable the testing app - * @param AfterScenarioScope $event - */ - public function undoChangingParameters(AfterScenarioScope $event) { - $scenarios = $event->getFeature()->getScenarios(); - if ($event->getScenario() === end($scenarios)) { - $user = $this->currentUser; - $this->currentUser = 'admin'; - - $this->resetAppConfigs(); - - $this->setStatusTestingApp(false); - $this->currentUser = $user; - } - } - - /** - * @param string $app - * @param string $parameter - * @param string $value - */ - protected function modifyServerConfig($app, $parameter, $value) { - $body = new \Behat\Gherkin\Node\TableNode([['value', $value]]); - $this->sendingToWith('post', "/apps/testing/api/v1/app/{$app}/{$parameter}", $body); - $this->theHTTPStatusCodeShouldBe('200'); - $this->theOCSStatusCodeShouldBe('100'); - } - - protected function setStatusTestingApp($enabled) { - $this->sendingTo(($enabled ? 'post' : 'delete'), '/cloud/apps/testing'); - $this->theHTTPStatusCodeShouldBe('200'); - $this->theOCSStatusCodeShouldBe('100'); - - $this->sendingTo('get', '/cloud/apps?filter=enabled'); - $this->theHTTPStatusCodeShouldBe('200'); - if ($enabled) { - PHPUnit_Framework_Assert::assertContains('testing', $this->response->getBody()->getContents()); - } else { - PHPUnit_Framework_Assert::assertNotContains('testing', $this->response->getBody()->getContents()); - } + $this->modifyServerConfig('core', 'shareapi_allow_group_sharing', 'yes'); } } diff --git a/build/integration/features/bootstrap/CardDavContext.php b/build/integration/features/bootstrap/CardDavContext.php index 251d76d0833..d317ba5193f 100644 --- a/build/integration/features/bootstrap/CardDavContext.php +++ b/build/integration/features/bootstrap/CardDavContext.php @@ -73,6 +73,10 @@ class CardDavContext implements \Behat\Behat\Context\Context { /** * @When :user requests addressbook :addressBook with statuscode :statusCode + * @param string $user + * @param string $addressBook + * @param int $statusCode + * @throws \Exception */ public function requestsAddressbookWithStatuscode($user, $addressBook, $statusCode) { $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/'.$addressBook; @@ -112,6 +116,10 @@ class CardDavContext implements \Behat\Behat\Context\Context { /** * @Given :user creates an addressbook named :addressBook with statuscode :statusCode + * @param string $user + * @param string $addressBook + * @param int $statusCode + * @throws \Exception */ public function createsAnAddressbookNamedWithStatuscode($user, $addressBook, $statusCode) { $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/'.$user.'/'.$addressBook; @@ -121,9 +129,7 @@ class CardDavContext implements \Behat\Behat\Context\Context { 'MKCOL', $davUrl, [ - 'body' => '<d:mkcol xmlns:c="urn:ietf:params:xml:ns:caldav" - xmlns:card="urn:ietf:params:xml:ns:carddav" - xmlns:cs="http://calendarserver.org/ns/" + 'body' => '<d:mkcol xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:"> <d:set> <d:prop> @@ -158,6 +164,8 @@ class CardDavContext implements \Behat\Behat\Context\Context { /** * @When The CardDAV exception is :message + * @param string $message + * @throws \Exception */ public function theCarddavExceptionIs($message) { $result = $this->responseXml['value'][0]['value']; @@ -175,6 +183,8 @@ class CardDavContext implements \Behat\Behat\Context\Context { /** * @When The CardDAV error message is :arg1 + * @param string $message + * @throws \Exception */ public function theCarddavErrorMessageIs($message) { $result = $this->responseXml['value'][1]['value']; diff --git a/build/integration/features/bootstrap/ChecksumsContext.php b/build/integration/features/bootstrap/ChecksumsContext.php index a5d20ba965d..af8f9e5590d 100644 --- a/build/integration/features/bootstrap/ChecksumsContext.php +++ b/build/integration/features/bootstrap/ChecksumsContext.php @@ -2,7 +2,6 @@ require __DIR__ . '/../../vendor/autoload.php'; -use Behat\Gherkin\Node\TableNode; use GuzzleHttp\Client; use GuzzleHttp\Message\ResponseInterface; @@ -50,6 +49,10 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @When user :user uploads file :source to :destination with checksum :checksum + * @param string $user + * @param string $source + * @param string $destination + * @param string $checksum */ public function userUploadsFileToWithChecksum($user, $source, $destination, $checksum) { @@ -76,6 +79,8 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @Then The webdav response should have a status code :statusCode + * @param int $statusCode + * @throws \Exception */ public function theWebdavResponseShouldHaveAStatusCode($statusCode) { if((int)$statusCode !== $this->response->getStatusCode()) { @@ -85,6 +90,8 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @When user :user request the checksum of :path via propfind + * @param string $user + * @param string $path */ public function userRequestTheChecksumOfViaPropfind($user, $path) { @@ -109,6 +116,8 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @Then The webdav checksum should match :checksum + * @param string $checksum + * @throws \Exception */ public function theWebdavChecksumShouldMatch($checksum) { @@ -128,6 +137,8 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @When user :user downloads the file :path + * @param string $user + * @param string $path */ public function userDownloadsTheFile($user, $path) { @@ -144,6 +155,8 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @Then The header checksum should match :checksum + * @param string $checksum + * @throws \Exception */ public function theHeaderChecksumShouldMatch($checksum) { @@ -154,6 +167,9 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @Given User :user copied file :source to :destination + * @param string $user + * @param string $source + * @param string $destination */ public function userCopiedFileTo($user, $source, $destination) { @@ -204,6 +220,12 @@ class ChecksumsContext implements \Behat\Behat\Context\Context { /** * @Given user :user uploads chunk file :num of :total with :data to :destination with checksum :checksum + * @param string $user + * @param int $num + * @param int $total + * @param string $data + * @param string $destination + * @param string $checksum */ public function userUploadsChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination, $checksum) { diff --git a/build/integration/features/bootstrap/CommentsContext.php b/build/integration/features/bootstrap/CommentsContext.php index d720bb8dcd6..e74e9580dcc 100644 --- a/build/integration/features/bootstrap/CommentsContext.php +++ b/build/integration/features/bootstrap/CommentsContext.php @@ -45,7 +45,7 @@ class CommentsContext implements \Behat\Behat\Context\Context { } /** @AfterScenario */ - public function teardownScenario(\Behat\Behat\Hook\Scope\AfterScenarioScope $scope) { + public function teardownScenario() { $client = new \GuzzleHttp\Client(); try { $client->delete( @@ -66,6 +66,7 @@ class CommentsContext implements \Behat\Behat\Context\Context { } /** + * @param string $path * @return int */ private function getFileIdForPath($path) { @@ -90,6 +91,11 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @When :user posts a comment with content :content on the file named :fileName it should return :statusCode + * @param string $user + * @param string $content + * @param string $fileName + * @param int $statusCode + * @throws \Exception */ public function postsACommentWithContentOnTheFileNamedItShouldReturn($user, $content, $fileName, $statusCode) { $fileId = $this->getFileIdForPath($fileName); @@ -123,6 +129,10 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Then As :user load all the comments of the file named :fileName it should return :statusCode + * @param string $user + * @param string $fileName + * @param int $statusCode + * @throws \Exception */ public function asLoadloadAllTheCommentsOfTheFileNamedItShouldReturn($user, $fileName, $statusCode) { $fileId = $this->getFileIdForPath($fileName); @@ -135,7 +145,7 @@ class CommentsContext implements \Behat\Behat\Context\Context { $url, [ 'body' => '<?xml version="1.0" encoding="utf-8" ?> -<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns"> +<oc:filter-comments xmlns:oc="http://owncloud.org/ns"> <oc:limit>200</oc:limit> <oc:offset>0</oc:offset> </oc:filter-comments> @@ -167,6 +177,11 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Given As :user sending :verb to :url with + * @param string $user + * @param string $verb + * @param string $url + * @param \Behat\Gherkin\Node\TableNode $body + * @throws \Exception */ public function asUserSendingToWith($user, $verb, $url, \Behat\Gherkin\Node\TableNode $body) { $client = new \GuzzleHttp\Client(); @@ -179,6 +194,9 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Then As :user delete the created comment it should return :statusCode + * @param string $user + * @param int $statusCode + * @throws \Exception */ public function asDeleteTheCreatedCommentItShouldReturn($user, $statusCode) { $url = $this->baseUrl.'/remote.php/dav/comments/files/'.$this->fileId.'/'.$this->commentId; @@ -208,6 +226,9 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Then the response should contain a property :key with value :value + * @param string $key + * @param string $value + * @throws \Exception */ public function theResponseShouldContainAPropertyWithValue($key, $value) { $keys = $this->response[0]['value'][2]['value'][0]['value']; @@ -226,6 +247,8 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Then the response should contain only :number comments + * @param int $number + * @throws \Exception */ public function theResponseShouldContainOnlyComments($number) { if(count($this->response) !== (int)$number) { @@ -235,6 +258,10 @@ class CommentsContext implements \Behat\Behat\Context\Context { /** * @Then As :user edit the last created comment and set text to :text it should return :statusCode + * @param string $user + * @param string $text + * @param int $statusCode + * @throws \Exception */ public function asEditTheLastCreatedCommentAndSetTextToItShouldReturn($user, $text, $statusCode) { $client = new \GuzzleHttp\Client(); diff --git a/build/integration/features/bootstrap/FeatureContext.php b/build/integration/features/bootstrap/FeatureContext.php index 78ff4e7833d..21ca8d87295 100644 --- a/build/integration/features/bootstrap/FeatureContext.php +++ b/build/integration/features/bootstrap/FeatureContext.php @@ -2,8 +2,6 @@ use Behat\Behat\Context\Context; use Behat\Behat\Context\SnippetAcceptingContext; -use GuzzleHttp\Client; -use GuzzleHttp\Message\ResponseInterface; require __DIR__ . '/../../vendor/autoload.php'; @@ -12,8 +10,5 @@ require __DIR__ . '/../../vendor/autoload.php'; * Features context. */ class FeatureContext implements Context, SnippetAcceptingContext { - use BasicStructure; - use Provisioning; - use Sharing; use WebDav; } diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php index 279b5206dee..55f3a55da0d 100644 --- a/build/integration/features/bootstrap/FederationContext.php +++ b/build/integration/features/bootstrap/FederationContext.php @@ -12,16 +12,43 @@ require __DIR__ . '/../../vendor/autoload.php'; */ class FederationContext implements Context, SnippetAcceptingContext { - use BasicStructure; - use Provisioning; - use Sharing; + use WebDav; /** - * @When /^User "([^"]*)" from server "([^"]*)" shares "([^"]*)" with user "([^"]*)" from server "([^"]*)"$/ + * @Given /^User "([^"]*)" from server "(LOCAL|REMOTE)" shares "([^"]*)" with user "([^"]*)" from server "(LOCAL|REMOTE)"$/ + * + * @param string $sharerUser + * @param string $sharerServer "LOCAL" or "REMOTE" + * @param string $sharerPath + * @param string $shareeUser + * @param string $shareeServer "LOCAL" or "REMOTE" */ - public function federateSharing($userLocal, $serverLocal, $pathLocal, $userRemote, $serverRemote){ - $shareWith = "$userRemote@" . substr($this->remoteBaseUrl, 0, -4); - $this->createShare($userLocal, $pathLocal, 6, $shareWith, null, null, null); + public function federateSharing($sharerUser, $sharerServer, $sharerPath, $shareeUser, $shareeServer){ + if ($shareeServer == "REMOTE"){ + $shareWith = "$shareeUser@" . substr($this->remoteBaseUrl, 0, -4); + } else { + $shareWith = "$shareeUser@" . substr($this->localBaseUrl, 0, -4); + } + $previous = $this->usingServer($sharerServer); + $this->createShare($sharerUser, $sharerPath, 6, $shareWith, null, null, null); + $this->usingServer($previous); } + /** + * @When /^User "([^"]*)" from server "(LOCAL|REMOTE)" accepts last pending share$/ + * @param string $user + * @param string $server + */ + public function acceptLastPendingShare($user, $server){ + $previous = $this->usingServer($server); + $this->asAn($user); + $this->sendingToWith('GET', "/apps/files_sharing/api/v1/remote_shares/pending", null); + $this->theHTTPStatusCodeShouldBe('200'); + $this->theOCSStatusCodeShouldBe('100'); + $share_id = $this->response->xml()->data[0]->element[0]->id; + $this->sendingToWith('POST', "/apps/files_sharing/api/v1/remote_shares/pending/{$share_id}", null); + $this->theHTTPStatusCodeShouldBe('200'); + $this->theOCSStatusCodeShouldBe('100'); + $this->usingServer($previous); + } } diff --git a/build/integration/features/bootstrap/Provisioning.php b/build/integration/features/bootstrap/Provisioning.php index 6d710b2016f..6cf57514483 100644 --- a/build/integration/features/bootstrap/Provisioning.php +++ b/build/integration/features/bootstrap/Provisioning.php @@ -1,16 +1,12 @@ <?php -use Behat\Behat\Context\Context; -use Behat\Behat\Context\SnippetAcceptingContext; use GuzzleHttp\Client; use GuzzleHttp\Message\ResponseInterface; require __DIR__ . '/../../vendor/autoload.php'; trait Provisioning { - - /** @var int */ - private $apiVersion = 1; + use BasicStructure; /** @var array */ private $createdUsers = []; @@ -25,14 +21,8 @@ trait Provisioning { private $createdGroups = []; /** - * @Given /^using api version "([^"]*)"$/ - */ - public function usingApiVersion($version) { - $this->apiVersion = $version; - } - - /** * @Given /^user "([^"]*)" exists$/ + * @param string $user */ public function assureUserExists($user) { try { @@ -45,11 +35,11 @@ trait Provisioning { } $this->userExists($user); PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); - } /** * @Given /^user "([^"]*)" does not exist$/ + * @param string $user */ public function userDoesNotExist($user) { try { @@ -142,6 +132,8 @@ trait Provisioning { /** * @Then /^check that user "([^"]*)" belongs to group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function checkThatUserBelongsToGroup($user, $group) { $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups"; @@ -167,7 +159,6 @@ trait Provisioning { } $this->response = $client->get($fullUrl, $options); - $groups = array($group); $respondedArray = $this->getArrayOfGroupsResponded($this->response); if (array_key_exists($group, $respondedArray)) { @@ -179,20 +170,25 @@ trait Provisioning { /** * @Given /^user "([^"]*)" belongs to group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function assureUserBelongsToGroup($user, $group){ + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + if (!$this->userBelongsToGroup($user, $group)){ - $previous_user = $this->currentUser; - $this->currentUser = "admin"; $this->addingUserToGroup($user, $group); - $this->currentUser = $previous_user; } - $this->checkThatUserBelongsToGroup($user, $group); + $this->checkThatUserBelongsToGroup($user, $group); + $this->currentUser = $previous_user; } /** * @Given /^user "([^"]*)" does not belong to group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function userDoesNotBelongToGroup($user, $group) { $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups"; @@ -209,8 +205,9 @@ trait Provisioning { PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); } - /** + /** * @When /^creating the group "([^"]*)"$/ + * @param string $group */ public function creatingTheGroup($group) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups"; @@ -233,7 +230,22 @@ 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 */ public function deletingTheUser($user) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user"; @@ -248,6 +260,7 @@ trait Provisioning { /** * @When /^Deleting the group "([^"]*)"$/ + * @param string $group */ public function deletingTheGroup($group) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups/$group"; @@ -262,6 +275,8 @@ trait Provisioning { /** * @Given /^Add user "([^"]*)" to the group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function addUserToGroup($user, $group) { $this->userExists($user); @@ -272,6 +287,8 @@ trait Provisioning { /** * @When /^User "([^"]*)" is added to the group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function addingUserToGroup($user, $group) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users/$user/groups"; @@ -300,6 +317,7 @@ trait Provisioning { /** * @Given /^group "([^"]*)" exists$/ + * @param string $group */ public function assureGroupExists($group) { try { @@ -316,6 +334,7 @@ trait Provisioning { /** * @Given /^group "([^"]*)" does not exist$/ + * @param string $group */ public function groupDoesNotExist($group) { try { @@ -339,6 +358,8 @@ trait Provisioning { /** * @Given /^user "([^"]*)" is subadmin of group "([^"]*)"$/ + * @param string $user + * @param string $group */ public function userIsSubadminOfGroup($user, $group) { $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group/subadmins"; @@ -356,7 +377,28 @@ 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 */ public function userIsNotSubadminOfGroup($user, $group) { $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group/subadmins"; @@ -484,6 +526,7 @@ trait Provisioning { /** * @Given /^app "([^"]*)" is disabled$/ + * @param string $app */ public function appIsDisabled($app) { $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=disabled"; @@ -501,6 +544,7 @@ trait Provisioning { /** * @Given /^app "([^"]*)" is enabled$/ + * @param string $app */ public function appIsEnabled($app) { $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=enabled"; @@ -517,7 +561,41 @@ 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 */ public function userHasAQuotaOf($user, $quota) { @@ -532,6 +610,7 @@ trait Provisioning { /** * @Given user :user has unlimited quota + * @param string $user */ public function userHasUnlimitedQuota($user) { @@ -573,4 +652,5 @@ trait Provisioning { } $this->usingServer($previousServer); } + } diff --git a/build/integration/features/bootstrap/ShareesContext.php b/build/integration/features/bootstrap/ShareesContext.php new file mode 100644 index 00000000000..bd08ae6e138 --- /dev/null +++ b/build/integration/features/bootstrap/ShareesContext.php @@ -0,0 +1,73 @@ +<?php + +use Behat\Behat\Context\Context; +use Behat\Behat\Context\SnippetAcceptingContext; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + + +/** + * Features context. + */ +class ShareesContext implements Context, SnippetAcceptingContext { + use Provisioning; + use AppConfiguration; + + /** + * @When /^getting sharees for$/ + * @param \Behat\Gherkin\Node\TableNode $body + */ + public function whenGettingShareesFor($body) { + $url = '/apps/files_sharing/api/v1/sharees'; + if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $parameters = []; + foreach ($body->getRowsHash() as $key => $value) { + $parameters[] = $key . '=' . $value; + } + if (!empty($parameters)) { + $url .= '?' . implode('&', $parameters); + } + } + + $this->sendingTo('GET', $url); + } + + /** + * @Then /^"([^"]*)" sharees returned (are|is empty)$/ + * @param string $shareeType + * @param string $isEmpty + * @param \Behat\Gherkin\Node\TableNode|null $shareesList + */ + public function thenListOfSharees($shareeType, $isEmpty, $shareesList = null) { + if ($isEmpty !== 'is empty') { + $sharees = $shareesList->getRows(); + $respondedArray = $this->getArrayOfShareesResponded($this->response, $shareeType); + PHPUnit_Framework_Assert::assertEquals($sharees, $respondedArray); + } else { + $respondedArray = $this->getArrayOfShareesResponded($this->response, $shareeType); + PHPUnit_Framework_Assert::assertEmpty($respondedArray); + } + } + + public function getArrayOfShareesResponded(ResponseInterface $response, $shareeType) { + $elements = $response->xml()->data; + $elements = json_decode(json_encode($elements), 1); + if (strpos($shareeType, 'exact ') === 0) { + $elements = $elements['exact']; + $shareeType = substr($shareeType, 6); + } + + $sharees = []; + foreach ($elements[$shareeType] as $element) { + $sharees[] = [$element['label'], $element['value']['shareType'], $element['value']['shareWith']]; + } + return $sharees; + } + + protected function resetAppConfigs() { + $this->modifyServerConfig('core', 'shareapi_only_share_with_group_members', 'no'); + $this->modifyServerConfig('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'); + $this->modifyServerConfig('core', 'shareapi_allow_group_sharing', 'yes'); + } +} diff --git a/build/integration/features/bootstrap/Sharing.php b/build/integration/features/bootstrap/Sharing.php index 884d69597fc..2c8c1798250 100644 --- a/build/integration/features/bootstrap/Sharing.php +++ b/build/integration/features/bootstrap/Sharing.php @@ -1,7 +1,5 @@ <?php -use Behat\Behat\Context\Context; -use Behat\Behat\Context\SnippetAcceptingContext; use GuzzleHttp\Client; use GuzzleHttp\Message\ResponseInterface; @@ -9,7 +7,8 @@ require __DIR__ . '/../../vendor/autoload.php'; -trait Sharing{ +trait Sharing { + use Provisioning; /** @var int */ private $sharingApiVersion = 1; @@ -22,10 +21,11 @@ trait Sharing{ /** * @Given /^as "([^"]*)" creating a share with$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData + * @param string $user + * @param \Behat\Gherkin\Node\TableNode|null $body */ public function asCreatingAShareWith($user, $body) { - $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v1/shares"; + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares"; $client = new Client(); $options = []; if ($user === 'admin') { @@ -54,10 +54,10 @@ trait Sharing{ /** * @When /^creating a share with$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData + * @param \Behat\Gherkin\Node\TableNode|null $body */ public function creatingShare($body) { - return $this->asCreatingAShareWith($this->currentUser, $body); + $this->asCreatingAShareWith($this->currentUser, $body); } /** @@ -112,7 +112,7 @@ trait Sharing{ * @When /^Adding expiration date to last share$/ */ public function addingExpirationDate() { - $share_id = $this->lastShareData->data[0]->id; + $share_id = (string) $this->lastShareData->data[0]->id; $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id"; $client = new Client(); $options = []; @@ -132,7 +132,7 @@ trait Sharing{ * @param \Behat\Gherkin\Node\TableNode|null $body */ public function updatingLastShare($body) { - $share_id = $this->lastShareData->data[0]->id; + $share_id = (string) $this->lastShareData->data[0]->id; $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id"; $client = new Client(); $options = []; @@ -225,6 +225,9 @@ trait Sharing{ elseif ((string)$element->$field == $contentExpected){ return True; } + else{ + print($element->$field); + } } return False; @@ -247,6 +250,8 @@ trait Sharing{ /** * @Then /^File "([^"]*)" should be included in the response$/ + * + * @param string $filename */ public function checkSharedFileInResponse($filename){ PHPUnit_Framework_Assert::assertEquals(True, $this->isFieldInResponse('file_target', "/$filename")); @@ -254,6 +259,8 @@ trait Sharing{ /** * @Then /^File "([^"]*)" should not be included in the response$/ + * + * @param string $filename */ public function checkSharedFileNotInResponse($filename){ PHPUnit_Framework_Assert::assertEquals(False, $this->isFieldInResponse('file_target', "/$filename")); @@ -261,6 +268,8 @@ trait Sharing{ /** * @Then /^User "([^"]*)" should be included in the response$/ + * + * @param string $user */ public function checkSharedUserInResponse($user){ PHPUnit_Framework_Assert::assertEquals(True, $this->isFieldInResponse('share_with', "$user")); @@ -268,15 +277,17 @@ trait Sharing{ /** * @Then /^User "([^"]*)" should not be included in the response$/ + * + * @param string $user */ public function checkSharedUserNotInResponse($user){ PHPUnit_Framework_Assert::assertEquals(False, $this->isFieldInResponse('share_with', "$user")); } - public function isUserOrGroupInSharedData($userOrGroup){ + public function isUserOrGroupInSharedData($userOrGroup, $permissions = null){ $data = $this->response->xml()->data[0]; foreach($data as $element) { - if ($element->share_with == $userOrGroup){ + if ($element->share_with == $userOrGroup && ($permissions === null || $permissions == $element->permissions)){ return True; } } @@ -284,9 +295,13 @@ trait Sharing{ } /** - * @Given /^file "([^"]*)" of user "([^"]*)" is shared with user "([^"]*)"$/ + * @Given /^(file|folder|entry) "([^"]*)" of user "([^"]*)" is shared with user "([^"]*)"( with permissions ([\d]*))?$/ + * + * @param string $filepath + * @param string $user1 + * @param string $user2 */ - public function assureFileIsShared($filepath, $user1, $user2){ + public function assureFileIsShared($entry, $filepath, $user1, $user2, $withPerms = null, $permissions = null){ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath"; $client = new Client(); $options = []; @@ -296,19 +311,23 @@ trait Sharing{ $options['auth'] = [$user1, $this->regularUser]; } $this->response = $client->get($fullUrl, $options); - if ($this->isUserOrGroupInSharedData($user2)){ + if ($this->isUserOrGroupInSharedData($user2, $permissions)){ return; } else { - $this->createShare($user1, $filepath, 0, $user2, null, null, null); + $this->createShare($user1, $filepath, 0, $user2, null, null, $permissions); } $this->response = $client->get($fullUrl, $options); - PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($user2)); + PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($user2, $permissions)); } /** - * @Given /^file "([^"]*)" of user "([^"]*)" is shared with group "([^"]*)"$/ + * @Given /^(file|folder|entry) "([^"]*)" of user "([^"]*)" is shared with group "([^"]*)"( with permissions ([\d]*))?$/ + * + * @param string $filepath + * @param string $user + * @param string $group */ - public function assureFileIsSharedWithGroup($filepath, $user, $group){ + public function assureFileIsSharedWithGroup($entry, $filepath, $user, $group, $withPerms = null, $permissions = null){ $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath"; $client = new Client(); $options = []; @@ -318,13 +337,13 @@ trait Sharing{ $options['auth'] = [$user, $this->regularUser]; } $this->response = $client->get($fullUrl, $options); - if ($this->isUserOrGroupInSharedData($group)){ + if ($this->isUserOrGroupInSharedData($group, $permissions)){ return; } else { - $this->createShare($user, $filepath, 1, $group, null, null, null); + $this->createShare($user, $filepath, 1, $group, null, null, $permissions); } $this->response = $client->get($fullUrl, $options); - PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($group)); + PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($group, $permissions)); } /** @@ -367,13 +386,21 @@ trait Sharing{ /** * @Then /^Share fields of last share match with$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData + * @param \Behat\Gherkin\Node\TableNode|null $body */ public function checkShareFields($body){ if ($body instanceof \Behat\Gherkin\Node\TableNode) { $fd = $body->getRowsHash(); foreach($fd as $field => $value) { + if (substr($field, 0, 10 ) === "share_with"){ + $value = str_replace("REMOTE", substr($this->remoteBaseUrl, 0, -5), $value); + $value = str_replace("LOCAL", substr($this->localBaseUrl, 0, -5), $value); + } + if (substr($field, 0, 6 ) === "remote"){ + $value = str_replace("REMOTE", substr($this->remoteBaseUrl, 0, -4), $value); + $value = str_replace("LOCAL", substr($this->localBaseUrl, 0, -4), $value); + } if (!$this->isFieldInResponse($field, $value)){ PHPUnit_Framework_Assert::fail("$field" . " doesn't have value " . "$value"); } @@ -385,7 +412,7 @@ trait Sharing{ * @Then As :user remove all shares from the file named :fileName */ public function asRemoveAllSharesFromTheFileNamed($user, $fileName) { - $url = $this->baseUrl.'v2.php/apps/files_sharing/api/v1/shares?format=json'; + $url = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares?format=json"; $client = new \GuzzleHttp\Client(); $res = $client->get( $url, @@ -405,7 +432,7 @@ trait Sharing{ if (stripslashes($data['path']) === $fileName) { $id = $data['id']; $client->delete( - $this->baseUrl.'v2.php/apps/files_sharing/api/v1/shares/'.$id, + $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/{$id}", [ 'auth' => [ $user, @@ -439,59 +466,8 @@ trait Sharing{ public function shareIdsShouldMatch() { if ($this->savedShareId !== $this->lastShareData['data']['id']) { - throw new \Excetion('Expected the same link share to be returned'); - } - } - - /** - * @When /^getting sharees for$/ - * @param \Behat\Gherkin\Node\TableNode $body - */ - public function whenGettingShareesFor($body) { - $url = '/apps/files_sharing/api/v1/sharees'; - if ($body instanceof \Behat\Gherkin\Node\TableNode) { - $parameters = []; - foreach ($body->getRowsHash() as $key => $value) { - $parameters[] = $key . '=' . $value; - } - if (!empty($parameters)) { - $url .= '?' . implode('&', $parameters); - } - } - - $this->sendingTo('GET', $url); - } - - /** - * @Then /^"([^"]*)" sharees returned (are|is empty)$/ - * @param string $shareeType - * @param string $isEmpty - * @param \Behat\Gherkin\Node\TableNode|null $shareesList - */ - public function thenListOfSharees($shareeType, $isEmpty, $shareesList = null) { - if ($isEmpty !== 'is empty') { - $sharees = $shareesList->getRows(); - $respondedArray = $this->getArrayOfShareesResponded($this->response, $shareeType); - PHPUnit_Framework_Assert::assertEquals($sharees, $respondedArray); - } else { - $respondedArray = $this->getArrayOfShareesResponded($this->response, $shareeType); - PHPUnit_Framework_Assert::assertEmpty($respondedArray); - } - } - - public function getArrayOfShareesResponded(ResponseInterface $response, $shareeType) { - $elements = $response->xml()->data; - $elements = json_decode(json_encode($elements), 1); - if (strpos($shareeType, 'exact ') === 0) { - $elements = $elements['exact']; - $shareeType = substr($shareeType, 6); - } - - $sharees = []; - foreach ($elements[$shareeType] as $element) { - $sharees[] = [$element['label'], $element['value']['shareType'], $element['value']['shareWith']]; + throw new \Exception('Expected the same link share to be returned'); } - return $sharees; } } diff --git a/build/integration/features/bootstrap/TagsContext.php b/build/integration/features/bootstrap/TagsContext.php index 5e1f62ba838..447f51e3781 100644 --- a/build/integration/features/bootstrap/TagsContext.php +++ b/build/integration/features/bootstrap/TagsContext.php @@ -97,24 +97,36 @@ class TagsContext implements \Behat\Behat\Context\Context { } /** - * @When :user creates a :type tag with name :name + * @param string $user + * @param string $type + * @param string $name + * @param string $groups */ - public function createsATagWithName($user, $type, $name) { - $userVisible = 'true'; - $userAssignable = 'true'; + private function createTag($user, $type, $name, $groups = null) { + $userVisible = true; + $userAssignable = true; switch ($type) { case 'normal': break; case 'not user-assignable': - $userAssignable = 'false'; + $userAssignable = false; break; case 'not user-visible': - $userVisible = 'false'; + $userVisible = false; break; default: throw new \Exception('Unsupported type'); } + $body = [ + 'name' => $name, + 'userVisible' => $userVisible, + 'userAssignable' => $userAssignable, + ]; + if ($groups !== null) { + $body['groups'] = $groups; + } + try { $this->response = $this->client->post( $this->baseUrl . '/remote.php/dav/systemtags/', @@ -126,16 +138,41 @@ class TagsContext implements \Behat\Behat\Context\Context { 'headers' => [ 'Content-Type' => 'application/json', ], - 'body' => '{"name":"'.$name.'","userVisible":'.$userVisible.',"userAssignable":'.$userAssignable.'}', + 'body' => json_encode($body) ] ); - } catch (\GuzzleHttp\Exception\ClientException $e){ + } catch (\GuzzleHttp\Exception\ClientException $e) { $this->response = $e->getResponse(); } } /** + * @When :user creates a :type tag with name :name + * @param string $user + * @param string $type + * @param string $name + * @throws \Exception + */ + public function createsATagWithName($user, $type, $name) { + $this->createTag($user, $type, $name); + } + + /** + * @When :user creates a :type tag with name :name and groups :groups + * @param string $user + * @param string $type + * @param string $name + * @param string $groups + * @throws \Exception + */ + public function createsATagWithNameAndGroups($user, $type, $name, $groups) { + $this->createTag($user, $type, $name, $groups); + } + + /** * @Then The response should have a status code :statusCode + * @param int $statusCode + * @throws \Exception */ public function theResponseShouldHaveAStatusCode($statusCode) { if((int)$statusCode !== $this->response->getStatusCode()) { @@ -149,21 +186,30 @@ class TagsContext implements \Behat\Behat\Context\Context { * @param string $user * @return array */ - private function requestTagsForUser($user) { + private function requestTagsForUser($user, $withGroups = false) { try { - $request = $this->client->createRequest( - 'PROPFIND', - $this->baseUrl . '/remote.php/dav/systemtags/', - [ - 'body' => '<?xml version="1.0"?> + $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 /> + <oc:user-assignable /> + <oc:can-assign /> +'; + + if ($withGroups) { + $body .= '<oc:groups />'; + } + + $body .= ' </d:prop> -</d:propfind>', +</d:propfind>'; + $request = $this->client->createRequest( + 'PROPFIND', + $this->baseUrl . '/remote.php/dav/systemtags/', + [ + 'body' => $body, 'auth' => [ $user, $this->getPasswordForUser($user), @@ -187,11 +233,15 @@ class TagsContext implements \Behat\Behat\Context\Context { continue; } + // FIXME: use actual property names instead of guessing index position $tags[$singleEntry[0]['value']] = [ 'display-name' => $singleEntry[1]['value'], 'user-visible' => $singleEntry[2]['value'], 'user-assignable' => $singleEntry[3]['value'], ]; + if (isset($singleEntry[5])) { + $tags[$singleEntry[0]['value']]['groups'] = $singleEntry[5]['value']; + } } return $tags; @@ -199,6 +249,9 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @Then The following tags should exist for :user + * @param string $user + * @param TableNode $table + * @throws \Exception */ public function theFollowingTagsShouldExistFor($user, TableNode $table) { $tags = $this->requestTagsForUser($user); @@ -230,7 +283,24 @@ class TagsContext implements \Behat\Behat\Context\Context { } /** + * @Then The :type tag with name :tagName has the groups :groups + */ + public function theTagHasGroup($type, $tagName, $groups) { + $foundTag = $this->findTag($type, $tagName, 'admin', true); + if ($foundTag === null) { + throw new \Exception('No matching tag found'); + } + + if ($foundTag['groups'] !== $groups) { + throw new \Exception('Tag has groups "' . $foundTag['group'] . '" instead of the expected "' . $groups . '"'); + } + } + + /** * @Then :count tags should exist for :user + * @param int $count + * @param string $user + * @throws \Exception */ public function tagsShouldExistFor($count, $user) { if((int)$count !== count($this->requestTagsForUser($user))) { @@ -239,6 +309,45 @@ class TagsContext implements \Behat\Behat\Context\Context { } /** + * Find tag by type and name + * + * @param string $type tag type + * @param string $tagName tag name + * @param string $user retrieved from which user + * @param bool $withGroups whether to also query the tag's groups + * + * @return array tag values or null if not found + */ + private function findTag($type, $tagName, $user = 'admin', $withGroups = false) { + $tags = $this->requestTagsForUser($user, $withGroups); + $userAssignable = 'true'; + $userVisible = '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'); + } + + $foundTag = null; + foreach ($tags as $tag) { + if ($tag['display-name'] === $tagName + && $tag['user-visible'] === $userVisible + && $tag['user-assignable'] === $userAssignable) { + $foundTag = $tag; + break; + } + } + return $foundTag; + } + + /** * @param string $name * @return int */ @@ -256,6 +365,10 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @When :user edits the tag with name :oldNmae and sets its name to :newName + * @param string $user + * @param string $oldName + * @param string $newName + * @throws \Exception */ public function editsTheTagWithNameAndSetsItsNameTo($user, $oldName, $newName) { $tagId = $this->findTagIdByName($oldName); @@ -289,7 +402,47 @@ class TagsContext implements \Behat\Behat\Context\Context { } /** + * @When :user edits the tag with name :oldNmae and sets its groups to :groups + * @param string $user + * @param string $oldName + * @param string $groups + * @throws \Exception + */ + public function editsTheTagWithNameAndSetsItsGroupsTo($user, $oldName, $groups) { + $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:groups>' . $groups . '</oc:groups> + </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 + * @param string $user + * @param string $name */ public function deletesTheTagWithName($user, $name) { $tagId = $this->findTagIdByName($name); @@ -338,6 +491,10 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @When :taggingUser adds the tag :tagName to :fileName shared by :sharingUser + * @param string $taggingUser + * @param string $tagName + * @param string $fileName + * @param string $sharingUser */ public function addsTheTagToSharedBy($taggingUser, $tagName, $fileName, $sharingUser) { $fileId = $this->getFileIdForPath($fileName, $sharingUser); @@ -360,6 +517,10 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @Then :fileName shared by :sharingUser has the following tags + * @param string $fileName + * @param string $sharingUser + * @param TableNode $table + * @throws \Exception */ public function sharedByHasTheFollowingTags($fileName, $sharingUser, TableNode $table) { $loadedExpectedTags = $table->getTable(); @@ -406,6 +567,11 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @Then :fileName shared by :sharingUser has the following tags for :user + * @param string $fileName + * @param string $sharingUser + * @param string $user + * @param TableNode $table + * @throws \Exception */ public function sharedByHasTheFollowingTagsFor($fileName, $sharingUser, $user, TableNode $table) { $loadedExpectedTags = $table->getTable(); @@ -460,6 +626,10 @@ class TagsContext implements \Behat\Behat\Context\Context { /** * @When :user removes the tag :tagName from :fileName shared by :shareUser + * @param string $user + * @param string $tagName + * @param string $fileName + * @param string $shareUser */ public function removesTheTagFromSharedBy($user, $tagName, $fileName, $shareUser) { $tagId = $this->findTagIdByName($tagName); diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php index f7861399d76..092c79fe020 100644 --- a/build/integration/features/bootstrap/WebDav.php +++ b/build/integration/features/bootstrap/WebDav.php @@ -1,7 +1,5 @@ <?php -use Behat\Behat\Context\Context; -use Behat\Behat\Context\SnippetAcceptingContext; use GuzzleHttp\Client as GClient; use GuzzleHttp\Message\ResponseInterface; use Sabre\DAV\Client as SClient; @@ -10,6 +8,8 @@ require __DIR__ . '/../../vendor/autoload.php'; trait WebDav { + use Sharing; + /** @var string*/ private $davPath = "remote.php/webdav"; /** @var ResponseInterface */ @@ -46,9 +46,12 @@ trait WebDav { } /** - * @Given /^User "([^"]*)" moved file "([^"]*)" to "([^"]*)"$/ + * @Given /^User "([^"]*)" moved (file|folder|entry) "([^"]*)" to "([^"]*)"$/ + * @param string $user + * @param string $fileSource + * @param string $fileDestination */ - public function userMovedFile($user, $fileSource, $fileDestination){ + public function userMovedFile($user, $entry, $fileSource, $fileDestination){ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; $headers['Destination'] = $fullUrl . $fileDestination; $this->response = $this->makeDavRequest($user, "MOVE", $fileSource, $headers); @@ -56,9 +59,12 @@ trait WebDav { } /** - * @When /^User "([^"]*)" moves file "([^"]*)" to "([^"]*)"$/ + * @When /^User "([^"]*)" moves (file|folder|entry) "([^"]*)" to "([^"]*)"$/ + * @param string $user + * @param string $fileSource + * @param string $fileDestination */ - public function userMovesFile($user, $fileSource, $fileDestination){ + public function userMovesFile($user, $entry, $fileSource, $fileDestination){ $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; $headers['Destination'] = $fullUrl . $fileDestination; try { @@ -86,15 +92,17 @@ trait WebDav { /** * @When /^Downloading file "([^"]*)" with range "([^"]*)"$/ + * @param string $fileSource + * @param string $range */ public function downloadFileWithRange($fileSource, $range){ - $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; $headers['Range'] = $range; $this->response = $this->makeDavRequest($this->currentUser, "GET", $fileSource, $headers); } /** * @When /^Downloading last public shared file with range "([^"]*)"$/ + * @param string $range */ public function downloadPublicFileWithRange($range){ $token = $this->lastShareData->data->token; @@ -112,7 +120,27 @@ trait WebDav { } /** + * @When /^Downloading last public shared file inside a folder "([^"]*)" with range "([^"]*)"$/ + * @param string $range + */ + public function downloadPublicFileInsideAFolderWithRange($path, $range){ + $token = $this->lastShareData->data->token; + $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav" . "$path"; + $headers['Range'] = $range; + + $client = new GClient(); + $options = []; + $options['auth'] = [$token, ""]; + + $request = $client->createRequest("GET", $fullUrl, $options); + $request->addHeader('Range', $range); + + $this->response = $client->send($request); + } + + /** * @Then /^Downloaded content should be "([^"]*)"$/ + * @param string $content */ public function downloadedContentShouldBe($content){ PHPUnit_Framework_Assert::assertEquals($content, (string)$this->response->getBody()); @@ -120,6 +148,9 @@ trait WebDav { /** * @Then /^Downloaded content when downloading file "([^"]*)" with range "([^"]*)" should be "([^"]*)"$/ + * @param string $fileSource + * @param string $range + * @param string $content */ public function downloadedContentWhenDownloadindShouldBe($fileSource, $range, $content){ $this->downloadFileWithRange($fileSource, $range); @@ -128,6 +159,7 @@ trait WebDav { /** * @When Downloading file :fileName + * @param string $fileName */ public function downloadingFile($fileName) { try { @@ -139,6 +171,8 @@ trait WebDav { /** * @Then The following headers should be set + * @param \Behat\Gherkin\Node\TableNode $table + * @throws \Exception */ public function theFollowingHeadersShouldBeSet(\Behat\Gherkin\Node\TableNode $table) { foreach($table->getTable() as $header) { @@ -160,6 +194,8 @@ trait WebDav { /** * @Then Downloaded content should start with :start + * @param int $start + * @throws \Exception */ public function downloadedContentShouldStartWith($start) { if(strpos($this->response->getBody()->getContents(), $start) !== 0) { @@ -175,6 +211,8 @@ trait WebDav { /** * @Then /^as "([^"]*)" gets properties of folder "([^"]*)" with$/ + * @param string $user + * @param string $path * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable */ public function asGetsPropertiesOfFolderWith($user, $path, $propertiesTable) { @@ -188,7 +226,36 @@ trait WebDav { } /** + * @Then /^as "([^"]*)" the (file|folder|entry) "([^"]*)" does not exist$/ + * @param string $user + * @param string $path + * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable + */ + public function asTheFileOrFolderDoesNotExist($user, $entry, $path) { + $client = $this->getSabreClient($user); + $response = $client->request('HEAD', $this->makeSabrePath($path)); + if ($response['statusCode'] !== 404) { + throw new \Exception($entry . ' "' . $path . '" expected to not exist (status code ' . $response['statusCode'] . ', expected 404)'); + } + + return $response; + } + + /** + * @Then /^as "([^"]*)" the (file|folder|entry) "([^"]*)" exists$/ + * @param string $user + * @param string $path + * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable + */ + public function asTheFileOrFolderExists($user, $entry, $path) { + $this->response = $this->listFolder($user, $path, 0); + } + + /** * @Then the single response should contain a property :key with value :value + * @param string $key + * @param string $expectedValue + * @throws \Exception */ public function theSingleResponseShouldContainAPropertyWithValue($key, $expectedValue) { $keys = $this->response; @@ -197,7 +264,7 @@ trait WebDav { } $value = $keys[$key]; - if ($value !== $expectedValue) { + if ($value != $expectedValue) { throw new \Exception("Property \"$key\" found with value \"$value\", expected \"$expectedValue\""); } } @@ -252,9 +319,25 @@ trait WebDav { } } - /*Returns the elements of a propfind, $folderDepth requires 1 to see elements without children*/ public function listFolder($user, $path, $folderDepth, $properties = null){ + $client = $this->getSabreClient($user); + if (!$properties) { + $properties = [ + '{DAV:}getetag' + ]; + } + + $response = $client->propfind($this->makeSabrePath($path), $properties, $folderDepth); + + return $response; + } + + public function makeSabrePath($path) { + return $this->encodePath($this->davPath . '/' . ltrim($path, '/')); + } + + public function getSabreClient($user) { $fullUrl = substr($this->baseUrl, 0, -4); $settings = array( @@ -268,21 +351,12 @@ trait WebDav { $settings['password'] = $this->regularUser; } - $client = new SClient($settings); - - if (!$properties) { - $properties = [ - '{DAV:}getetag' - ]; - } - - $response = $client->propfind($this->davPath . '/' . ltrim($path, '/'), $properties, $folderDepth); - - return $response; + return new SClient($settings); } /** * @Then /^user "([^"]*)" should see following elements$/ + * @param string $user * @param \Behat\Gherkin\Node\TableNode|null $expectedElements */ public function checkElementList($user, $expectedElements){ @@ -301,6 +375,9 @@ trait WebDav { /** * @When User :user uploads file :source to :destination + * @param string $user + * @param string $source + * @param string $destination */ public function userUploadsAFileTo($user, $source, $destination) { @@ -314,7 +391,23 @@ trait WebDav { } /** + * @When User :user uploads file with content :content to :destination + */ + public function userUploadsAFileWithContentTo($user, $content, $destination) + { + $file = \GuzzleHttp\Stream\Stream::factory($content); + try { + $this->response = $this->makeDavRequest($user, "PUT", $destination, [], $file); + } catch (\GuzzleHttp\Exception\ServerException $e) { + // 4xx and 5xx responses cause an exception + $this->response = $e->getResponse(); + } + } + + /** * @When User :user deletes file :file + * @param string $user + * @param string $file */ public function userDeletesFile($user, $file) { try { @@ -327,10 +420,12 @@ trait WebDav { /** * @Given User :user created a folder :destination + * @param string $user + * @param string $destination */ public function userCreatedAFolder($user, $destination){ try { - $this->response = $this->makeDavRequest($user, "MKCOL", $destination, []); + $this->response = $this->makeDavRequest($user, "MKCOL", '/' . ltrim($destination, '/'), []); } catch (\GuzzleHttp\Exception\ServerException $e) { // 4xx and 5xx responses cause an exception $this->response = $e->getResponse(); @@ -339,6 +434,11 @@ trait WebDav { /** * @Given user :user uploads chunk file :num of :total with :data to :destination + * @param string $user + * @param int $num + * @param int $total + * @param string $data + * @param string $destination */ public function userUploadsChunkFileOfWithToWithChecksum($user, $num, $total, $data, $destination) { @@ -348,5 +448,104 @@ trait WebDav { $this->makeDavRequest($user, 'PUT', $file, ['OC-Chunked' => '1'], $data); } -} + /** + * @Given user :user creates a new chunking upload with id :id + */ + public function userCreatesANewChunkingUploadWithId($user, $id) + { + $destination = '/uploads/'.$user.'/'.$id; + $this->makeDavRequest($user, 'MKCOL', $destination, []); + } + + /** + * @Given user :user uploads new chunk file :num with :data to id :id + */ + public function userUploadsNewChunkFileOfWithToId($user, $num, $data, $id) + { + $data = \GuzzleHttp\Stream\Stream::factory($data); + $destination = '/uploads/'.$user.'/'.$id.'/'.$num; + $this->makeDavRequest($user, 'PUT', $destination, [], $data); + } + + /** + * @Given user :user moves new chunk file with id :id to :dest + */ + public function userMovesNewChunkFileWithIdToMychunkedfile($user, $id, $dest) + { + $source = '/uploads/'.$user.'/'.$id.'/.file'; + $destination = substr($this->baseUrl, 0, -4) . $this->davPath . '/files/'.$user.$dest; + $this->makeDavRequest($user, 'MOVE', $source, [ + 'Destination' => $destination + ]); + } + + /** + * @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(); + } + } + + /** + * URL encodes the given path but keeps the slashes + * + * @param string $path to encode + * @return string encoded path + */ + private function encodePath($path) { + // slashes need to stay + return str_replace('%2F', '/', rawurlencode($path)); + } + + /** + * @When user :user favorites element :path + */ + public function userFavoritesElement($user, $path){ + $this->response = $this->changeFavStateOfAnElement($user, $path, 1, 0, null); + } + + /** + * @When user :user unfavorites element :path + */ + public function userUnfavoritesElement($user, $path){ + $this->response = $this->changeFavStateOfAnElement($user, $path, 0, 0, null); + } + + /*Set the elements of a proppatch, $folderDepth requires 1 to see elements without children*/ + public function changeFavStateOfAnElement($user, $path, $favOrUnfav, $folderDepth, $properties = null){ + $fullUrl = substr($this->baseUrl, 0, -4); + $settings = array( + 'baseUri' => $fullUrl, + 'userName' => $user, + ); + if ($user === 'admin') { + $settings['password'] = $this->adminUser[1]; + } else { + $settings['password'] = $this->regularUser; + } + $client = new SClient($settings); + if (!$properties) { + $properties = [ + '{http://owncloud.org/ns}favorite' => $favOrUnfav + ]; + } + + $response = $client->proppatch($this->davPath . '/' . ltrim($path, '/'), $properties, $folderDepth); + return $response; + } + + /** + * @Then /^as "([^"]*)" gets properties of file "([^"]*)" with$/ + * @param string $user + * @param string $path + * @param \Behat\Gherkin\Node\TableNode|null $propertiesTable + */ + public function asGetsPropertiesOfFileWith($user, $path, $propertiesTable) { + $this->asGetsPropertiesOfFolderWith($user, $path, $propertiesTable); + } +} diff --git a/build/integration/features/dav-v2.feature b/build/integration/features/dav-v2.feature new file mode 100644 index 00000000000..9de78a7ed16 --- /dev/null +++ b/build/integration/features/dav-v2.feature @@ -0,0 +1,55 @@ +Feature: dav-v2 + Background: + Given using api version "1" + + Scenario: moving a file new endpoint way + Given using dav path "remote.php/dav" + And As an "admin" + And user "user0" exists + When User "user0" moves file "/files/user0/textfile0.txt" to "/files/user0/abcde.txt" + Then the HTTP status code should be "201" + + Scenario: download a file with range using new endpoint + Given using dav path "remote.php/dav" + And As an "admin" + And user "user0" exists + And As an "user0" + When Downloading file "/files/user0/welcome.txt" with range "bytes=51-77" + Then Downloaded content should be "example file for developers" + + Scenario: Downloading a file on the new endpoint should serve security headers + Given using dav path "remote.php/dav/files/admin/" + And As an "admin" + When Downloading file "welcome.txt" + Then The following headers should be set + |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| + |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| + |X-Content-Type-Options |nosniff| + |X-Download-Options|noopen| + |X-Frame-Options|Sameorigin| + |X-Permitted-Cross-Domain-Policies|none| + |X-Robots-Tag|none| + |X-XSS-Protection|1; mode=block| + And Downloaded content should start with "Welcome to your ownCloud account!" + + Scenario: Doing a GET with a web login should work without CSRF token on the new backend + Given Logging in using web as "admin" + When Sending a "GET" to "/remote.php/dav/files/admin/welcome.txt" without requesttoken + Then Downloaded content should start with "Welcome to your ownCloud account!" + Then the HTTP status code should be "200" + + Scenario: Doing a GET with a web login should work with CSRF token on the new backend + Given Logging in using web as "admin" + When Sending a "GET" to "/remote.php/dav/files/admin/welcome.txt" with requesttoken + Then Downloaded content should start with "Welcome to your ownCloud account!" + Then the HTTP status code should be "200" + + Scenario: Doing a PROPFIND with a web login should not work without CSRF token on the new backend + Given Logging in using web as "admin" + When Sending a "PROPFIND" to "/remote.php/dav/files/admin/welcome.txt" without requesttoken + Then the HTTP status code should be "401" + + Scenario: Doing a PROPFIND with a web login should work with CSRF token on the new backend + Given Logging in using web as "admin" + When Sending a "PROPFIND" to "/remote.php/dav/files/admin/welcome.txt" with requesttoken + Then the HTTP status code should be "207" diff --git a/build/integration/features/favorites.feature b/build/integration/features/favorites.feature new file mode 100644 index 00000000000..86643fdd1e2 --- /dev/null +++ b/build/integration/features/favorites.feature @@ -0,0 +1,42 @@ +Feature: favorite + Background: + Given using api version "1" + + Scenario: Favorite a folder + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + When user "user0" favorites element "/FOLDER" + Then as "user0" gets properties of folder "/FOLDER" with + |{http://owncloud.org/ns}favorite| + And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1" + + Scenario: Favorite and unfavorite a folder + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + When user "user0" favorites element "/FOLDER" + And user "user0" unfavorites element "/FOLDER" + Then as "user0" gets properties of folder "/FOLDER" with + |{http://owncloud.org/ns}favorite| + And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "" + + Scenario: Favorite a file + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + When user "user0" favorites element "/textfile0.txt" + Then as "user0" gets properties of file "/textfile0.txt" with + |{http://owncloud.org/ns}favorite| + And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1" + + Scenario: Favorite and unfavorite a file + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + When user "user0" favorites element "/textfile0.txt" + And user "user0" unfavorites element "/textfile0.txt" + Then as "user0" gets properties of file "/textfile0.txt" with + |{http://owncloud.org/ns}favorite| + And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "" + diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index 8c32c04523c..625d24260de 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -315,3 +315,20 @@ 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: 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/sharees.feature b/build/integration/features/sharees.feature deleted file mode 100644 index ea3a2c9bcea..00000000000 --- a/build/integration/features/sharees.feature +++ /dev/null @@ -1,148 +0,0 @@ -Feature: sharees - Background: - Given using api version "1" - - Scenario: Search without exact match - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | Sharee | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And "exact users" sharees returned is empty - And "users" sharees returned are - | Sharee1 | 0 | Sharee1 | - And "exact groups" sharees returned is empty - And "groups" sharees returned are - | ShareeGroup | 1 | ShareeGroup | - And "exact remotes" sharees returned is empty - And "remotes" sharees returned is empty - - Scenario: Search without exact match not-exact casing - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | sharee | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And "exact users" sharees returned is empty - And "users" sharees returned are - | Sharee1 | 0 | Sharee1 | - And "exact groups" sharees returned is empty - And "groups" sharees returned are - | ShareeGroup | 1 | ShareeGroup | - And "exact remotes" sharees returned is empty - And "remotes" sharees returned is empty - - Scenario: Search with exact match - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | Sharee1 | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned are - | Sharee1 | 0 | Sharee1 | - Then "users" sharees returned is empty - Then "exact groups" sharees returned is empty - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned is empty - Then "remotes" sharees returned is empty - - Scenario: Search with exact match not-exact casing - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | sharee1 | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned are - | Sharee1 | 0 | Sharee1 | - Then "users" sharees returned is empty - Then "exact groups" sharees returned is empty - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned is empty - Then "remotes" sharees returned is empty - - Scenario: Search with exact match not-exact casing group - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | shareegroup | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned is empty - Then "users" sharees returned is empty - Then "exact groups" sharees returned are - | ShareeGroup | 1 | ShareeGroup | - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned is empty - Then "remotes" sharees returned is empty - - Scenario: Search with "self" - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "Sharee1" - When getting sharees for - | search | Sharee1 | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned are - | Sharee1 | 0 | Sharee1 | - Then "users" sharees returned is empty - Then "exact groups" sharees returned is empty - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned is empty - Then "remotes" sharees returned is empty - - Scenario: Remote sharee for files - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | test@localhost | - | itemType | file | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned is empty - Then "users" sharees returned is empty - Then "exact groups" sharees returned is empty - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned are - | test@localhost | 6 | test@localhost | - Then "remotes" sharees returned is empty - - Scenario: Remote sharee for calendars not allowed - Given user "test" exists - And user "Sharee1" exists - And group "ShareeGroup" exists - And As an "test" - When getting sharees for - | search | test@localhost | - | itemType | calendar | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - Then "exact users" sharees returned is empty - Then "users" sharees returned is empty - Then "exact groups" sharees returned is empty - Then "groups" sharees returned is empty - Then "exact remotes" sharees returned is empty - Then "remotes" sharees returned is empty diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature index ed8f3b23f25..da6ab1d71d3 100644 --- a/build/integration/features/sharing-v1.feature +++ b/build/integration/features/sharing-v1.feature @@ -266,6 +266,20 @@ Feature: sharing And User "user2" should be included in the response And User "user3" should not be included in the response + Scenario: Reshared files can be still accessed if a user in the middle removes it. + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user3" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile0 (2).txt" of user "user1" is shared with user "user2" + And file "textfile0 (2).txt" of user "user2" is shared with user "user3" + And As an "user1" + When User "user1" deletes file "/textfile0 (2).txt" + And As an "user3" + And Downloading file "/textfile0 (2).txt" with range "bytes=1-7" + Then Downloaded content should be "wnCloud" + Scenario: getting share info of a share Given user "user0" exists And user "user1" exists @@ -582,7 +596,7 @@ Feature: sharing | shareType | 3 | Then share ids should match - Scenario: unique target names for incomming shares + Scenario: unique target names for incoming shares Given user "user0" exists And user "user1" exists And user "user2" exists diff --git a/build/integration/features/tags.feature b/build/integration/features/tags.feature index 286fb62bf42..e2acadbe541 100644 --- a/build/integration/features/tags.feature +++ b/build/integration/features/tags.feature @@ -368,3 +368,4 @@ Feature: tags And "/myFileToTag.txt" shared by "user0" has the following tags for "user1" || And The response should have a status code "404" + diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature index f0712751b7f..d19fd4988d4 100644 --- a/build/integration/features/webdav-related.feature +++ b/build/integration/features/webdav-related.feature @@ -169,6 +169,15 @@ Feature: webdav-related And Downloading last public shared file with range "bytes=51-77" Then Downloaded content should be "example file for developers" + Scenario: download a public shared file inside a folder with range + Given user "user0" exists + And As an "user0" + When creating a share with + | path | PARENT | + | shareType | 3 | + And Downloading last public shared file inside a folder "/parent.txt" with range "bytes=1-7" + Then Downloaded content should be "wnCloud" + Scenario: Downloading a file on the old endpoint should serve security headers Given using dav path "remote.php/webdav" And As an "admin" @@ -184,43 +193,6 @@ Feature: webdav-related |X-XSS-Protection|1; mode=block| And Downloaded content should start with "Welcome to your ownCloud account!" - Scenario: Downloading a file on the new endpoint should serve security headers - Given using dav path "remote.php/dav/files/admin/" - And As an "admin" - When Downloading file "/welcome.txt" - Then The following headers should be set - |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| - |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| - |X-Content-Type-Options |nosniff| - |X-Download-Options|noopen| - |X-Frame-Options|Sameorigin| - |X-Permitted-Cross-Domain-Policies|none| - |X-Robots-Tag|none| - |X-XSS-Protection|1; mode=block| - And Downloaded content should start with "Welcome to your ownCloud account!" - - Scenario: Doing a GET with a web login should work without CSRF token on the new backend - Given Logging in using web as "admin" - When Sending a "GET" to "/remote.php/dav/files/admin/welcome.txt" without requesttoken - Then Downloaded content should start with "Welcome to your ownCloud account!" - Then the HTTP status code should be "200" - - Scenario: Doing a GET with a web login should work with CSRF token on the new backend - Given Logging in using web as "admin" - When Sending a "GET" to "/remote.php/dav/files/admin/welcome.txt" with requesttoken - Then Downloaded content should start with "Welcome to your ownCloud account!" - Then the HTTP status code should be "200" - - Scenario: Doing a PROPFIND with a web login should not work without CSRF token on the new backend - Given Logging in using web as "admin" - When Sending a "PROPFIND" to "/remote.php/dav/files/admin/welcome.txt" without requesttoken - Then the HTTP status code should be "401" - - Scenario: Doing a PROPFIND with a web login should work with CSRF token on the new backend - Given Logging in using web as "admin" - When Sending a "PROPFIND" to "/remote.php/dav/files/admin/welcome.txt" with requesttoken - Then the HTTP status code should be "207" - Scenario: Doing a GET with a web login should work without CSRF token on the old backend Given Logging in using web as "admin" When Sending a "GET" to "/remote.php/webdav/welcome.txt" without requesttoken @@ -342,3 +314,4 @@ Feature: webdav-related | 0 | | 1 | | 3 | + diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature index 5437d01dee2..8bf8e921b0f 100644 --- a/build/integration/federation_features/federated.feature +++ b/build/integration/federation_features/federated.feature @@ -10,7 +10,177 @@ Feature: federated When User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" Then the OCS status code should be "100" And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | item_type | file | + | item_source | A_NUMBER | + | share_type | 6 | + | file_source | A_NUMBER | + | path | /textfile0.txt | + | permissions | 19 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | share_with | user1@REMOTE | + | share_with_displayname | user1@REMOTE | + Scenario: Federate share a file with local server + Given Using server "LOCAL" + And user "user0" exists + And Using server "REMOTE" + And user "user1" exists + When User "user1" from server "REMOTE" shares "/textfile0.txt" with user "user0" from server "LOCAL" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | item_type | file | + | item_source | A_NUMBER | + | share_type | 6 | + | file_source | A_NUMBER | + | path | /textfile0.txt | + | permissions | 19 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user1 | + | storage_id | home::user1 | + | file_parent | A_NUMBER | + | displayname_owner | user1 | + | share_with | user0@LOCAL | + | share_with_displayname | user0@LOCAL | + + Scenario: Remote sharee can see the pending share + Given Using server "REMOTE" + And user "user1" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And Using server "REMOTE" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | remote | LOCAL | + | remote_id | A_NUMBER | + | share_token | A_TOKEN | + | name | /textfile0.txt | + | owner | user0 | + | user | user1 | + | mountpoint | {{TemporaryMountPointName#/textfile0.txt}} | + | accepted | 0 | + + Scenario: accept a pending remote share + Given Using server "REMOTE" + And user "user1" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + When User "user1" from server "REMOTE" accepts last pending share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Reshare a federated shared file + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + When creating a share with + | path | /textfile0 (2).txt | + | shareType | 0 | + | shareWith | user2 | + | permissions | 19 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Share fields of last share match with + | id | A_NUMBER | + | item_type | file | + | item_source | A_NUMBER | + | share_type | 0 | + | file_source | A_NUMBER | + | path | /textfile0 (2).txt | + | permissions | 19 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user1 | + | file_parent | A_NUMBER | + | displayname_owner | user1 | + | share_with | user2 | + | share_with_displayname | user2 | + + Scenario: Overwrite a federated shared file as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt" + And Downloading file "/textfile0 (2).txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared folder as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt" + And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared file as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/textfile0 (2).txt" + When Downloading file "/textfile0 (2).txt" with range "bytes=0-4" + Then Downloaded content should be "AAAAA" + + Scenario: Overwrite a federated shared folder as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/PARENT (2)/textfile0.txt" + When Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=3-13" + Then Downloaded content should be "AABBBBBCCCC" diff --git a/build/integration/sharees_features/sharees.feature b/build/integration/sharees_features/sharees.feature new file mode 100644 index 00000000000..58570cfc5f1 --- /dev/null +++ b/build/integration/sharees_features/sharees.feature @@ -0,0 +1,240 @@ +Feature: sharees + Background: + Given using api version "1" + And user "test" exists + And user "Sharee1" exists + And group "ShareeGroup" exists + And user "test" belongs to group "ShareeGroup" + + Scenario: Search without exact match + Given As an "test" + When getting sharees for + | search | Sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned are + | Sharee1 | 0 | Sharee1 | + And "exact groups" sharees returned is empty + And "groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search without exact match not-exact casing + Given As an "test" + When getting sharees for + | search | sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned are + | Sharee1 | 0 | Sharee1 | + And "exact groups" sharees returned is empty + And "groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search only with group members - denied + Given As an "test" + And parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes" + When getting sharees for + | search | sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned is empty + And "exact groups" sharees returned is empty + And "groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search only with group members - allowed + Given As an "test" + And parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes" + And user "Sharee1" belongs to group "ShareeGroup" + When getting sharees for + | search | sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned are + | Sharee1 | 0 | Sharee1 | + And "exact groups" sharees returned is empty + And "groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search only with group members - no group as non-member + Given As an "Sharee1" + And parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes" + When getting sharees for + | search | sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned is empty + And "exact groups" sharees returned is empty + And "groups" sharees returned is empty + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search without exact match no iteration allowed + Given As an "test" + And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no" + When getting sharees for + | search | Sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned is empty + And "exact groups" sharees returned is empty + And "groups" sharees returned is empty + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search with exact match no iteration allowed + Given As an "test" + And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no" + When getting sharees for + | search | Sharee1 | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned are + | Sharee1 | 0 | Sharee1 | + And "users" sharees returned is empty + And "exact groups" sharees returned is empty + And "groups" sharees returned is empty + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search with exact match group no iteration allowed + Given As an "test" + And parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no" + When getting sharees for + | search | ShareeGroup | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned is empty + And "exact groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + And "groups" sharees returned is empty + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty + + Scenario: Search with exact match + Given As an "test" + When getting sharees for + | search | Sharee1 | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned are + | Sharee1 | 0 | Sharee1 | + Then "users" sharees returned is empty + Then "exact groups" sharees returned is empty + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned is empty + Then "remotes" sharees returned is empty + + Scenario: Search with exact match not-exact casing + Given As an "test" + When getting sharees for + | search | sharee1 | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned are + | Sharee1 | 0 | Sharee1 | + Then "users" sharees returned is empty + Then "exact groups" sharees returned is empty + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned is empty + Then "remotes" sharees returned is empty + + Scenario: Search with exact match not-exact casing group + Given As an "test" + When getting sharees for + | search | shareegroup | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned is empty + Then "users" sharees returned is empty + Then "exact groups" sharees returned are + | ShareeGroup | 1 | ShareeGroup | + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned is empty + Then "remotes" sharees returned is empty + + Scenario: Search with "self" + Given As an "Sharee1" + When getting sharees for + | search | Sharee1 | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned are + | Sharee1 | 0 | Sharee1 | + Then "users" sharees returned is empty + Then "exact groups" sharees returned is empty + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned is empty + Then "remotes" sharees returned is empty + + Scenario: Remote sharee for files + Given As an "test" + When getting sharees for + | search | test@localhost | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned is empty + Then "users" sharees returned is empty + Then "exact groups" sharees returned is empty + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned are + | test@localhost | 6 | test@localhost | + Then "remotes" sharees returned is empty + + Scenario: Remote sharee for calendars not allowed + Given As an "test" + When getting sharees for + | search | test@localhost | + | itemType | calendar | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + Then "exact users" sharees returned is empty + Then "users" sharees returned is empty + Then "exact groups" sharees returned is empty + Then "groups" sharees returned is empty + Then "exact remotes" sharees returned is empty + Then "remotes" sharees returned is empty + + Scenario: Group sharees not returned when group sharing is disabled + Given As an "test" + And parameter "shareapi_allow_group_sharing" of app "core" is set to "no" + When getting sharees for + | search | sharee | + | itemType | file | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And "exact users" sharees returned is empty + And "users" sharees returned are + | Sharee1 | 0 | Sharee1 | + And "exact groups" sharees returned is empty + And "groups" sharees returned is empty + And "exact remotes" sharees returned is empty + And "remotes" sharees returned is empty |