diff options
author | Sergio BertolÃn <sbertolin@solidgear.es> | 2016-07-25 12:06:28 +0200 |
---|---|---|
committer | Thomas Müller <DeepDiver1975@users.noreply.github.com> | 2016-07-25 12:06:28 +0200 |
commit | 4ae337f827e328fa876563f295839528189c413e (patch) | |
tree | f49d299cc70340781d1a274d4b93b1153202d75a | |
parent | 3eaa45ddcc61686aa0a4a742c1a00f6ed855697d (diff) | |
download | nextcloud-server-4ae337f827e328fa876563f295839528189c413e.tar.gz nextcloud-server-4ae337f827e328fa876563f295839528189c413e.zip |
Adapted integration tests for stable8.2 (#25456)
* Adapted integration tests for stable8.2
* Fixed provisioning-v1:279
* Removed provisioning-v1.feature:160
* Removed tests related with enabling or disabling users
* Removed tests about permissions property added in 9.1
* Removed check of mimetype and permissions to expect 7 instead of 15
* Removed tests only suitable for 9.1 and modified expected fields which doesn't work on 8.2
* Removed test for allowing sharing of root folder
* Fixed problem with shares involving a rename which calls setupFS and requires to check the parent id instead of the actual share id
* Removed quota check fixed only in >9.0
* Removed test with undefined quota -3 only in >9.0
* Removed tests about new chunking (only >9.1)
* removed all the field checks in fed sharing, as they appeared after 8.2
* Removed tests about CSRF only suitable for master
* Removed tests about special characters in groups
* Modified range to start on 0
-rw-r--r-- | build/integration/composer.json | 4 | ||||
-rw-r--r-- | build/integration/config/behat.yml | 42 | ||||
-rw-r--r-- | build/integration/data/textfile.txt | 3 | ||||
-rw-r--r-- | build/integration/features/bootstrap/BasicStructure.php | 312 | ||||
-rw-r--r-- | build/integration/features/bootstrap/FeatureContext.php | 244 | ||||
-rw-r--r-- | build/integration/features/bootstrap/FederationContext.php | 54 | ||||
-rw-r--r-- | build/integration/features/bootstrap/Provisioning.php | 656 | ||||
-rw-r--r-- | build/integration/features/bootstrap/Sharing.php | 498 | ||||
-rw-r--r-- | build/integration/features/bootstrap/WebDav.php | 508 | ||||
-rw-r--r-- | build/integration/features/favorites.feature | 42 | ||||
-rw-r--r-- | build/integration/features/provisioning-v1.feature | 394 | ||||
-rw-r--r-- | build/integration/features/sharing-v1.feature | 569 | ||||
-rw-r--r-- | build/integration/features/webdav-related.feature | 203 | ||||
-rw-r--r-- | build/integration/federation_features/federated.feature | 156 | ||||
-rwxr-xr-x | build/integration/run.sh | 25 |
15 files changed, 3351 insertions, 359 deletions
diff --git a/build/integration/composer.json b/build/integration/composer.json index 98b2f294c7a..29f73f2064a 100644 --- a/build/integration/composer.json +++ b/build/integration/composer.json @@ -1,7 +1,9 @@ { "require-dev": { "phpunit/phpunit": "~4.6", + "behat/behat": "^3.0", "guzzlehttp/guzzle": "~5.0", - "behat/behat": "2.4.*@stable" + "jarnaiz/behat-junit-formatter": "^1.3", + "sabre/dav": "3.0.9" } } diff --git a/build/integration/config/behat.yml b/build/integration/config/behat.yml index 01ca0d18790..c898c285a31 100644 --- a/build/integration/config/behat.yml +++ b/build/integration/config/behat.yml @@ -1,17 +1,31 @@ default: - paths: - features: ../features - bootstrap: %behat.paths.features%/bootstrap + autoload: + '': %paths.base%/../features/bootstrap + suites: + default: + paths: + - %paths.base%/../features + contexts: + - FeatureContext: + baseUrl: http://localhost:8080/ocs/ + admin: + - admin + - admin + regular_user_password: 123456 + federation: + paths: + - %paths.base%/../federation_features + contexts: + - FederationContext: + baseUrl: http://localhost:8080/ocs/ + admin: + - admin + - admin + regular_user_password: 123456 - context: - parameters: - baseUrl: http://localhost:8080/ocs/ - admin: - - admin - - admin -ci: - formatter: - name: pretty,junit - parameters: - output_path: null,./output + + extensions: + jarnaiz\JUnitFormatter\JUnitFormatterExtension: + filename: report.xml + outputDir: %paths.base%/../output/ diff --git a/build/integration/data/textfile.txt b/build/integration/data/textfile.txt new file mode 100644 index 00000000000..efffdeff159 --- /dev/null +++ b/build/integration/data/textfile.txt @@ -0,0 +1,3 @@ +This is a testfile. + +Cheers.
\ No newline at end of file diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php new file mode 100644 index 00000000000..f693a242e17 --- /dev/null +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -0,0 +1,312 @@ +<?php + +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + +trait BasicStructure { + + /** @var string */ + private $currentUser = ''; + + /** @var string */ + private $currentServer = ''; + + /** @var string */ + private $baseUrl = ''; + + /** @var int */ + private $apiVersion = 1; + + /** @var ResponseInterface */ + private $response = null; + + /** @var \GuzzleHttp\Cookie\CookieJar */ + private $cookieJar; + + /** @var string */ + private $requestToken; + + public function __construct($baseUrl, $admin, $regular_user_password) { + + // Initialize your context here + $this->baseUrl = $baseUrl; + $this->adminUser = $admin; + $this->regularUser = $regular_user_password; + $this->localBaseUrl = $this->baseUrl; + $this->remoteBaseUrl = $this->baseUrl; + $this->currentServer = 'LOCAL'; + $this->cookieJar = new \GuzzleHttp\Cookie\CookieJar(); + + // in case of ci deployment we take the server url from the environment + $testServerUrl = getenv('TEST_SERVER_URL'); + if ($testServerUrl !== false) { + $this->baseUrl = $testServerUrl; + $this->localBaseUrl = $testServerUrl; + } + + // federated server url from the environment + $testRemoteServerUrl = getenv('TEST_SERVER_FED_URL'); + if ($testRemoteServerUrl !== false) { + $this->remoteBaseUrl = $testRemoteServerUrl; + } + } + + /** + * @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 "(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'; + return $previousServer; + } else { + $this->baseUrl = $this->remoteBaseUrl; + $this->currentServer = 'REMOTE'; + return $previousServer; + } + } + + /** + * @When /^sending "([^"]*)" to "([^"]*)"$/ + * @param string $verb + * @param string $url + */ + public function sendingTo($verb, $url) { + $this->sendingToWith($verb, $url, null); + } + + /** + * Parses the xml answer to get ocs response which doesn't match with + * http one in v1 of the api. + * @param ResponseInterface $response + * @return string + */ + public function getOCSResponse($response) { + return $response->xml()->meta[0]->statuscode; + } + + /** + * 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); + return $a; + } + + /** + * @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; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$this->currentUser, $this->regularUser]; + } + if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $fd = $body->getRowsHash(); + $options['body'] = $fd; + } + + try { + $this->response = $client->send($client->createRequest($verb, $fullUrl, $options)); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + } + } + + public function isExpectedUrl($possibleUrl, $finalPart){ + $baseUrlChopped = substr($this->baseUrl, 0, -4); + $endCharacter = strlen($baseUrlChopped) + strlen($finalPart); + return (substr($possibleUrl,0,$endCharacter) == "$baseUrlChopped" . "$finalPart"); + } + + /** + * @Then /^the OCS status code should be "([^"]*)"$/ + * @param int $statusCode + */ + public function theOCSStatusCodeShouldBe($statusCode) { + PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response)); + } + + /** + * @Then /^the HTTP status code should be "([^"]*)"$/ + * @param int $statusCode + */ + public function theHTTPStatusCodeShouldBe($statusCode) { + PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode()); + } + + /** + * @param ResponseInterface $response + */ + private function extracRequestTokenFromResponse(ResponseInterface $response) { + $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) . '/login'; + // Request a new session and extract CSRF token + $client = new Client(); + $response = $client->get( + $loginUrl, + [ + 'cookies' => $this->cookieJar, + ] + ); + $this->extracRequestTokenFromResponse($response); + + // Login and extract new token + $password = ($user === 'admin') ? 'admin' : '123456'; + $client = new Client(); + $response = $client->post( + $loginUrl, + [ + 'body' => [ + 'user' => $user, + 'password' => $password, + 'requesttoken' => $this->requestToken, + ], + 'cookies' => $this->cookieJar, + ] + ); + $this->extracRequestTokenFromResponse($response); + } + + /** + * @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); + + $client = new Client(); + $request = $client->createRequest( + $method, + $baseUrl . $url, + [ + 'cookies' => $this->cookieJar, + ] + ); + $request->addHeader('requesttoken', $this->requestToken); + try { + $this->response = $client->send($request); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @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); + + $client = new Client(); + $request = $client->createRequest( + $method, + $baseUrl . $url, + [ + 'cookies' => $this->cookieJar, + ] + ); + try { + $this->response = $client->send($request); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + public static function removeFile($path, $filename){ + if (file_exists("$path" . "$filename")) { + unlink("$path" . "$filename"); + } + } + + /** + * @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(){ + for ($i=0; $i<5; $i++){ + file_put_contents("../../core/skeleton/" . "textfile" . "$i" . ".txt", "ownCloud test text file\n"); + } + if (!file_exists("../../core/skeleton/FOLDER")) { + mkdir("../../core/skeleton/FOLDER", 0777, true); + } + if (!file_exists("../../core/skeleton/PARENT")) { + mkdir("../../core/skeleton/PARENT", 0777, true); + } + file_put_contents("../../core/skeleton/PARENT/" . "parent.txt", "ownCloud test text file\n"); + if (!file_exists("../../core/skeleton/PARENT/CHILD")) { + mkdir("../../core/skeleton/PARENT/CHILD", 0777, true); + } + file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "ownCloud test text file\n"); + } + + /** + * @AfterSuite + */ + public static function removeFilesFromSkeleton(){ + for ($i=0; $i<5; $i++){ + self::removeFile("../../core/skeleton/", "textfile" . "$i" . ".txt"); + } + if (is_dir("../../core/skeleton/FOLDER")) { + rmdir("../../core/skeleton/FOLDER"); + } + self::removeFile("../../core/skeleton/PARENT/CHILD/", "child.txt"); + if (is_dir("../../core/skeleton/PARENT/CHILD")) { + rmdir("../../core/skeleton/PARENT/CHILD"); + } + self::removeFile("../../core/skeleton/PARENT/", "parent.txt"); + if (is_dir("../../core/skeleton/PARENT")) { + rmdir("../../core/skeleton/PARENT"); + } + } +} + diff --git a/build/integration/features/bootstrap/FeatureContext.php b/build/integration/features/bootstrap/FeatureContext.php index caff517a16d..21ca8d87295 100644 --- a/build/integration/features/bootstrap/FeatureContext.php +++ b/build/integration/features/bootstrap/FeatureContext.php @@ -1,248 +1,14 @@ <?php -use Behat\Behat\Context\BehatContext; -use GuzzleHttp\Client; -use GuzzleHttp\Message\ResponseInterface; +use Behat\Behat\Context\Context; +use Behat\Behat\Context\SnippetAcceptingContext; require __DIR__ . '/../../vendor/autoload.php'; + /** * Features context. */ -class FeatureContext extends BehatContext { - - /** @var string */ - private $baseUrl = ''; - - /** @var ResponseInterface */ - private $response = null; - - /** @var string */ - private $currentUser = ''; - - /** @var int */ - private $apiVersion = 1; - - /** - * Initializes context. - * Every scenario gets it's own context object. - * - * @param array $parameters context parameters (set them up through behat.yml) - */ - public function __construct(array $parameters) { - - // Initialize your context here - $this->baseUrl = $parameters['baseUrl']; - $this->adminUser = $parameters['admin']; - - // in case of ci deployment we take the server url from the environment - $testServerUrl = getenv('TEST_SERVER_URL'); - if ($testServerUrl !== false) { - $this->baseUrl = $testServerUrl; - } - } - - /** - * @When /^sending "([^"]*)" to "([^"]*)"$/ - */ - public function sendingTo($verb, $url) { - $this->sendingToWith($verb, $url, null); - } - - /** - * Parses the xml answer to get ocs response which doesn't match with - * http one in v1 of the api. - */ - public function getOCSResponse($response) { - return $response->xml()->meta[0]->statuscode; - } - - /** - * Parses the xml answer to get the array of users returned. - */ - public function getArrayOfUsersResponded($resp) { - $listCheckedElements = $resp->xml()->data[0]->users[0]->element; - $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); - return $extractedElementsArray; - } - - /** - * Parses the xml answer to get the array of groups returned. - */ - public function getArrayOfGroupsResponded($resp) { - $listCheckedElements = $resp->xml()->data[0]->groups[0]->element; - $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); - return $extractedElementsArray; - } - - /** - * @Then /^users returned are$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData - */ - public function theUsersShouldBe($usersList) { - if ($usersList instanceof \Behat\Gherkin\Node\TableNode) { - $users = $usersList->getRows()[0]; - $respondedArray = $this->getArrayOfUsersResponded($this->response); - PHPUnit_Framework_Assert::assertEquals(asort($users), asort($respondedArray)); - } - - } - - /** - * @Then /^groups returned are$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData - */ - public function theGroupsShouldBe($groupsList) { - if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) { - $groups = $groupsList->getRows()[0]; - $respondedArray = $this->getArrayOfGroupsResponded($this->response); - PHPUnit_Framework_Assert::assertEquals(asort($groups), asort($respondedArray)); - } - - } - - /** - * @Then /^the OCS status code should be "([^"]*)"$/ - */ - public function theOCSStatusCodeShouldBe($statusCode) { - PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response)); - } - - /** - * @Then /^the HTTP status code should be "([^"]*)"$/ - */ - public function theHTTPStatusCodeShouldBe($statusCode) { - PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode()); - } - - /** - * @Given /^As an "([^"]*)"$/ - */ - public function asAn($user) { - $this->currentUser = $user; - } - - /** - * @Given /^using api version "([^"]*)"$/ - */ - public function usingApiVersion($version) { - $this->apiVersion = $version; - } - - /** - * @Given /^user "([^"]*)" exists$/ - */ - public function userExists($user) { - $fullUrl = $this->baseUrl . "v2.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(200, $this->response->getStatusCode()); - } - - /** - * @Given /^user "([^"]*)" does not exist$/ - */ - public function userDoesNotExist($user) { - try { - $this->userExists($user); - PHPUnit_Framework_Assert::fail('The user "' . $user . '" exists'); - } catch (\GuzzleHttp\Exception\ClientException $ex) { - $this->response = $ex->getResponse(); - PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); - } - } - - /** - * @When /^creating the user "([^"]*)r"$/ - */ - public function creatingTheUser($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->post($fullUrl, [ - 'form_params' => [ - 'userid' => $user, - 'password' => '123456' - ] - ]); - - } - - /** - * @When /^creating the group "([^"]*)r"$/ - */ - public function creatingTheGroup($group) { - $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups/addgroup"; - $client = new Client(); - $options = []; - if ($this->currentUser === 'admin') { - $options['auth'] = $this->adminUser; - } - - $this->response = $client->post($fullUrl, [ - 'form_params' => [ - 'groupid' => $user - ] - ]); - } - - /** - * @Given /^group "([^"]*)" exists$/ - */ - public function groupExists($group) { - $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group"; - $client = new Client(); - $options = []; - if ($this->currentUser === 'admin') { - $options['auth'] = $this->adminUser; - } - - $this->response = $client->get($fullUrl, $options); - PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); - } - - /** - * @Given /^group "([^"]*)" does not exist$/ - */ - public function groupDoesNotExist($group) { - try { - $this->groupExists($group); - PHPUnit_Framework_Assert::fail('The group "' . $group . '" exists'); - } catch (\GuzzleHttp\Exception\ClientException $ex) { - $this->response = $ex->getResponse(); - PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); - } - } - - /** - * @When /^sending "([^"]*)" to "([^"]*)" with$/ - * @param \Behat\Gherkin\Node\TableNode|null $formData - */ - public function sendingToWith($verb, $url, $body) { - $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url; - $client = new Client(); - $options = []; - if ($this->currentUser === 'admin') { - $options['auth'] = $this->adminUser; - } - if ($body instanceof \Behat\Gherkin\Node\TableNode) { - $fd = $body->getRowsHash(); - $options['body'] = $fd; - } - - try { - $this->response = $client->send($client->createRequest($verb, $fullUrl, $options)); - } catch (\GuzzleHttp\Exception\ClientException $ex) { - $this->response = $ex->getResponse(); - } - } +class FeatureContext implements Context, SnippetAcceptingContext { + use WebDav; } diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php new file mode 100644 index 00000000000..55f3a55da0d --- /dev/null +++ b/build/integration/features/bootstrap/FederationContext.php @@ -0,0 +1,54 @@ +<?php + +use Behat\Behat\Context\Context; +use Behat\Behat\Context\SnippetAcceptingContext; +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + +/** + * Federation context. + */ +class FederationContext implements Context, SnippetAcceptingContext { + + use WebDav; + + /** + * @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($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 new file mode 100644 index 00000000000..6cf57514483 --- /dev/null +++ b/build/integration/features/bootstrap/Provisioning.php @@ -0,0 +1,656 @@ +<?php + +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + +trait Provisioning { + use BasicStructure; + + /** @var array */ + private $createdUsers = []; + + /** @var array */ + private $createdRemoteUsers = []; + + /** @var array */ + private $createdRemoteGroups = []; + + /** @var array */ + private $createdGroups = []; + + /** + * @Given /^user "([^"]*)" exists$/ + * @param string $user + */ + public function assureUserExists($user) { + try { + $this->userExists($user); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->creatingTheUser($user); + $this->currentUser = $previous_user; + } + $this->userExists($user); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @Given /^user "([^"]*)" does not exist$/ + * @param string $user + */ + public function userDoesNotExist($user) { + try { + $this->userExists($user); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); + return; + } + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->deletingTheUser($user); + $this->currentUser = $previous_user; + try { + $this->userExists($user); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); + } + } + + public function creatingTheUser($user) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/users"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $options['body'] = [ + 'userid' => $user, + 'password' => '123456' + ]; + + $this->response = $client->send($client->createRequest("POST", $fullUrl, $options)); + if ($this->currentServer === 'LOCAL'){ + $this->createdUsers[$user] = $user; + } elseif ($this->currentServer === 'REMOTE') { + $this->createdRemoteUsers[$user] = $user; + } + + //Quick hack to login once with the current user + $options2 = [ + 'auth' => [$user, '123456'], + ]; + $url = $fullUrl.'/'.$user; + $client->send($client->createRequest('GET', $url, $options2)); + } + + public function createUser($user) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->creatingTheUser($user); + $this->userExists($user); + $this->currentUser = $previous_user; + } + + public function deleteUser($user) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->deletingTheUser($user); + $this->userDoesNotExist($user); + $this->currentUser = $previous_user; + } + + public function createGroup($group) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->creatingTheGroup($group); + $this->groupExists($group); + $this->currentUser = $previous_user; + } + + public function deleteGroup($group) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->deletingTheGroup($group); + $this->groupDoesNotExist($group); + $this->currentUser = $previous_user; + } + + public function userExists($user){ + $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user"; + $client = new Client(); + $options = []; + $options['auth'] = $this->adminUser; + + $this->response = $client->get($fullUrl, $options); + } + + /** + * @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"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfGroupsResponded($this->response); + sort($respondedArray); + PHPUnit_Framework_Assert::assertContains($group, $respondedArray); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + public function userBelongsToGroup($user, $group) { + $fullUrl = $this->baseUrl . "v2.php/cloud/users/$user/groups"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfGroupsResponded($this->response); + + if (array_key_exists($group, $respondedArray)) { + return True; + } else{ + return False; + } + } + + /** + * @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)){ + $this->addingUserToGroup($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"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $groups = array($group); + $respondedArray = $this->getArrayOfGroupsResponded($this->response); + PHPUnit_Framework_Assert::assertNotEquals($groups, $respondedArray, "", 0.0, 10, true); + 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"; + $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)); + if ($this->currentServer === 'LOCAL'){ + $this->createdGroups[$group] = $group; + } elseif ($this->currentServer === 'REMOTE') { + $this->createdRemoteGroups[$group] = $group; + } + } + + /** + * @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"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->send($client->createRequest("DELETE", $fullUrl, $options)); + } + + /** + * @When /^Deleting the group "([^"]*)"$/ + * @param string $group + */ + public function deletingTheGroup($group) { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/cloud/groups/$group"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->send($client->createRequest("DELETE", $fullUrl, $options)); + } + + /** + * @Given /^Add user "([^"]*)" to the group "([^"]*)"$/ + * @param string $user + * @param string $group + */ + public function addUserToGroup($user, $group) { + $this->userExists($user); + $this->groupExists($group); + $this->addingUserToGroup($user, $group); + + } + + /** + * @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"; + $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)); + } + + + public function groupExists($group) { + $fullUrl = $this->baseUrl . "v2.php/cloud/groups/$group"; + $client = new Client(); + $options = []; + $options['auth'] = $this->adminUser; + + $this->response = $client->get($fullUrl, $options); + } + + /** + * @Given /^group "([^"]*)" exists$/ + * @param string $group + */ + public function assureGroupExists($group) { + try { + $this->groupExists($group); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->creatingTheGroup($group); + $this->currentUser = $previous_user; + } + $this->groupExists($group); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @Given /^group "([^"]*)" does not exist$/ + * @param string $group + */ + public function groupDoesNotExist($group) { + try { + $this->groupExists($group); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); + return; + } + $previous_user = $this->currentUser; + $this->currentUser = "admin"; + $this->deletingTheGroup($group); + $this->currentUser = $previous_user; + try { + $this->groupExists($group); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + PHPUnit_Framework_Assert::assertEquals(404, $ex->getResponse()->getStatusCode()); + } + } + + /** + * @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"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfSubadminsResponded($this->response); + sort($respondedArray); + PHPUnit_Framework_Assert::assertContains($user, $respondedArray); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @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"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfSubadminsResponded($this->response); + sort($respondedArray); + PHPUnit_Framework_Assert::assertNotContains($user, $respondedArray); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @Then /^users returned are$/ + * @param \Behat\Gherkin\Node\TableNode|null $usersList + */ + public function theUsersShouldBe($usersList) { + if ($usersList instanceof \Behat\Gherkin\Node\TableNode) { + $users = $usersList->getRows(); + $usersSimplified = $this->simplifyArray($users); + $respondedArray = $this->getArrayOfUsersResponded($this->response); + PHPUnit_Framework_Assert::assertEquals($usersSimplified, $respondedArray, "", 0.0, 10, true); + } + + } + + /** + * @Then /^groups returned are$/ + * @param \Behat\Gherkin\Node\TableNode|null $groupsList + */ + public function theGroupsShouldBe($groupsList) { + if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) { + $groups = $groupsList->getRows(); + $groupsSimplified = $this->simplifyArray($groups); + $respondedArray = $this->getArrayOfGroupsResponded($this->response); + PHPUnit_Framework_Assert::assertEquals($groupsSimplified, $respondedArray, "", 0.0, 10, true); + } + + } + + /** + * @Then /^subadmin groups returned are$/ + * @param \Behat\Gherkin\Node\TableNode|null $groupsList + */ + public function theSubadminGroupsShouldBe($groupsList) { + if ($groupsList instanceof \Behat\Gherkin\Node\TableNode) { + $groups = $groupsList->getRows(); + $groupsSimplified = $this->simplifyArray($groups); + $respondedArray = $this->getArrayOfSubadminsResponded($this->response); + PHPUnit_Framework_Assert::assertEquals($groupsSimplified, $respondedArray, "", 0.0, 10, true); + } + + } + + /** + * @Then /^apps returned are$/ + * @param \Behat\Gherkin\Node\TableNode|null $appList + */ + public function theAppsShouldBe($appList) { + if ($appList instanceof \Behat\Gherkin\Node\TableNode) { + $apps = $appList->getRows(); + $appsSimplified = $this->simplifyArray($apps); + $respondedArray = $this->getArrayOfAppsResponded($this->response); + PHPUnit_Framework_Assert::assertEquals($appsSimplified, $respondedArray, "", 0.0, 10, true); + } + + } + + /** + * @Then /^subadmin users returned are$/ + * @param \Behat\Gherkin\Node\TableNode|null $groupsList + */ + public function theSubadminUsersShouldBe($groupsList) { + $this->theSubadminGroupsShouldBe($groupsList); + } + + /** + * Parses the xml answer to get the array of users returned. + * @param ResponseInterface $resp + * @return array + */ + public function getArrayOfUsersResponded($resp) { + $listCheckedElements = $resp->xml()->data[0]->users[0]->element; + $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); + return $extractedElementsArray; + } + + /** + * Parses the xml answer to get the array of groups returned. + * @param ResponseInterface $resp + * @return array + */ + public function getArrayOfGroupsResponded($resp) { + $listCheckedElements = $resp->xml()->data[0]->groups[0]->element; + $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); + return $extractedElementsArray; + } + + /** + * Parses the xml answer to get the array of apps returned. + * @param ResponseInterface $resp + * @return array + */ + public function getArrayOfAppsResponded($resp) { + $listCheckedElements = $resp->xml()->data[0]->apps[0]->element; + $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); + return $extractedElementsArray; + } + + /** + * Parses the xml answer to get the array of subadmins returned. + * @param ResponseInterface $resp + * @return array + */ + public function getArrayOfSubadminsResponded($resp) { + $listCheckedElements = $resp->xml()->data[0]->element; + $extractedElementsArray = json_decode(json_encode($listCheckedElements), 1); + return $extractedElementsArray; + } + + + /** + * @Given /^app "([^"]*)" is disabled$/ + * @param string $app + */ + public function appIsDisabled($app) { + $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=disabled"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfAppsResponded($this->response); + PHPUnit_Framework_Assert::assertContains($app, $respondedArray); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @Given /^app "([^"]*)" is enabled$/ + * @param string $app + */ + public function appIsEnabled($app) { + $fullUrl = $this->baseUrl . "v2.php/cloud/apps?filter=enabled"; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } + + $this->response = $client->get($fullUrl, $options); + $respondedArray = $this->getArrayOfAppsResponded($this->response); + PHPUnit_Framework_Assert::assertContains($app, $respondedArray); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @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) + { + $body = new \Behat\Gherkin\Node\TableNode([ + 0 => ['key', 'quota'], + 1 => ['value', $quota], + ]); + + // method used from BasicStructure trait + $this->sendingToWith("PUT", "/cloud/users/" . $user, $body); + } + + /** + * @Given user :user has unlimited quota + * @param string $user + */ + public function userHasUnlimitedQuota($user) + { + $this->userHasAQuotaOf($user, 'none'); + } + + /** + * @BeforeScenario + * @AfterScenario + */ + public function cleanupUsers() + { + $previousServer = $this->currentServer; + $this->usingServer('LOCAL'); + foreach($this->createdUsers as $user) { + $this->deleteUser($user); + } + $this->usingServer('REMOTE'); + foreach($this->createdRemoteUsers as $remoteUser) { + $this->deleteUser($remoteUser); + } + $this->usingServer($previousServer); + } + + /** + * @BeforeScenario + * @AfterScenario + */ + public function cleanupGroups() + { + $previousServer = $this->currentServer; + $this->usingServer('LOCAL'); + foreach($this->createdGroups as $group) { + $this->deleteGroup($group); + } + $this->usingServer('REMOTE'); + foreach($this->createdRemoteGroups as $remoteGroup) { + $this->deleteUser($remoteGroup); + } + $this->usingServer($previousServer); + } + +} diff --git a/build/integration/features/bootstrap/Sharing.php b/build/integration/features/bootstrap/Sharing.php new file mode 100644 index 00000000000..39ec4397bab --- /dev/null +++ b/build/integration/features/bootstrap/Sharing.php @@ -0,0 +1,498 @@ +<?php + +use GuzzleHttp\Client; +use GuzzleHttp\Message\ResponseInterface; + +require __DIR__ . '/../../vendor/autoload.php'; + + + +trait Sharing { + use Provisioning; + + /** @var int */ + private $sharingApiVersion = 1; + + /** @var SimpleXMLElement */ + private $lastShareData = null; + + /** @var int */ + private $savedShareId = null; + + /** + * @Given /^as "([^"]*)" creating a share with$/ + * @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/v{$this->sharingApiVersion}/shares"; + $client = new Client(); + $options = []; + if ($user === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$user, $this->regularUser]; + } + + if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $fd = $body->getRowsHash(); + if (array_key_exists('expireDate', $fd)){ + $dateModification = $fd['expireDate']; + $fd['expireDate'] = date('Y-m-d', strtotime($dateModification)); + } + $options['body'] = $fd; + } + + try { + $this->response = $client->send($client->createRequest("POST", $fullUrl, $options)); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + } + + $this->lastShareData = $this->response->xml(); + } + + /** + * @When /^creating a share with$/ + * @param \Behat\Gherkin\Node\TableNode|null $body + */ + public function creatingShare($body) { + $this->asCreatingAShareWith($this->currentUser, $body); + } + + /** + * @Then /^Public shared file "([^"]*)" can be downloaded$/ + */ + public function checkPublicSharedFile($filename) { + $client = new Client(); + $options = []; + if (count($this->lastShareData->data->element) > 0){ + $url = $this->lastShareData->data[0]->url; + } + else{ + $url = $this->lastShareData->data->url; + } + $fullUrl = $url . "/download"; + $options['save_to'] = "./$filename"; + $this->response = $client->get($fullUrl, $options); + $finfo = new finfo; + $fileinfo = $finfo->file("./$filename", FILEINFO_MIME_TYPE); + PHPUnit_Framework_Assert::assertEquals($fileinfo, "text/plain"); + if (file_exists("./$filename")) { + unlink("./$filename"); + } + } + + /** + * @Then /^Public shared file "([^"]*)" with password "([^"]*)" can be downloaded$/ + */ + public function checkPublicSharedFileWithPassword($filename, $password) { + $client = new Client(); + $options = []; + if (count($this->lastShareData->data->element) > 0){ + $token = $this->lastShareData->data[0]->token; + } + else{ + $token = $this->lastShareData->data->token; + } + + $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav"; + $options['auth'] = [$token, $password]; + $options['save_to'] = "./$filename"; + $this->response = $client->get($fullUrl, $options); + $finfo = new finfo; + $fileinfo = $finfo->file("./$filename", FILEINFO_MIME_TYPE); + PHPUnit_Framework_Assert::assertEquals($fileinfo, "text/plain"); + if (file_exists("./$filename")) { + unlink("./$filename"); + } + } + + /** + * @When /^Adding expiration date to last share$/ + */ + public function addingExpirationDate() { + $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 = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$this->currentUser, $this->regularUser]; + } + $date = date('Y-m-d', strtotime("+3 days")); + $options['body'] = ['expireDate' => $date]; + $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options)); + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + /** + * @When /^Updating last share with$/ + * @param \Behat\Gherkin\Node\TableNode|null $body + */ + public function updatingLastShare($body) { + $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 = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$this->currentUser, $this->regularUser]; + } + + if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $fd = $body->getRowsHash(); + if (array_key_exists('expireDate', $fd)){ + $dateModification = $fd['expireDate']; + $fd['expireDate'] = date('Y-m-d', strtotime($dateModification)); + } + $options['body'] = $fd; + } + + try { + $this->response = $client->send($client->createRequest("PUT", $fullUrl, $options)); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + } + + PHPUnit_Framework_Assert::assertEquals(200, $this->response->getStatusCode()); + } + + public function createShare($user, + $path = null, + $shareType = null, + $shareWith = null, + $publicUpload = null, + $password = null, + $permissions = null){ + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares"; + $client = new Client(); + $options = []; + + if ($user === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$user, $this->regularUser]; + } + $fd = []; + if (!is_null($path)){ + $fd['path'] = $path; + } + if (!is_null($shareType)){ + $fd['shareType'] = $shareType; + } + if (!is_null($shareWith)){ + $fd['shareWith'] = $shareWith; + } + if (!is_null($publicUpload)){ + $fd['publicUpload'] = $publicUpload; + } + if (!is_null($password)){ + $fd['password'] = $password; + } + if (!is_null($permissions)){ + $fd['permissions'] = $permissions; + } + + $options['body'] = $fd; + + try { + $this->response = $client->send($client->createRequest("POST", $fullUrl, $options)); + $this->lastShareData = $this->response->xml(); + } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $ex->getResponse(); + } + } + + public function isFieldInResponse($field, $contentExpected){ + $data = $this->response->xml()->data[0]; + if ((string)$field == 'expiration'){ + $contentExpected = date('Y-m-d', strtotime($contentExpected)) . " 00:00:00"; + } + + if (count($data->element) > 0){ + foreach($data as $element) { + if ($contentExpected == "A_TOKEN"){ + return (strlen((string)$element->$field) == 15); + } + elseif ($contentExpected == "A_NUMBER"){ + return is_numeric((string)$element->$field); + } + elseif($contentExpected == "AN_URL"){ + return $this->isExpectedUrl((string)$element->$field, "index.php/s/"); + } + elseif ((string)$element->$field == $contentExpected){ + return True; + } + else{ + print($element->$field); + } + } + + return False; + } else { + if ($contentExpected == "A_TOKEN"){ + return (strlen((string)$data->$field) == 15); + } + elseif ($contentExpected == "A_NUMBER"){ + return is_numeric((string)$data->$field); + } + elseif($contentExpected == "AN_URL"){ + return $this->isExpectedUrl((string)$data->$field, "index.php/s/"); + } + elseif ($data->$field == $contentExpected){ + return True; + } + return False; + } + } + + /** + * @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")); + } + + /** + * @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")); + } + + /** + * @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")); + } + + /** + * @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){ + $data = $this->response->xml()->data[0]; + foreach($data as $element) { + if ($element->share_with == $userOrGroup){ + return True; + } + } + return False; + } + + /** + * @Given /^file "([^"]*)" of user "([^"]*)" is shared with user "([^"]*)"$/ + * + * @param string $filepath + * @param string $user1 + * @param string $user2 + */ + public function assureFileIsShared($filepath, $user1, $user2){ + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath"; + $client = new Client(); + $options = []; + if ($user1 === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$user1, $this->regularUser]; + } + $this->response = $client->get($fullUrl, $options); + if ($this->isUserOrGroupInSharedData($user2)){ + return; + } else { + $this->createShare($user1, $filepath, 0, $user2, null, null, null); + } + $this->response = $client->get($fullUrl, $options); + PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($user2)); + } + + /** + * @Given /^file "([^"]*)" of user "([^"]*)" is shared with group "([^"]*)"$/ + * + * @param string $filepath + * @param string $user + * @param string $group + */ + public function assureFileIsSharedWithGroup($filepath, $user, $group){ + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares" . "?path=$filepath"; + $client = new Client(); + $options = []; + if ($user === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$user, $this->regularUser]; + } + $this->response = $client->get($fullUrl, $options); + if ($this->isUserOrGroupInSharedData($group)){ + return; + } else { + $this->createShare($user, $filepath, 1, $group, null, null, null); + } + $this->response = $client->get($fullUrl, $options); + PHPUnit_Framework_Assert::assertEquals(True, $this->isUserOrGroupInSharedData($group)); + } + + /** + * @When /^Deleting last share$/ + */ + public function deletingLastShare(){ + $share_id = $this->lastShareData->data[0]->id; + $url = "/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id"; + $this->sendingToWith("DELETE", $url, null); + } + + /** + * @When /^Getting info of last share$/ + */ + public function gettingInfoOfLastShare(){ + $share_id = $this->lastShareData->data[0]->id; + $url = "/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/$share_id"; + $this->sendingToWith("GET", $url, null); + } + + /** + * @Then /^last share_id is included in the answer$/ + */ + public function checkingLastShareIDIsIncluded(){ + $share_id = $this->lastShareData->data[0]->id; + if (!$this->isFieldInResponse('id', $share_id)){ + PHPUnit_Framework_Assert::fail("Share id $share_id not found in response"); + } + } + + /** + * @Then /^last share_id is included in the answer as parent$/ + */ + public function checkingLastShareIDIsIncludedAsParent(){ + $share_id = $this->lastShareData->data[0]->id; + //This is a problem before 9.0, setupFS is called with every api call so a new share id is created + //we just need to check the parent id to match with the returned id. + if (!$this->isFieldInResponse('parent', $share_id)){ + PHPUnit_Framework_Assert::fail("Share id $share_id not found in response as parent"); + } + } + + /** + * @Then /^last share_id is not included in the answer$/ + */ + public function checkingLastShareIDIsNotIncluded(){ + $share_id = $this->lastShareData->data[0]->id; + if ($this->isFieldInResponse('id', $share_id)){ + PHPUnit_Framework_Assert::fail("Share id $share_id has been found in response"); + } + } + + /** + * @Then /^last share_id is not included in the answer as parent$/ + */ + public function checkingLastShareIDIsNotIncludedAsParent(){ + $share_id = $this->lastShareData->data[0]->id; + //This is a problem before 9.0, setupFS is called with every api call so a new share id is created + //we just need to check the parent id to match with the returned id. + if ($this->isFieldInResponse('parent', $share_id)){ + PHPUnit_Framework_Assert::fail("Share id $share_id has been found in response as parent"); + } + } + + /** + * @Then /^Share fields of last share match with$/ + * @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"); + } + } + } + } + + /** + * @Then As :user remove all shares from the file named :fileName + */ + public function asRemoveAllSharesFromTheFileNamed($user, $fileName) { + $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, + [ + 'auth' => [ + $user, + '123456', + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + $json = json_decode($res->getBody()->getContents(), true); + $deleted = false; + foreach($json['ocs']['data'] as $data) { + if (stripslashes($data['path']) === $fileName) { + $id = $data['id']; + $client->delete( + $this->baseUrl . "v{$this->apiVersion}.php/apps/files_sharing/api/v{$this->sharingApiVersion}/shares/{$id}", + [ + 'auth' => [ + $user, + '123456', + ], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + ] + ); + $deleted = true; + } + } + + if($deleted === false) { + throw new \Exception("Could not delete file $fileName"); + } + } + + /** + * @When save last share id + */ + public function saveLastShareId() + { + $this->savedShareId = $this->lastShareData['data']['id']; + } + + /** + * @Then share ids should match + */ + public function shareIdsShouldMatch() + { + if ($this->savedShareId !== $this->lastShareData['data']['id']) { + throw new \Exception('Expected the same link share to be returned'); + } + } +} + diff --git a/build/integration/features/bootstrap/WebDav.php b/build/integration/features/bootstrap/WebDav.php new file mode 100644 index 00000000000..3df37db72bb --- /dev/null +++ b/build/integration/features/bootstrap/WebDav.php @@ -0,0 +1,508 @@ +<?php + +use GuzzleHttp\Client as GClient; +use GuzzleHttp\Message\ResponseInterface; +use Sabre\DAV\Client as SClient; + +require __DIR__ . '/../../vendor/autoload.php'; + + +trait WebDav { + use Sharing; + + /** @var string*/ + private $davPath = "remote.php/webdav"; + /** @var ResponseInterface */ + private $response; + + /** + * @Given /^using dav path "([^"]*)"$/ + */ + public function usingDavPath($davPath) { + $this->davPath = $davPath; + } + + public function makeDavRequest($user, $method, $path, $headers, $body = null){ + $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath . "$path"; + $client = new GClient(); + $options = []; + if ($user === 'admin') { + $options['auth'] = $this->adminUser; + } else { + $options['auth'] = [$user, $this->regularUser]; + } + $request = $client->createRequest($method, $fullUrl, $options); + if (!is_null($headers)){ + foreach ($headers as $key => $value) { + $request->addHeader($key, $value); + } + } + + if (!is_null($body)) { + $request->setBody($body); + } + + return $client->send($request); + } + + /** + * @Given /^User "([^"]*)" moved file "([^"]*)" to "([^"]*)"$/ + * @param string $user + * @param string $fileSource + * @param string $fileDestination + */ + public function userMovedFile($user, $fileSource, $fileDestination){ + $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; + $headers['Destination'] = $fullUrl . $fileDestination; + $this->response = $this->makeDavRequest($user, "MOVE", $fileSource, $headers); + PHPUnit_Framework_Assert::assertEquals(201, $this->response->getStatusCode()); + } + + /** + * @When /^User "([^"]*)" moves file "([^"]*)" to "([^"]*)"$/ + * @param string $user + * @param string $fileSource + * @param string $fileDestination + */ + public function userMovesFile($user, $fileSource, $fileDestination){ + $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; + $headers['Destination'] = $fullUrl . $fileDestination; + try { + $this->response = $this->makeDavRequest($user, "MOVE", $fileSource, $headers); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @When /^User "([^"]*)" copies file "([^"]*)" to "([^"]*)"$/ + * @param string $user + * @param string $fileSource + * @param string $fileDestination + */ + public function userCopiesFile($user, $fileSource, $fileDestination){ + $fullUrl = substr($this->baseUrl, 0, -4) . $this->davPath; + $headers['Destination'] = $fullUrl . $fileDestination; + try { + $this->response = $this->makeDavRequest($user, "COPY", $fileSource, $headers); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @When /^Downloading file "([^"]*)" with range "([^"]*)"$/ + * @param string $fileSource + * @param string $range + */ + public function downloadFileWithRange($fileSource, $range){ + $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; + $fullUrl = substr($this->baseUrl, 0, -4) . "public.php/webdav"; + $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); + } + + /** + * @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()); + } + + /** + * @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); + $this->downloadedContentShouldBe($content); + } + + /** + * @When Downloading file :fileName + * @param string $fileName + */ + public function downloadingFile($fileName) { + try { + $this->response = $this->makeDavRequest($this->currentUser, 'GET', $fileName, []); + } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @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) { + $headerName = $header[0]; + $expectedHeaderValue = $header[1]; + $returnedHeader = $this->response->getHeader($headerName); + if($returnedHeader !== $expectedHeaderValue) { + throw new \Exception( + sprintf( + "Expected value '%s' for header '%s', got '%s'", + $expectedHeaderValue, + $headerName, + $returnedHeader + ) + ); + } + } + } + + /** + * @Then Downloaded content should start with :start + * @param int $start + * @throws \Exception + */ + public function downloadedContentShouldStartWith($start) { + if(strpos($this->response->getBody()->getContents(), $start) !== 0) { + throw new \Exception( + sprintf( + "Expected '%s', got '%s'", + $start, + $this->response->getBody()->getContents() + ) + ); + } + } + + /** + * @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) { + $properties = null; + if ($propertiesTable instanceof \Behat\Gherkin\Node\TableNode) { + foreach ($propertiesTable->getRows() as $row) { + $properties[] = $row[0]; + } + } + $this->response = $this->listFolder($user, $path, 0, $properties); + } + + /** + * @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; + if (!array_key_exists($key, $keys)) { + throw new \Exception("Cannot find property \"$key\" with \"$expectedValue\""); + } + + $value = $keys[$key]; + if ($value != $expectedValue) { + throw new \Exception("Property \"$key\" found with value \"$value\", expected \"$expectedValue\""); + } + } + + /** + * @Then the response should contain a share-types property with + */ + public function theResponseShouldContainAShareTypesPropertyWith($table) + { + $keys = $this->response; + if (!array_key_exists('{http://owncloud.org/ns}share-types', $keys)) { + throw new \Exception("Cannot find property \"{http://owncloud.org/ns}share-types\""); + } + + $foundTypes = []; + $data = $keys['{http://owncloud.org/ns}share-types']; + foreach ($data as $item) { + if ($item['name'] !== '{http://owncloud.org/ns}share-type') { + throw new \Exception('Invalid property found: "' . $item['name'] . '"'); + } + + $foundTypes[] = $item['value']; + } + + foreach ($table->getRows() as $row) { + $key = array_search($row[0], $foundTypes); + if ($key === false) { + throw new \Exception('Expected type ' . $row[0] . ' not found'); + } + + unset($foundTypes[$key]); + } + + if ($foundTypes !== []) { + throw new \Exception('Found more share types then specified: ' . $foundTypes); + } + } + + /** + * @Then the response should contain an empty property :property + * @param string $property + * @throws \Exception + */ + public function theResponseShouldContainAnEmptyProperty($property) { + $properties = $this->response; + if (!array_key_exists($property, $properties)) { + throw new \Exception("Cannot find property \"$property\""); + } + + if ($properties[$property] !== null) { + throw new \Exception("Property \"$property\" is not empty"); + } + } + + + /*Returns the elements of a propfind, $folderDepth requires 1 to see elements without children*/ + public function listFolder($user, $path, $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 = [ + '{DAV:}getetag' + ]; + } + + $response = $client->propfind($this->davPath . '/' . ltrim($path, '/'), $properties, $folderDepth); + + return $response; + } + + /** + * @Then /^user "([^"]*)" should see following elements$/ + * @param string $user + * @param \Behat\Gherkin\Node\TableNode|null $expectedElements + */ + public function checkElementList($user, $expectedElements){ + $elementList = $this->listFolder($user, '/', 3); + if ($expectedElements instanceof \Behat\Gherkin\Node\TableNode) { + $elementRows = $expectedElements->getRows(); + $elementsSimplified = $this->simplifyArray($elementRows); + foreach($elementsSimplified as $expectedElement) { + $webdavPath = "/" . $this->davPath . $expectedElement; + if (!array_key_exists($webdavPath,$elementList)){ + PHPUnit_Framework_Assert::fail("$webdavPath" . " is not in propfind answer"); + } + } + } + } + + /** + * @When User :user uploads file :source to :destination + * @param string $user + * @param string $source + * @param string $destination + */ + public function userUploadsAFileTo($user, $source, $destination) + { + $file = \GuzzleHttp\Stream\Stream::factory(fopen($source, 'r')); + 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 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 { + $this->response = $this->makeDavRequest($user, 'DELETE', $file, []); + } catch (\GuzzleHttp\Exception\ServerException $e) { + // 4xx and 5xx responses cause an exception + $this->response = $e->getResponse(); + } + } + + /** + * @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, []); + } catch (\GuzzleHttp\Exception\ServerException $e) { + // 4xx and 5xx responses cause an exception + $this->response = $e->getResponse(); + } + } + + /** + * @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) + { + $num -= 1; + $data = \GuzzleHttp\Stream\Stream::factory($data); + $file = $destination . '-chunking-42-'.$total.'-'.$num; + $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(); + } + } + + /** + * @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/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 d865ee687ed..5357769cdbe 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -1,106 +1,296 @@ Feature: provisioning - Background: - Given using api version "1" - - Scenario: Getting an not existing user - Given As an "admin" - When sending "GET" to "/cloud/users/test" - Then the OCS status code should be "998" - And the HTTP status code should be "200" - - Scenario: Listing all users - Given As an "admin" - When sending "GET" to "/cloud/users" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - - Scenario: Create a user - Given As an "admin" - And user "brand-new-user" does not exist - When sending "POST" to "/cloud/users" with - | userid | brand-new-user | - | password | 123456 | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And user "brand-new-user" exists - - Scenario: Create an existing user - Given As an "admin" - And user "brand-new-user" exists - When sending "POST" to "/cloud/users" with - | userid | brand-new-user | - | password | 123456 | - Then the OCS status code should be "102" - And the HTTP status code should be "200" - - Scenario: Get an existing user - Given As an "admin" - When sending "GET" to "/cloud/users/brand-new-user" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - - - Scenario: Getting all users - Given As an "admin" - And user "brand-new-user" exists - And user "admin" exists - When sending "GET" to "/cloud/users" - And users returned are - | brand-new-user | - | admin | - - - Scenario: Edit a user - Given As an "admin" - And user "brand-new-user" exists - When sending "PUT" to "/cloud/users/brand-new-user" with - | key | quota | - | value | 12MB | - | key | email | - | value | brand-new-user@gmail.com | - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And user "brand-new-user" exists - - - Scenario: Delete a user - Given As an "admin" - And user "brand-new-user" exists - When sending "DELETE" to "/cloud/users/brand-new-user" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And user "brand-new-user" does not exist - - - Scenario: Create a group - Given As an "admin" - And group "new-group" does not exist - When sending "POST" to "/cloud/groups" with - | groupid | new-group | - | password | 123456 | - - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And group "new-group" exists - - - Scenario: Getting all groups - Given As an "admin" - And group "new-group" exists - And group "admin" exists - When sending "GET" to "/cloud/groups" - And groups returned are - | admin | - | new-group | - - - Scenario: Delete a group - Given As an "admin" - And group "new-group" exists - When sending "DELETE" to "/cloud/groups/new-group" - Then the OCS status code should be "100" - And the HTTP status code should be "200" - And group "new-group" does not exist + Background: + Given using api version "1" + Scenario: Getting an not existing user + Given As an "admin" + When sending "GET" to "/cloud/users/test" + Then the OCS status code should be "998" + And the HTTP status code should be "200" + Scenario: Listing all users + Given As an "admin" + When sending "GET" to "/cloud/users" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Create a user + Given As an "admin" + And user "brand-new-user" does not exist + When sending "POST" to "/cloud/users" with + | userid | brand-new-user | + | password | 123456 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And user "brand-new-user" exists + + Scenario: Create an existing user + Given As an "admin" + And user "brand-new-user" exists + When sending "POST" to "/cloud/users" with + | userid | brand-new-user | + | password | 123456 | + Then the OCS status code should be "102" + And the HTTP status code should be "200" + + Scenario: Get an existing user + Given As an "admin" + When sending "GET" to "/cloud/users/brand-new-user" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Getting all users + Given As an "admin" + And user "brand-new-user" exists + And user "admin" exists + When sending "GET" to "/cloud/users" + Then users returned are + | brand-new-user | + | admin | + + Scenario: Edit a user + Given As an "admin" + And user "brand-new-user" exists + When sending "PUT" to "/cloud/users/brand-new-user" with + | key | quota | + | value | 12MB | + | key | email | + | value | brand-new-user@gmail.com | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And user "brand-new-user" exists + + Scenario: Create a group + Given As an "admin" + And group "new-group" does not exist + When sending "POST" to "/cloud/groups" with + | groupid | new-group | + | password | 123456 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And group "new-group" exists + + Scenario: adding user to a group without sending the group + Given As an "admin" + And user "brand-new-user" exists + When sending "POST" to "/cloud/users/brand-new-user/groups" with + | groupid | | + Then the OCS status code should be "101" + And the HTTP status code should be "200" + + Scenario: adding user to a group which doesn't exist + Given As an "admin" + And user "brand-new-user" exists + And group "not-group" does not exist + When sending "POST" to "/cloud/users/brand-new-user/groups" with + | groupid | not-group | + Then the OCS status code should be "102" + And the HTTP status code should be "200" + + Scenario: adding user to a group without privileges + Given As an "brand-new-user" + When sending "POST" to "/cloud/users/brand-new-user/groups" with + | groupid | new-group | + Then the OCS status code should be "997" + And the HTTP status code should be "401" + + Scenario: adding user to a group + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + When sending "POST" to "/cloud/users/brand-new-user/groups" with + | groupid | new-group | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: getting groups of an user + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + When sending "GET" to "/cloud/users/brand-new-user/groups" + Then groups returned are + | new-group | + And the OCS status code should be "100" + + Scenario: adding a user which doesn't exist to a group + Given As an "admin" + And user "not-user" does not exist + And group "new-group" exists + When sending "POST" to "/cloud/users/not-user/groups" with + | groupid | new-group | + Then the OCS status code should be "103" + And the HTTP status code should be "200" + + Scenario: getting a group + Given As an "admin" + And group "new-group" exists + When sending "GET" to "/cloud/groups/new-group" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Getting all groups + Given As an "admin" + And group "new-group" exists + And group "admin" exists + When sending "GET" to "/cloud/groups" + Then groups returned are + | admin | + | new-group | + + Scenario: create a subadmin + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + When sending "POST" to "/cloud/users/brand-new-user/subadmins" with + | groupid | new-group | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: removing a user from a group which doesn't exists + Given As an "admin" + And user "brand-new-user" exists + And group "not-group" does not exist + When sending "DELETE" to "/cloud/users/brand-new-user/groups" with + | groupid | not-group | + Then the OCS status code should be "102" + + Scenario: removing a user from a group + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + And user "brand-new-user" belongs to group "new-group" + When sending "DELETE" to "/cloud/users/brand-new-user/groups" with + | groupid | new-group | + Then the OCS status code should be "100" + And user "brand-new-user" does not belong to group "new-group" + + Scenario: create a subadmin using a user which not exist + Given As an "admin" + And user "not-user" does not exist + And group "new-group" exists + When sending "POST" to "/cloud/users/not-user/subadmins" with + | groupid | new-group | + Then the OCS status code should be "101" + And the HTTP status code should be "200" + + Scenario: create a subadmin using a group which not exist + Given As an "admin" + And user "brand-new-user" exists + And group "not-group" does not exist + When sending "POST" to "/cloud/users/brand-new-user/subadmins" with + | groupid | not-group | + Then the OCS status code should be "102" + And the HTTP status code should be "200" + + Scenario: Getting subadmin groups + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + When sending "GET" to "/cloud/users/brand-new-user/subadmins" + Then subadmin groups returned are + | new-group | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Getting subadmin groups of a user which not exist + Given As an "admin" + And user "not-user" does not exist + And group "new-group" exists + When sending "GET" to "/cloud/users/not-user/subadmins" + Then the OCS status code should be "101" + And the HTTP status code should be "200" + + Scenario: Getting subadmin users of a group + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + When sending "GET" to "/cloud/groups/new-group/subadmins" + Then subadmin users returned are + | brand-new-user | + And the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Getting subadmin users of a group which doesn't exist + Given As an "admin" + And user "brand-new-user" exists + And group "not-group" does not exist + When sending "GET" to "/cloud/groups/not-group/subadmins" + Then the OCS status code should be "101" + And the HTTP status code should be "200" + + Scenario: Removing subadmin from a group + Given As an "admin" + And user "brand-new-user" exists + And group "new-group" exists + And user "brand-new-user" is subadmin of group "new-group" + When sending "DELETE" to "/cloud/users/brand-new-user/subadmins" with + | groupid | new-group | + And the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Delete a user + Given As an "admin" + And user "brand-new-user" exists + When sending "DELETE" to "/cloud/users/brand-new-user" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And user "brand-new-user" does not exist + + Scenario: Delete a group + Given As an "admin" + And group "new-group" exists + When sending "DELETE" to "/cloud/groups/new-group" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And group "new-group" does not exist + + Scenario: get enabled apps + Given As an "admin" + When sending "GET" to "/cloud/apps?filter=enabled" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And apps returned are + | files | + | files_sharing | + | files_trashbin | + | files_versions | + | provisioning_api | + + Scenario: get app info + Given As an "admin" + When sending "GET" to "/cloud/apps/files" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: enable an app + Given As an "admin" + And app "files_external" is disabled + When sending "POST" to "/cloud/apps/files_external" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And app "files_external" is enabled + + Scenario: disable an app + Given As an "admin" + And app "files_external" is enabled + When sending "DELETE" to "/cloud/apps/files_external" + 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/sharing-v1.feature b/build/integration/features/sharing-v1.feature new file mode 100644 index 00000000000..cb1c55fe267 --- /dev/null +++ b/build/integration/features/sharing-v1.feature @@ -0,0 +1,569 @@ +Feature: sharing + Background: + Given using api version "1" + Given using dav path "remote.php/webdav" + + Scenario: Creating a new share with user + Given user "user0" exists + And user "user1" exists + And As an "user0" + When sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | user1 | + | shareType | 0 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Creating a share with a group + Given user "user0" exists + And user "user1" exists + And group "sharing-group" exists + And As an "user0" + When sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | sharing-group | + | shareType | 1 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Creating a new share with user who already received a share through their group + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "sharing-group" exists + And user "user1" belongs to group "sharing-group" + And file "welcome.txt" of user "user0" is shared with group "sharing-group" + And As an "user0" + Then sending "POST" to "/apps/files_sharing/api/v1/shares" with + | path | welcome.txt | + | shareWith | user1 | + | shareType | 0 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Creating a new public share + Given user "user0" exists + And As an "user0" + When creating a share with + | path | welcome.txt | + | shareType | 3 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Public shared file "welcome.txt" can be downloaded + + Scenario: Creating a new public share with password + Given user "user0" exists + And As an "user0" + When creating a share with + | path | welcome.txt | + | shareType | 3 | + | password | publicpw | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Public shared file "welcome.txt" with password "publicpw" can be downloaded + + Scenario: Creating a new public share of a folder + Given user "user0" exists + And As an "user0" + When creating a share with + | path | FOLDER | + | shareType | 3 | + | password | publicpw | + | expireDate | +3 days | + | publicUpload | true | + | permissions | 7 | + 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 | + | permissions | 7 | + | expiration | +3 days | + | url | AN_URL | + | token | A_TOKEN | + + Scenario: Creating a new public share with password and adding an expiration date + Given user "user0" exists + And As an "user0" + When creating a share with + | path | welcome.txt | + | shareType | 3 | + | password | publicpw | + And Updating last share with + | expireDate | +3 days | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And Public shared file "welcome.txt" with password "publicpw" can be downloaded + + Scenario: Creating a new public share, updating its expiration date and getting its info + Given user "user0" exists + And As an "user0" + When creating a share with + | path | FOLDER | + | shareType | 3 | + And Updating last share with + | expireDate | +3 days | + And Getting info of last share + 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 | folder | + | item_source | A_NUMBER | + | share_type | 3 | + | file_source | A_NUMBER | + | file_target | /FOLDER | + | permissions | 1 | + | stime | A_NUMBER | + | expiration | +3 days | + | token | A_TOKEN | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | url | AN_URL | + + Scenario: Creating a new public share, updating its password and getting its info + Given user "user0" exists + And As an "user0" + When creating a share with + | path | FOLDER | + | shareType | 3 | + And Updating last share with + | password | publicpw | + And Getting info of last share + 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 | folder | + | item_source | A_NUMBER | + | share_type | 3 | + | file_source | A_NUMBER | + | file_target | /FOLDER | + | permissions | 1 | + | stime | A_NUMBER | + | token | A_TOKEN | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | url | AN_URL | + + Scenario: Creating a new public share, updating its permissions and getting its info + Given user "user0" exists + And As an "user0" + When creating a share with + | path | FOLDER | + | shareType | 3 | + And Updating last share with + | permissions | 7 | + And Getting info of last share + 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 | folder | + | item_source | A_NUMBER | + | share_type | 3 | + | file_source | A_NUMBER | + | file_target | /FOLDER | + | permissions | 7 | + | stime | A_NUMBER | + | token | A_TOKEN | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | url | AN_URL | + + Scenario: Creating a new public share, updating publicUpload option and getting its info + Given user "user0" exists + And As an "user0" + When creating a share with + | path | FOLDER | + | shareType | 3 | + And Updating last share with + | publicUpload | true | + And Getting info of last share + 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 | folder | + | item_source | A_NUMBER | + | share_type | 3 | + | file_source | A_NUMBER | + | file_target | /FOLDER | + | permissions | 7 | + | stime | A_NUMBER | + | token | A_TOKEN | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + | url | AN_URL | + + Scenario: getting all shares of a user using that user + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user0" + When sending "GET" to "/apps/files_sharing/api/v1/shares" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And File "textfile0 (2).txt" should be included in the response + + Scenario: getting all shares of a user using another user + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "admin" + When sending "GET" to "/apps/files_sharing/api/v1/shares" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And File "textfile0.txt" should not be included in the response + + Scenario: getting all shares of a file + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user3" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile0.txt" of user "user0" is shared with user "user2" + And As an "user0" + When sending "GET" to "/apps/files_sharing/api/v1/shares?path=textfile0.txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And User "user1" should be included in the response + And User "user2" should be included in the response + And User "user3" should not be included in the response + + Scenario: getting all shares of a file with reshares + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user3" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile0 (2).txt" of user "user1" is shared with user "user2" + And As an "user0" + When sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0.txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And User "user1" should be included in the response + And User "user2" should be included in the response + And User "user3" should not be included in the response + + Scenario: getting share info of a share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user0" + When Getting info of last share + 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 | + | share_with | user1 | + | file_source | A_NUMBER | + | file_target | /textfile0 (2).txt | + | path | /textfile0.txt | + | permissions | 23 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | share_with_displayname | user1 | + | displayname_owner | user0 | + + Scenario: keep group permissions in sync + Given As an "admin" + Given user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And file "textfile0.txt" of user "user0" is shared with group "group1" + And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" + And As an "user0" + When Updating last share with + | permissions | 1 | + And Getting info of last share + 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 | 1 | + | file_source | A_NUMBER | + | file_target | /textfile0.txt | + | permissions | 1 | + | stime | A_NUMBER | + | storage | A_NUMBER | + | mail_send | 0 | + | uid_owner | user0 | + | storage_id | home::user0 | + | file_parent | A_NUMBER | + | displayname_owner | user0 | + + Scenario: Sharee can see the share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer + + Scenario: Sharee can see the filtered share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile1.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile1 (2).txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer + + Scenario: Sharee can't see the share that is filtered out + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And file "textfile1.txt" of user "user0" is shared with user "user1" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile0 (2).txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is not included in the answer as parent + + Scenario: Sharee can see the group share + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And file "textfile0.txt" of user "user0" is shared with group "group0" + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is included in the answer as parent + + Scenario: User is not allowed to reshare file + As an "admin" + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And As an "user0" + And creating a share with + | path | /textfile0.txt | + | shareType | 0 | + | shareWith | user1 | + | permissions | 8 | + And As an "user1" + When creating a share with + | path | /textfile0 (2).txt | + | shareType | 0 | + | shareWith | user2 | + | permissions | 31 | + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: User is not allowed to reshare file with more permissions + As an "admin" + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And As an "user0" + And creating a share with + | path | /textfile0.txt | + | shareType | 0 | + | shareWith | user1 | + | permissions | 16 | + And As an "user1" + When creating a share with + | path | /textfile0 (2).txt | + | shareType | 0 | + | shareWith | user2 | + | permissions | 31 | + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: Get a share with a user which didn't received the share + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user2" + When Getting info of last share + Then the OCS status code should be "404" + And the HTTP status code should be "200" + + Scenario: Share of folder and sub-folder to same user - core#20645 + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And file "/PARENT" of user "user0" is shared with user "user1" + When file "/PARENT/CHILD" of user "user0" is shared with group "group0" + Then user "user1" should see following elements + | /FOLDER/ | + | /PARENT/ | + | /CHILD/ | + | /PARENT/parent.txt | + | /CHILD/child.txt | + And the HTTP status code should be "200" + + Scenario: Share a file by multiple channels + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And user "user2" belongs to group "group0" + And user "user0" created a folder "/common" + And user "user0" created a folder "/common/sub" + And file "common" of user "user0" is shared with group "group0" + And file "textfile0.txt" of user "user1" is shared with user "user2" + And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" + And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" + And As an "user2" + When Downloading file "/common/sub/textfile0.txt" with range "bytes=9-17" + Then Downloaded content should be "test text" + And Downloaded content when downloading file "/textfile0.txt" with range "bytes=9-17" should be "test text" + And user "user2" should see following elements + | /common/sub/textfile0.txt | + + Scenario: Share a file by multiple channels + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group0" exists + And user "user1" belongs to group "group0" + And user "user2" belongs to group "group0" + And user "user0" created a folder "/common" + And user "user0" created a folder "/common/sub" + And file "common" of user "user0" is shared with group "group0" + And file "textfile0.txt" of user "user1" is shared with user "user2" + And User "user1" moved file "/textfile0.txt" to "/common/textfile0.txt" + And User "user1" moved file "/common/textfile0.txt" to "/common/sub/textfile0.txt" + And As an "user2" + When Downloading file "/textfile0.txt" with range "bytes=9-17" + Then Downloaded content should be "test text" + And user "user2" should see following elements + | /common/sub/textfile0.txt | + + Scenario: Delete all group shares + Given As an "admin" + And user "user0" exists + And user "user1" exists + And group "group1" exists + And user "user1" belongs to group "group1" + And file "textfile0.txt" of user "user0" is shared with group "group1" + And User "user1" moved file "/textfile0.txt" to "/FOLDER/textfile0.txt" + And As an "user0" + And Deleting last share + And As an "user1" + When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last share_id is not included in the answer + + Scenario: delete a share + Given user "user0" exists + And user "user1" exists + And file "textfile0.txt" of user "user0" is shared with user "user1" + And As an "user0" + When Deleting last share + Then the OCS status code should be "100" + And the HTTP status code should be "200" + + Scenario: Keep usergroup shares (#22143) + Given As an "admin" + And user "user0" exists + And user "user1" exists + And user "user2" exists + And group "group" exists + And user "user1" belongs to group "group" + And user "user2" belongs to group "group" + And user "user0" created a folder "/TMP" + And file "TMP" of user "user0" is shared with group "group" + And user "user1" created a folder "/myFOLDER" + And User "user1" moves file "/TMP" to "/myFOLDER/myTMP" + And user "user2" does not exist + And user "user1" should see following elements + | /myFOLDER/myTMP/ | + + Scenario: Allow modification of reshare + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/TMP" + And file "TMP" of user "user0" is shared with user "user1" + And file "TMP" of user "user1" is shared with user "user2" + And As an "user1" + When Updating last share with + | permissions | 1 | + Then the OCS status code should be "100" + + Scenario: Do not allow reshare to exceed permissions + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/TMP" + And As an "user0" + And creating a share with + | path | /TMP | + | shareType | 0 | + | shareWith | user1 | + | permissions | 21 | + And As an "user1" + And creating a share with + | path | /TMP | + | shareType | 0 | + | shareWith | user2 | + | permissions | 21 | + When Updating last share with + | permissions | 31 | + Then the OCS status code should be "404" + + Scenario: Only allow 1 link share per file/folder + Given user "user0" exists + And As an "user0" + And creating a share with + | path | welcome.txt | + | shareType | 3 | + When save last share id + And creating a share with + | path | welcome.txt | + | shareType | 3 | + Then share ids should match + + Scenario: unique target names for incoming shares + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/foo" + And user "user1" created a folder "/foo" + When file "/foo" of user "user0" is shared with user "user2" + And file "/foo" of user "user1" is shared with user "user2" + Then user "user2" should see following elements + | /foo/ | + | /foo%20(2)/ | diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature new file mode 100644 index 00000000000..a0d469f4805 --- /dev/null +++ b/build/integration/features/webdav-related.feature @@ -0,0 +1,203 @@ +Feature: webdav-related + Background: + Given using api version "1" + + Scenario: Moving a file + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And As an "user0" + When User "user0" moves file "/welcome.txt" to "/FOLDER/welcome.txt" + Then the HTTP status code should be "201" + And Downloaded content when downloading file "/FOLDER/welcome.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: Moving and overwriting a file old way + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And As an "user0" + When User "user0" moves file "/welcome.txt" to "/textfile0.txt" + Then the HTTP status code should be "204" + And Downloaded content when downloading file "/textfile0.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: Moving a file to a folder with no permissions + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user1" exists + And As an "user1" + And user "user1" created a folder "/testshare" + And as "user1" creating a share with + | path | testshare | + | shareType | 0 | + | permissions | 1 | + | shareWith | user0 | + And As an "user0" + And User "user0" moves file "/textfile0.txt" to "/testshare/textfile0.txt" + And the HTTP status code should be "403" + When Downloading file "/testshare/textfile0.txt" + Then the HTTP status code should be "404" + + Scenario: Moving a file to overwrite a file in a folder with no permissions + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user1" exists + And As an "user1" + And user "user1" created a folder "/testshare" + And as "user1" creating a share with + | path | testshare | + | shareType | 0 | + | permissions | 1 | + | shareWith | user0 | + And User "user1" copies file "/welcome.txt" to "/testshare/overwritethis.txt" + And As an "user0" + When User "user0" moves file "/textfile0.txt" to "/testshare/overwritethis.txt" + Then the HTTP status code should be "403" + And Downloaded content when downloading file "/testshare/overwritethis.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: Copying a file + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And As an "user0" + When User "user0" copies file "/welcome.txt" to "/FOLDER/welcome.txt" + Then the HTTP status code should be "201" + And Downloaded content when downloading file "/FOLDER/welcome.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: Copying and overwriting a file + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And As an "user0" + When User "user0" copies file "/welcome.txt" to "/textfile1.txt" + Then the HTTP status code should be "204" + And Downloaded content when downloading file "/textfile1.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: Copying a file to a folder with no permissions + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user1" exists + And As an "user1" + And user "user1" created a folder "/testshare" + And as "user1" creating a share with + | path | testshare | + | shareType | 0 | + | permissions | 1 | + | shareWith | user0 | + And As an "user0" + When User "user0" copies file "/textfile0.txt" to "/testshare/textfile0.txt" + Then the HTTP status code should be "403" + And Downloading file "/testshare/textfile0.txt" + And the HTTP status code should be "404" + + Scenario: Copying a file to overwrite a file into a folder with no permissions + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user1" exists + And As an "user1" + And user "user1" created a folder "/testshare" + And as "user1" creating a share with + | path | testshare | + | shareType | 0 | + | permissions | 1 | + | shareWith | user0 | + And User "user1" copies file "/welcome.txt" to "/testshare/overwritethis.txt" + And As an "user0" + When User "user0" copies file "/textfile0.txt" to "/testshare/overwritethis.txt" + Then the HTTP status code should be "403" + And Downloaded content when downloading file "/testshare/overwritethis.txt" with range "bytes=0-6" should be "Welcome" + + Scenario: download a file with range + Given using dav path "remote.php/webdav" + And As an "admin" + When Downloading file "/welcome.txt" with range "bytes=51-77" + Then Downloaded content should be "example file for developers" + + Scenario: Upload forbidden if quota is 0 + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user0" has a quota of "0" + When User "user0" uploads file "data/textfile.txt" to "/asdf.txt" + Then the HTTP status code should be "507" + + Scenario: Retrieving folder quota when quota is set + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + When user "user0" has a quota of "10 MB" + Then as "user0" gets properties of folder "/" with + |{DAV:}quota-available-bytes| + And the single response should contain a property "{DAV:}quota-available-bytes" with value "10485429" + + Scenario: Retrieving folder quota of shared folder with quota when no quota is set for recipient + Given using dav path "remote.php/webdav" + And As an "admin" + And user "user0" exists + And user "user1" exists + And user "user0" has unlimited quota + And user "user1" has a quota of "10 MB" + And As an "user1" + And user "user1" created a folder "/testquota" + And as "user1" creating a share with + | path | testquota | + | shareType | 0 | + | permissions | 31 | + | shareWith | user0 | + Then as "user0" gets properties of folder "/testquota" with + |{DAV:}quota-available-bytes| + And the single response should contain a property "{DAV:}quota-available-bytes" with value "10485429" + + Scenario: download a public shared file with range + Given user "user0" exists + And As an "user0" + When creating a share with + | path | welcome.txt | + | shareType | 3 | + 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: 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 + Then Downloaded content should start with "Welcome to your ownCloud account!" + And the HTTP status code should be "200" + + Scenario: Upload chunked file asc + Given user "user0" exists + And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt" + When As an "user0" + And Downloading file "/myChunkedFile.txt" + Then Downloaded content should be "AAAAABBBBBCCCCC" + + Scenario: Upload chunked file desc + Given user "user0" exists + And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt" + When As an "user0" + And Downloading file "/myChunkedFile.txt" + Then Downloaded content should be "AAAAABBBBBCCCCC" + + Scenario: Upload chunked file random + Given user "user0" exists + And user "user0" uploads chunk file "2" of "3" with "BBBBB" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "3" of "3" with "CCCCC" to "/myChunkedFile.txt" + And user "user0" uploads chunk file "1" of "3" with "AAAAA" to "/myChunkedFile.txt" + When As an "user0" + And Downloading file "/myChunkedFile.txt" + Then Downloaded content should be "AAAAABBBBBCCCCC" diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature new file mode 100644 index 00000000000..e822de94c85 --- /dev/null +++ b/build/integration/federation_features/federated.feature @@ -0,0 +1,156 @@ +Feature: federated + Background: + Given using api version "1" + + Scenario: Federate share a file with another server + Given Using server "REMOTE" + And user "user1" exists + And Using server "LOCAL" + And user "user0" exists + 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 | + | permissions | 23 | + + 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 | + | permissions | 23 | + + + 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 | + | permissions | 19 | + + 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=0-11" + Then Downloaded content should be "AAAAABBBBBCC" + + + + + + + + + + + + + diff --git a/build/integration/run.sh b/build/integration/run.sh index 08f10b86c5f..5a222bda3e3 100755 --- a/build/integration/run.sh +++ b/build/integration/run.sh @@ -2,18 +2,37 @@ composer install -# TODO: avoid port collision on jenkins - use $EXECUTOR_NUMBER +SCENARIO_TO_RUN=$1 +HIDE_OC_LOGS=$2 + +# avoid port collision on jenkins - use $EXECUTOR_NUMBER if [ -z "$EXECUTOR_NUMBER" ]; then EXECUTOR_NUMBER=0 fi PORT=$((8080 + $EXECUTOR_NUMBER)) -#PORT=8080 echo $PORT php -S localhost:$PORT -t ../.. & PHPPID=$! echo $PHPPID +PORT_FED=$((8180 + $EXECUTOR_NUMBER)) +echo $PORT_FED +php -S localhost:$PORT_FED -t ../.. & +PHPPID_FED=$! +echo $PHPPID_FED + export TEST_SERVER_URL="http://localhost:$PORT/ocs/" -vendor/bin/behat --profile ci +export TEST_SERVER_FED_URL="http://localhost:$PORT_FED/ocs/" + +vendor/bin/behat -f junit -f pretty $SCENARIO_TO_RUN +RESULT=$? kill $PHPPID +kill $PHPPID_FED + +if [ -z $HIDE_OC_LOGS ]; then + tail "../../data/owncloud.log" +fi + +exit $RESULT + |