Merge pull request #6651 from nextcloud/remote-cloud-client

Add api clients for talking to remote clouds
This commit is contained in:
Roeland Jago Douma 2017-12-11 15:28:39 +01:00 committed by GitHub
commit e8acf448eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1706 additions and 1 deletions

View File

@ -599,6 +599,15 @@ pipeline:
when:
matrix:
TESTS: integration-trashbin
integration-remote-api:
image: nextcloudci/integration-php7.0:integration-php7.0-6
commands:
- ./occ maintenance:install --admin-pass=admin --data-dir=/dev/shm/nc_int
- cd build/integration
- ./run.sh remoteapi_features/remote.feature
when:
matrix:
TESTS: integration-remote-api
acceptance-access-levels:
image: nextcloudci/integration-php7.0:integration-php7.0-6
commands:
@ -801,6 +810,7 @@ matrix:
- TESTS: integration-transfer-ownership-features
- TESTS: integration-ldap-features
- TESTS: integration-trashbin
- TESTS: integration-remote-api
- TESTS: acceptance
TESTS-ACCEPTANCE: access-levels
- TESTS: acceptance

View File

@ -85,7 +85,18 @@ default:
- admin
- admin
regular_user_password: what_for
remoteapi:
paths:
- %paths.base%/../remoteapi_features
contexts:
- FeatureContext:
baseUrl: http://localhost:8080/ocs/
admin:
- admin
- admin
regular_user_password: 123456
- RemoteContext:
remote: http://localhost:8080
extensions:
jarnaiz\JUnitFormatter\JUnitFormatterExtension:
filename: report.xml

View File

@ -62,6 +62,11 @@ trait BasicStructure {
/** @var string */
private $requestToken;
protected $adminUser;
protected $regularUser;
protected $localBaseUrl;
protected $remoteBaseUrl;
public function __construct($baseUrl, $admin, $regular_user_password) {
// Initialize your context here

View File

@ -0,0 +1,147 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
use Behat\Behat\Context\Context;
require __DIR__ . '/../../vendor/autoload.php';
/**
* Remote context.
*/
class RemoteContext implements Context {
/** @var \OC\Remote\Instance */
protected $remoteInstance;
/** @var \OC\Remote\Credentials */
protected $credentails;
/** @var \OC\Remote\User */
protected $userResult;
protected $remoteUrl;
protected $lastException;
public function __construct($remote) {
require_once __DIR__ . '/../../../../lib/base.php';
$this->remoteUrl = $remote;
}
protected function getApiClient() {
return new \OC\Remote\Api\OCS($this->remoteInstance, $this->credentails, \OC::$server->getHTTPClientService());
}
/**
* @Given /^using remote server "(REMOTE|NON_EXISTING)"$/
*
* @param string $remoteServer "NON_EXISTING" or "REMOTE"
*/
public function selectRemoteInstance($remoteServer) {
if ($remoteServer == "REMOTE") {
$baseUri = $this->remoteUrl;
} else {
$baseUri = 'nonexistingnextcloudserver.local';
}
$this->lastException = null;
try {
$this->remoteInstance = new \OC\Remote\Instance($baseUri, \OC::$server->getMemCacheFactory()->createLocal(), \OC::$server->getHTTPClientService());
// trigger the status request
$this->remoteInstance->getProtocol();
} catch (\Exception $e) {
$this->lastException = $e;
}
}
/**
* @Then /^the remote version should be "([^"]*)"$/
* @param string $version
*/
public function theRemoteVersionShouldBe($version) {
if ($version === '__current_version__') {
$version = \OC::$server->getConfig()->getSystemValue('version', '0.0.0.0');
}
PHPUnit_Framework_Assert::assertEquals($version, $this->remoteInstance->getVersion());
}
/**
* @Then /^the remote protocol should be "([^"]*)"$/
* @param string $protocol
*/
public function theRemoteProtocolShouldBe($protocol) {
PHPUnit_Framework_Assert::assertEquals($protocol, $this->remoteInstance->getProtocol());
}
/**
* @Given /^using credentials "([^"]*)", "([^"]*)"/
* @param string $user
* @param string $password
*/
public function usingCredentials($user, $password) {
$this->credentails = new \OC\Remote\Credentials($user, $password);
}
/**
* @When /^getting the remote user info for "([^"]*)"$/
* @param string $user
*/
public function remoteUserInfo($user) {
$this->lastException = null;
try {
$this->userResult = $this->getApiClient()->getUser($user);
} catch (\Exception $e) {
$this->lastException = $e;
}
}
/**
* @Then /^the remote user should have userid "([^"]*)"$/
* @param string $user
*/
public function remoteUserId($user) {
PHPUnit_Framework_Assert::assertEquals($user, $this->userResult->getUserId());
}
/**
* @Then /^the request should throw a "([^"]*)"$/
* @param string $class
*/
public function lastError($class) {
PHPUnit_Framework_Assert::assertEquals($class, get_class($this->lastException));
}
/**
* @Then /^the capability "([^"]*)" is "([^"]*)"$/
* @param string $key
* @param string $value
*/
public function hasCapability($key, $value) {
$capabilities = $this->getApiClient()->getCapabilities();
$current = $capabilities;
$parts = explode('.', $key);
foreach ($parts as $part) {
if ($current !== null) {
$current = isset($current[$part]) ? $current[$part] : null;
}
}
PHPUnit_Framework_Assert::assertEquals($value, $current);
}
}

View File

@ -0,0 +1,37 @@
Feature: remote
Scenario: Get status of remote server
Given using remote server "REMOTE"
Then the remote version should be "__current_version__"
And the remote protocol should be "http"
Scenario: Get status of a non existing server
Given using remote server "NON_EXISTING"
Then the request should throw a "OC\Remote\Api\NotFoundException"
Scenario: Get user info for a remote user
Given using remote server "REMOTE"
And user "user0" exists
And using credentials "user0", "123456"
When getting the remote user info for "user0"
Then the remote user should have userid "user0"
Scenario: Get user info for a non existing remote user
Given using remote server "REMOTE"
And user "user0" exists
And using credentials "user0", "123456"
When getting the remote user info for "user_non_existing"
Then the request should throw a "OC\Remote\Api\NotFoundException"
Scenario: Get user info with invalid credentials
Given using remote server "REMOTE"
And user "user0" exists
And using credentials "user0", "invalid"
When getting the remote user info for "user0"
Then the request should throw a "OC\ForbiddenException"
Scenario: Get capability of remote server
Given using remote server "REMOTE"
And user "user0" exists
And using credentials "user0", "invalid"
Then the capability "theming.name" is "Nextcloud"

View File

@ -246,6 +246,14 @@ return array(
'OCP\\OCS\\IDiscoveryService' => $baseDir . '/lib/public/OCS/IDiscoveryService.php',
'OCP\\PreConditionNotMetException' => $baseDir . '/lib/public/PreConditionNotMetException.php',
'OCP\\Preview\\IProvider' => $baseDir . '/lib/public/Preview/IProvider.php',
'OCP\\Remote\\Api\\IApiCollection' => $baseDir . '/lib/public/Remote/Api/IApiCollection.php',
'OCP\\Remote\\Api\\IApiFactory' => $baseDir . '/lib/public/Remote/Api/IApiFactory.php',
'OCP\\Remote\\Api\\ICapabilitiesApi' => $baseDir . '/lib/public/Remote/Api/ICapabilitiesApi.php',
'OCP\\Remote\\Api\\IUserApi' => $baseDir . '/lib/public/Remote/Api/IUserApi.php',
'OCP\\Remote\\ICredentials' => $baseDir . '/lib/public/Remote/ICredentials.php',
'OCP\\Remote\\IInstance' => $baseDir . '/lib/public/Remote/IInstance.php',
'OCP\\Remote\\IInstanceFactory' => $baseDir . '/lib/public/Remote/IInstanceFactory.php',
'OCP\\Remote\\IUser' => $baseDir . '/lib/public/Remote/IUser.php',
'OCP\\Response' => $baseDir . '/lib/public/Response.php',
'OCP\\RichObjectStrings\\Definitions' => $baseDir . '/lib/public/RichObjectStrings/Definitions.php',
'OCP\\RichObjectStrings\\IValidator' => $baseDir . '/lib/public/RichObjectStrings/IValidator.php',
@ -779,6 +787,15 @@ return array(
'OC\\Preview\\WatcherConnector' => $baseDir . '/lib/private/Preview/WatcherConnector.php',
'OC\\Preview\\XBitmap' => $baseDir . '/lib/private/Preview/XBitmap.php',
'OC\\RedisFactory' => $baseDir . '/lib/private/RedisFactory.php',
'OC\\Remote\\Api\\ApiBase' => $baseDir . '/lib/private/Remote/Api/ApiBase.php',
'OC\\Remote\\Api\\ApiCollection' => $baseDir . '/lib/private/Remote/Api/ApiCollection.php',
'OC\\Remote\\Api\\ApiFactory' => $baseDir . '/lib/private/Remote/Api/ApiFactory.php',
'OC\\Remote\\Api\\NotFoundException' => $baseDir . '/lib/private/Remote/Api/NotFoundException.php',
'OC\\Remote\\Api\\OCS' => $baseDir . '/lib/private/Remote/Api/OCS.php',
'OC\\Remote\\Credentials' => $baseDir . '/lib/private/Remote/Credentials.php',
'OC\\Remote\\Instance' => $baseDir . '/lib/private/Remote/Instance.php',
'OC\\Remote\\InstanceFactory' => $baseDir . '/lib/private/Remote/InstanceFactory.php',
'OC\\Remote\\User' => $baseDir . '/lib/private/Remote/User.php',
'OC\\Repair' => $baseDir . '/lib/private/Repair.php',
'OC\\RepairException' => $baseDir . '/lib/private/RepairException.php',
'OC\\Repair\\CleanTags' => $baseDir . '/lib/private/Repair/CleanTags.php',

View File

@ -276,6 +276,14 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\OCS\\IDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCS/IDiscoveryService.php',
'OCP\\PreConditionNotMetException' => __DIR__ . '/../../..' . '/lib/public/PreConditionNotMetException.php',
'OCP\\Preview\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Preview/IProvider.php',
'OCP\\Remote\\Api\\IApiCollection' => __DIR__ . '/../../..' . '/lib/public/Remote/Api/IApiCollection.php',
'OCP\\Remote\\Api\\IApiFactory' => __DIR__ . '/../../..' . '/lib/public/Remote/Api/IApiFactory.php',
'OCP\\Remote\\Api\\ICapabilitiesApi' => __DIR__ . '/../../..' . '/lib/public/Remote/Api/ICapabilitiesApi.php',
'OCP\\Remote\\Api\\IUserApi' => __DIR__ . '/../../..' . '/lib/public/Remote/Api/IUserApi.php',
'OCP\\Remote\\ICredentials' => __DIR__ . '/../../..' . '/lib/public/Remote/ICredentials.php',
'OCP\\Remote\\IInstance' => __DIR__ . '/../../..' . '/lib/public/Remote/IInstance.php',
'OCP\\Remote\\IInstanceFactory' => __DIR__ . '/../../..' . '/lib/public/Remote/IInstanceFactory.php',
'OCP\\Remote\\IUser' => __DIR__ . '/../../..' . '/lib/public/Remote/IUser.php',
'OCP\\Response' => __DIR__ . '/../../..' . '/lib/public/Response.php',
'OCP\\RichObjectStrings\\Definitions' => __DIR__ . '/../../..' . '/lib/public/RichObjectStrings/Definitions.php',
'OCP\\RichObjectStrings\\IValidator' => __DIR__ . '/../../..' . '/lib/public/RichObjectStrings/IValidator.php',
@ -809,6 +817,15 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Preview\\WatcherConnector' => __DIR__ . '/../../..' . '/lib/private/Preview/WatcherConnector.php',
'OC\\Preview\\XBitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/XBitmap.php',
'OC\\RedisFactory' => __DIR__ . '/../../..' . '/lib/private/RedisFactory.php',
'OC\\Remote\\Api\\ApiBase' => __DIR__ . '/../../..' . '/lib/private/Remote/Api/ApiBase.php',
'OC\\Remote\\Api\\ApiCollection' => __DIR__ . '/../../..' . '/lib/private/Remote/Api/ApiCollection.php',
'OC\\Remote\\Api\\ApiFactory' => __DIR__ . '/../../..' . '/lib/private/Remote/Api/ApiFactory.php',
'OC\\Remote\\Api\\NotFoundException' => __DIR__ . '/../../..' . '/lib/private/Remote/Api/NotFoundException.php',
'OC\\Remote\\Api\\OCS' => __DIR__ . '/../../..' . '/lib/private/Remote/Api/OCS.php',
'OC\\Remote\\Credentials' => __DIR__ . '/../../..' . '/lib/private/Remote/Credentials.php',
'OC\\Remote\\Instance' => __DIR__ . '/../../..' . '/lib/private/Remote/Instance.php',
'OC\\Remote\\InstanceFactory' => __DIR__ . '/../../..' . '/lib/private/Remote/InstanceFactory.php',
'OC\\Remote\\User' => __DIR__ . '/../../..' . '/lib/private/Remote/User.php',
'OC\\Repair' => __DIR__ . '/../../..' . '/lib/private/Repair.php',
'OC\\RepairException' => __DIR__ . '/../../..' . '/lib/private/RepairException.php',
'OC\\Repair\\CleanTags' => __DIR__ . '/../../..' . '/lib/private/Repair/CleanTags.php',

View File

@ -0,0 +1,97 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote\Api;
use OCP\Http\Client\IClientService;
use OCP\Remote\ICredentials;
use OCP\Remote\IInstance;
class ApiBase {
/** @var IInstance */
private $instance;
/** @var ICredentials */
private $credentials;
/** @var IClientService */
private $clientService;
public function __construct(IInstance $instance, ICredentials $credentials, IClientService $clientService) {
$this->instance = $instance;
$this->credentials = $credentials;
$this->clientService = $clientService;
}
protected function getHttpClient() {
return $this->clientService->newClient();
}
protected function addDefaultHeaders(array $headers) {
return array_merge([
'OCS-APIREQUEST' => 'true',
'Accept' => 'application/json'
], $headers);
}
/**
* @param string $method
* @param string $url
* @param array $body
* @param array $query
* @param array $headers
* @return resource|string
* @throws \InvalidArgumentException
*/
protected function request($method, $url, array $body = [], array $query = [], array $headers = []) {
$fullUrl = trim($this->instance->getFullUrl(), '/') . '/' . $url;
$options = [
'query' => $query,
'headers' => $this->addDefaultHeaders($headers),
'auth' => [$this->credentials->getUsername(), $this->credentials->getPassword()]
];
if ($body) {
$options['body'] = $body;
}
$client = $this->getHttpClient();
switch ($method) {
case 'get':
$response = $client->get($fullUrl, $options);
break;
case 'post':
$response = $client->post($fullUrl, $options);
break;
case 'put':
$response = $client->put($fullUrl, $options);
break;
case 'delete':
$response = $client->delete($fullUrl, $options);
break;
case 'options':
$response = $client->options($fullUrl, $options);
break;
default:
throw new \InvalidArgumentException('Invalid method ' . $method);
}
return $response->getBody();
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote\Api;
use OCP\Http\Client\IClientService;
use OCP\Remote\Api\IApiCollection;
use OCP\Remote\ICredentials;
use OCP\Remote\IInstance;
class ApiCollection implements IApiCollection {
/** @var IInstance */
private $instance;
/** @var ICredentials */
private $credentials;
/** @var IClientService */
private $clientService;
public function __construct(IInstance $instance, ICredentials $credentials, IClientService $clientService) {
$this->instance = $instance;
$this->credentials = $credentials;
$this->clientService = $clientService;
}
public function getCapabilitiesApi() {
return new OCS($this->instance, $this->credentials, $this->clientService);
}
public function getUserApi() {
return new OCS($this->instance, $this->credentials, $this->clientService);
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote\Api;
use OCP\Http\Client\IClientService;
use OCP\Remote\Api\IApiFactory;
use OCP\Remote\ICredentials;
use OCP\Remote\IInstance;
class ApiFactory implements IApiFactory {
/** @var IClientService */
private $clientService;
public function __construct(IClientService $clientService) {
$this->clientService = $clientService;
}
public function getApiCollection(IInstance $instance, ICredentials $credentials) {
return new ApiCollection($instance, $credentials, $this->clientService);
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote\Api;
class NotFoundException extends \Exception {
}

View File

@ -0,0 +1,99 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote\Api;
use GuzzleHttp\Exception\ClientException;
use OC\ForbiddenException;
use OC\Remote\User;
use OCP\API;
use OCP\Remote\Api\ICapabilitiesApi;
use OCP\Remote\Api\IUserApi;
class OCS extends ApiBase implements ICapabilitiesApi, IUserApi {
/**
* @param string $method
* @param string $url
* @param array $body
* @param array $query
* @param array $headers
* @return array
* @throws ForbiddenException
* @throws NotFoundException
* @throws \Exception
*/
protected function request($method, $url, array $body = [], array $query = [], array $headers = []) {
try {
$response = json_decode(parent::request($method, 'ocs/v2.php/' . $url, $body, $query, $headers), true);
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() === 404) {
throw new NotFoundException();
} else if ($e->getResponse()->getStatusCode() === 403 || $e->getResponse()->getStatusCode() === 401) {
throw new ForbiddenException();
} else {
throw $e;
}
}
if (!isset($response['ocs']) || !isset($response['ocs']['meta'])) {
throw new \Exception('Invalid ocs response');
}
if ($response['ocs']['meta']['statuscode'] === API::RESPOND_UNAUTHORISED) {
throw new ForbiddenException();
}
if ($response['ocs']['meta']['statuscode'] === API::RESPOND_NOT_FOUND) {
throw new NotFoundException();
}
if ($response['ocs']['meta']['status'] !== 'ok') {
throw new \Exception('Unknown ocs error ' . $response['ocs']['meta']['message']);
}
return $response['ocs']['data'];
}
/**
* @param array $data
* @param string $type
* @param string[] $keys
* @throws \Exception
*/
private function checkResponseArray(array $data, $type, array $keys) {
foreach ($keys as $key) {
if (!array_key_exists($key, $data)) {
throw new \Exception('Invalid ' . $type . ' response, expected field ' . $key . ' not found');
}
}
}
public function getUser($userId) {
$result = $this->request('get', 'cloud/users/' . $userId);
$this->checkResponseArray($result, 'user', User::EXPECTED_KEYS);
return new User($result);
}
/**
* @return array The capabilities in the form of [$appId => [$capability => $value]]
*/
public function getCapabilities() {
$result = $this->request('get', 'cloud/capabilities');
return $result['capabilities'];
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote;
use OCP\Remote\ICredentials;
class Credentials implements ICredentials {
/** @var string */
private $user;
/** @var string */
private $password;
/**
* @param string $user
* @param string $password
*/
public function __construct($user, $password) {
$this->user = $user;
$this->password = $password;
}
/**
* @return string
*/
public function getUsername() {
return $this->user;
}
/**
* @return string
*/
public function getPassword() {
return $this->password;
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote;
use OC\Remote\Api\NotFoundException;
use OCP\Http\Client\IClientService;
use OCP\ICache;
use OCP\Remote\IInstance;
/**
* Provides some basic info about a remote Nextcloud instance
*/
class Instance implements IInstance {
/** @var string */
private $url;
/** @var ICache */
private $cache;
/** @var IClientService */
private $clientService;
/** @var array|null */
private $status;
/**
* @param string $url
* @param ICache $cache
* @param IClientService $clientService
*/
public function __construct($url, ICache $cache, IClientService $clientService) {
$url = str_replace('https://', '', $url);
$this->url = str_replace('http://', '', $url);
$this->cache = $cache;
$this->clientService = $clientService;
}
/**
* @return string The url of the remote server without protocol
*/
public function getUrl() {
return $this->url;
}
/**
* @return string The of of the remote server with protocol
*/
public function getFullUrl() {
return $this->getProtocol() . '://' . $this->getUrl();
}
/**
* @return string The full version string in '13.1.2.3' format
*/
public function getVersion() {
$status = $this->getStatus();
return $status['version'];
}
/**
* @return string 'http' or 'https'
*/
public function getProtocol() {
$status = $this->getStatus();
return $status['protocol'];
}
/**
* Check that the remote server is installed and not in maintenance mode
*
* @return bool
*/
public function isActive() {
$status = $this->getStatus();
return $status['installed'] && !$status['maintenance'];
}
/**
* @return array
* @throws NotFoundException
* @throws \Exception
*/
private function getStatus() {
if ($this->status) {
return $this->status;
}
$key = 'remote/' . $this->url . '/status';
$httpsKey = 'remote/' . $this->url . '/https';
$status = $this->cache->get($key);
if (!$status) {
$response = $this->downloadStatus('https://' . $this->getUrl() . '/status.php');
$protocol = 'https';
if (!$response) {
if ($status = $this->cache->get($httpsKey)) {
throw new \Exception('refusing to connect to remote instance(' . $this->url . ') over http that was previously accessible over https');
}
$response = $this->downloadStatus('http://' . $this->getUrl() . '/status.php');
$protocol = 'http';
} else {
$this->cache->set($httpsKey, true, 60 * 60 * 24 * 365);
}
$status = json_decode($response, true);
if ($status) {
$status['protocol'] = $protocol;
}
if ($status) {
$this->cache->set($key, $status, 5 * 60);
$this->status = $status;
} else {
throw new NotFoundException('Remote server not found at address ' . $this->url);
}
}
return $status;
}
/**
* @param string $url
* @return bool|string
*/
private function downloadStatus($url) {
try {
$request = $this->clientService->newClient()->get($url);
return $request->getBody();
} catch (\Exception $e) {
return false;
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote;
use OCP\Http\Client\IClientService;
use OCP\ICache;
use OCP\Remote\IInstanceFactory;
class InstanceFactory implements IInstanceFactory {
/** @var ICache */
private $cache;
/** @var IClientService */
private $clientService;
public function __construct(ICache $cache, IClientService $clientService) {
$this->cache = $cache;
$this->clientService = $clientService;
}
public function getInstance($url) {
return new Instance($url, $this->cache, $this->clientService);
}
}

138
lib/private/Remote/User.php Normal file
View File

@ -0,0 +1,138 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Remote;
use OCP\Remote\IUser;
class User implements IUser {
const EXPECTED_KEYS = [
'id',
'email',
'displayname',
'phone',
'address',
'website',
'groups',
'language',
'quota'
];
/** @var array */
private $data;
public function __construct(array $data) {
$this->data = $data;
}
/**
* @return string
*/
public function getUserId() {
return $this->data['id'];
}
/**
* @return string
*/
public function getEmail() {
return $this->data['email'];
}
/**
* @return string
*/
public function getDisplayName() {
return $this->data['displayname'];
}
/**
* @return string
*/
public function getPhone() {
return $this->data['phone'];
}
/**
* @return string
*/
public function getAddress() {
return $this->data['address'];
}
/**
* @return string
*/
public function getWebsite() {
return $this->data['website'];
}
/**
* @return string
*/
public function getTwitter() {
return isset($this->data['twitter']) ? $this->data['twitter'] : '';
}
/**
* @return string[]
*/
public function getGroups() {
return $this->data['groups'];
}
/**
* @return string
*/
public function getLanguage() {
return $this->data['language'];
}
/**
* @return int
*/
public function getUsedSpace() {
return $this->data['quota']['used'];
}
/**
* @return int
*/
public function getFreeSpace() {
return $this->data['quota']['free'];
}
/**
* @return int
*/
public function getTotalSpace() {
return $this->data['quota']['total'];
}
/**
* @return int
*/
public function getQuota() {
return $this->data['quota']['quota'];
}
}

View File

@ -89,6 +89,8 @@ use OC\Memcache\ArrayCache;
use OC\Memcache\Factory;
use OC\Notification\Manager;
use OC\OCS\DiscoveryService;
use OC\Remote\Api\ApiFactory;
use OC\Remote\InstanceFactory;
use OC\Repair\NC11\CleanPreviewsBackgroundJob;
use OC\RichObjectStrings\Validator;
use OC\Security\Bruteforce\Throttler;
@ -123,6 +125,8 @@ use OCP\IServerContainer;
use OCP\ITempManager;
use OCP\Contacts\ContactsMenu\IActionFactory;
use OCP\Lock\ILockingProvider;
use OCP\Remote\Api\IApiFactory;
use OCP\Remote\IInstanceFactory;
use OCP\RichObjectStrings\IValidator;
use OCP\Security\IContentSecurityPolicyManager;
use OCP\Share;
@ -1109,6 +1113,15 @@ class Server extends ServerContainer implements IServerContainer {
$c->getConfig()
);
});
$this->registerService(IApiFactory::class, function(Server $c) {
return new ApiFactory($c->getHTTPClientService());
});
$this->registerService(IInstanceFactory::class, function(Server $c) {
$memcacheFactory = $c->getMemCacheFactory();
return new InstanceFactory($memcacheFactory->createLocal('remoteinstance.'), $c->getHTTPClientService());
});
}
/**
@ -1878,4 +1891,18 @@ class Server extends ServerContainer implements IServerContainer {
public function getCloudIdManager() {
return $this->query(ICloudIdManager::class);
}
/**
* @return \OCP\Remote\Api\IApiFactory
*/
public function getRemoteApiFactory() {
return $this->query(IApiFactory::class);
}
/**
* @return \OCP\Remote\IInstanceFactory
*/
public function getRemoteInstanceFactory() {
return $this->query(IInstanceFactory::class);
}
}

View File

@ -541,4 +541,16 @@ interface IServerContainer extends IContainer {
* @since 12.0.0
*/
public function getCloudIdManager();
/**
* @return \OCP\Remote\Api\IApiFactory
* @since 13.0.0
*/
public function getRemoteApiFactory();
/**
* @return \OCP\Remote\IInstanceFactory
* @since 13.0.0
*/
public function getRemoteInstanceFactory();
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote\Api;
/**
* Provides access to the various apis of a remote instance
*
* @since 13.0.0
*/
interface IApiCollection {
/**
* @return IUserApi
*
* @since 13.0.0
*/
public function getUserApi();
/**
* @return ICapabilitiesApi
*
* @since 13.0.0
*/
public function getCapabilitiesApi();
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote\Api;
use OCP\Remote\ICredentials;
use OCP\Remote\IInstance;
/**
* @since 13.0.0
*/
interface IApiFactory {
/**
* @param IInstance $instance
* @param ICredentials $credentials
* @return IApiCollection
*
* @since 13.0.0
*/
public function getApiCollection(IInstance $instance, ICredentials $credentials);
}

View File

@ -0,0 +1,34 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote\Api;
/**
* @since 13.0.0
*/
interface ICapabilitiesApi {
/**
* @return array The capabilities in the form of [$appId => [$capability => $value]]
*
* @since 13.0.0
*/
public function getCapabilities();
}

View File

@ -0,0 +1,37 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote\Api;
use OCP\Remote\IUser;
/**
* @since 13.0.0
*/
interface IUserApi {
/**
* @param string $userId
* @return IUser
*
* @since 13.0.0
*/
public function getUser($userId);
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote;
/**
* The credentials for a remote user
*
* @since 13.0.0
*/
interface ICredentials {
/**
* @return string
*
* @since 13.0.0
*/
public function getUsername();
/**
* @return string
*
* @since 13.0.0
*/
public function getPassword();
}

View File

@ -0,0 +1,66 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote;
/**
* Provides some basic info about a remote Nextcloud instance
*
* @since 13.0.0
*/
interface IInstance {
/**
* @return string The url of the remote server without protocol
*
* @since 13.0.0
*/
public function getUrl();
/**
* @return string The of of the remote server with protocol
*
* @since 13.0.0
*/
public function getFullUrl();
/**
* @return string The full version string in '13.1.2.3' format
*
* @since 13.0.0
*/
public function getVersion();
/**
* @return string 'http' or 'https'
*
* @since 13.0.0
*/
public function getProtocol();
/**
* Check that the remote server is installed and not in maintenance mode
*
* @since 13.0.0
*
* @return bool
*/
public function isActive();
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote;
/**
* @since 13.0.0
*/
interface IInstanceFactory {
/**
* @param string $url
* @return IInstance
*
* @since 13.0.0
*/
public function getInstance($url);
}

120
lib/public/Remote/IUser.php Normal file
View File

@ -0,0 +1,120 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\Remote;
/**
* User info for a remote user
*
* @since 13.0.0
*/
interface IUser {
/**
* @return string
*
* @since 13.0.0
*/
public function getUserId();
/**
* @return string
*
* @since 13.0.0
*/
public function getEmail();
/**
* @return string
*
* @since 13.0.0
*/
public function getDisplayName();
/**
* @return string
*
* @since 13.0.0
*/
public function getPhone();
/**
* @return string
*
* @since 13.0.0
*/
public function getAddress();
/**
* @return string
*
* @since 13.0.0
*/
public function getWebsite();
/**
* @return string
*
* @since 13.0.0
*/
public function getTwitter();
/**
* @return string[]
*
* @since 13.0.0
*/
public function getGroups();
/**
* @return string
*
* @since 13.0.0
*/
public function getLanguage();
/**
* @return int
*
* @since 13.0.0
*/
public function getUsedSpace();
/**
* @return int
*
* @since 13.0.0
*/
public function getFreeSpace();
/**
* @return int
*
* @since 13.0.0
*/
public function getTotalSpace();
/**
* @return int
*
* @since 13.0.0
*/
public function getQuota();
}

View File

@ -0,0 +1,95 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Remote\Api;
use OC\Memcache\ArrayCache;
use OC\Remote\Api\OCS;
use OC\Remote\Credentials;
use OC\Remote\InstanceFactory;
use OCP\Remote\IInstanceFactory;
use Test\TestCase;
use Test\Traits\ClientServiceTrait;
class OCSTest extends TestCase {
use ClientServiceTrait;
/** @var IInstanceFactory */
private $instanceFactory;
protected function setUp() {
parent::setUp();
$this->instanceFactory = new InstanceFactory(new ArrayCache(), $this->getClientService());
$this->expectGetRequest('https://example.com/status.php',
'{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
}
protected function getOCSClient() {
return new OCS(
$this->instanceFactory->getInstance('example.com'),
new Credentials('user', 'pass'),
$this->getClientService()
);
}
protected function getOCSUrl($url) {
return 'https://example.com/ocs/v2.php/' . $url;
}
public function testGetUser() {
$client = $this->getOCSClient();
$this->expectGetRequest($this->getOCSUrl('cloud/users/user'),
'{"ocs":{"meta":{"status":"ok","statuscode":200,"message":"OK"},
"data":{"id":"user","quota":{"free":5366379387,"used":2329733,"total":5368709120,"relative":0.040000000000000001,"quota":5368709120},
"email":null,"displayname":"test","phone":"","address":"","website":"","twitter":"","groups":["Test","Test1"],"language":"en"}}}');
$user = $client->getUser('user');
$this->assertEquals('user', $user->getUserId());
}
/**
* @expectedException \Exception
* @expectedExceptionMessage Invalid user response, expected field email not found
*/
public function testGetUserInvalidResponse() {
$client = $this->getOCSClient();
$this->expectGetRequest($this->getOCSUrl('cloud/users/user'),
'{"ocs":{"meta":{"status":"ok","statuscode":200,"message":"OK"},
"data":{"id":"user"}}}');
$client->getUser('user');
}
/**
* @expectedException \OC\ForbiddenException
*/
public function testInvalidPassword() {
$client = $this->getOCSClient();
$this->expectGetRequest($this->getOCSUrl('cloud/users/user'),
'{"ocs":{"meta":{"status":"failure","statuscode":997,"message":"Current user is not logged in"},"data":[]}}');
$client->getUser('user');
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Remote;
use OC\Memcache\ArrayCache;
use OC\Remote\Instance;
use OCP\ICache;
use Test\TestCase;
use Test\Traits\ClientServiceTrait;
class InstanceTest extends TestCase {
use ClientServiceTrait;
/** @var ICache */
private $cache;
protected function setUp() {
parent::setUp();
$this->cache = new ArrayCache();
}
public function testBasicStatus() {
$instance = new Instance('example.com', $this->cache, $this->getClientService());
$this->expectGetRequest('https://example.com/status.php', '{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
$this->assertEquals(true, $instance->isActive());
$this->assertEquals('13.0.0.5', $instance->getVersion());
$this->assertEquals('https', $instance->getProtocol());
$this->assertEquals('https://example.com', $instance->getFullUrl());
}
public function testHttpFallback() {
$instance = new Instance('example.com', $this->cache, $this->getClientService());
$this->expectGetRequest('https://example.com/status.php', new \Exception());
$this->expectGetRequest('http://example.com/status.php', '{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
$this->assertEquals('http', $instance->getProtocol());
$this->assertEquals('http://example.com', $instance->getFullUrl());
}
public function testRerequestHttps() {
$instance = new Instance('example.com', $this->cache, $this->getClientService());
$this->expectGetRequest('https://example.com/status.php', '{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
$this->assertEquals('https', $instance->getProtocol());
$this->assertEquals(true, $instance->isActive());
$this->cache->remove('remote/example.com/status');
$this->expectGetRequest('https://example.com/status.php', '{"installed":true,"maintenance":true,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
$instance2 = new Instance('example.com', $this->cache, $this->getClientService());
$this->assertEquals('https', $instance2->getProtocol());
$this->assertEquals(false, $instance2->isActive());
}
/**
* @expectedException \Exception
* @expectedExceptionMessage refusing to connect to remote instance(example.com) over http that was previously accessible over https
*/
public function testPreventDowngradeAttach() {
$instance = new Instance('example.com', $this->cache, $this->getClientService());
$this->expectGetRequest('https://example.com/status.php', '{"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"13.0.0.5","versionstring":"13.0.0 alpha","edition":"","productname":"Nextcloud"}');
$this->assertEquals('https', $instance->getProtocol());
$this->expectGetRequest('https://example.com/status.php', new \Exception());
$this->cache->remove('remote/example.com/status');
$instance2 = new Instance('example.com', $this->cache, $this->getClientService());
$instance2->getProtocol();
}
}

View File

@ -0,0 +1,121 @@
<?php
/**
* @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Traits;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
trait ClientServiceTrait {
/** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */
private $clientService;
/** @var IClient|\PHPUnit_Framework_MockObject_MockObject */
private $client;
private $expectedGetRequests = [];
private $expectedPostRequests = [];
/**
* Wrapper to be forward compatible to phpunit 5.4+
*
* @param string $originalClassName
* @return \PHPUnit_Framework_MockObject_MockObject
*/
abstract protected function createMock($originalClassName);
/**
* Returns a matcher that matches when the method is executed
* zero or more times.
*
* @return \PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
*
* @since Method available since Release 3.0.0
*/
abstract public function any();
protected function setUpClientServiceTrait() {
$this->clientService = $this->createMock(IClientService::class);
$this->client = $this->createMock(IClient::class);
$this->clientService->expects($this->any())
->method('newClient')
->willReturn($this->client);
$this->client->expects($this->any())
->method('get')
->willReturnCallback(function ($url) {
if (!isset($this->expectedGetRequests[$url])) {
throw new \Exception('unexpected request: ' . $url);
}
$result = $this->expectedGetRequests[$url];
if ($result instanceof \Exception) {
throw $result;
} else {
$response = $this->createMock(IResponse::class);
$response->expects($this->any())
->method('getBody')
->willReturn($result);
return $response;
}
});
$this->client->expects($this->any())
->method('post')
->willReturnCallback(function ($url) {
if (!isset($this->expectedPostRequests[$url])) {
throw new \Exception('unexpected request: ' . $url);
}
$result = $this->expectedPostRequests[$url];
if ($result instanceof \Exception) {
throw $result;
} else {
$response = $this->createMock(IResponse::class);
$response->expects($this->any())
->method('getBody')
->willReturn($result);
return $response;
}
});
}
/**
* @param string $url
* @param string|\Exception $result
*/
protected function expectGetRequest($url, $result) {
$this->expectedGetRequests[$url] = $result;
}
/**
* @param string $url
* @param string|\Exception $result
*/
protected function expectPostRequest($url, $result) {
$this->expectedPostRequests[$url] = $result;
}
/**
* @return IClientService|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getClientService() {
return $this->clientService;
}
}