CloudFederationProviderManager.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org>
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace OC\Federation;
  22. use OC\AppFramework\Http;
  23. use OCP\App\IAppManager;
  24. use OCP\Federation\Exceptions\ProviderDoesNotExistsException;
  25. use OCP\Federation\ICloudFederationNotification;
  26. use OCP\Federation\ICloudFederationProvider;
  27. use OCP\Federation\ICloudFederationProviderManager;
  28. use OCP\Federation\ICloudFederationShare;
  29. use OCP\Federation\ICloudIdManager;
  30. use OCP\Http\Client\IClientService;
  31. use OCP\ILogger;
  32. /**
  33. * Class Manager
  34. *
  35. * Manage Cloud Federation Providers
  36. *
  37. * @package OC\Federation
  38. */
  39. class CloudFederationProviderManager implements ICloudFederationProviderManager {
  40. /** @var array list of available cloud federation providers */
  41. private $cloudFederationProvider;
  42. /** @var IAppManager */
  43. private $appManager;
  44. /** @var IClientService */
  45. private $httpClientService;
  46. /** @var ICloudIdManager */
  47. private $cloudIdManager;
  48. /** @var ILogger */
  49. private $logger;
  50. /** @var array cache OCM end-points */
  51. private $ocmEndPoints = [];
  52. private $supportedAPIVersion = '1.0-proposal1';
  53. /**
  54. * CloudFederationProviderManager constructor.
  55. *
  56. * @param IAppManager $appManager
  57. * @param IClientService $httpClientService
  58. * @param ICloudIdManager $cloudIdManager
  59. * @param ILogger $logger
  60. */
  61. public function __construct(IAppManager $appManager,
  62. IClientService $httpClientService,
  63. ICloudIdManager $cloudIdManager,
  64. ILogger $logger) {
  65. $this->cloudFederationProvider= [];
  66. $this->appManager = $appManager;
  67. $this->httpClientService = $httpClientService;
  68. $this->cloudIdManager = $cloudIdManager;
  69. $this->logger = $logger;
  70. }
  71. /**
  72. * Registers an callback function which must return an cloud federation provider
  73. *
  74. * @param string $resourceType which resource type does the provider handles
  75. * @param string $displayName user facing name of the federated share provider
  76. * @param callable $callback
  77. */
  78. public function addCloudFederationProvider($resourceType, $displayName, callable $callback) {
  79. $this->cloudFederationProvider[$resourceType] = [
  80. 'resourceType' => $resourceType,
  81. 'displayName' => $displayName,
  82. 'callback' => $callback,
  83. ];
  84. }
  85. /**
  86. * remove cloud federation provider
  87. *
  88. * @param string $providerId
  89. */
  90. public function removeCloudFederationProvider($providerId) {
  91. unset($this->cloudFederationProvider[$providerId]);
  92. }
  93. /**
  94. * get a list of all cloudFederationProviders
  95. *
  96. * @return array [resourceType => ['resourceType' => $resourceType, 'displayName' => $displayName, 'callback' => callback]]
  97. */
  98. public function getAllCloudFederationProviders() {
  99. return $this->cloudFederationProvider;
  100. }
  101. /**
  102. * get a specific cloud federation provider
  103. *
  104. * @param string $resourceType
  105. * @return ICloudFederationProvider
  106. * @throws ProviderDoesNotExistsException
  107. */
  108. public function getCloudFederationProvider($resourceType) {
  109. if (isset($this->cloudFederationProvider[$resourceType])) {
  110. return call_user_func($this->cloudFederationProvider[$resourceType]['callback']);
  111. } else {
  112. throw new ProviderDoesNotExistsException($resourceType);
  113. }
  114. }
  115. public function sendShare(ICloudFederationShare $share) {
  116. $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith());
  117. $ocmEndPoint = $this->getOCMEndPoint($cloudID->getRemote());
  118. if (empty($ocmEndPoint)) {
  119. return false;
  120. }
  121. $client = $this->httpClientService->newClient();
  122. try {
  123. $response = $client->post($ocmEndPoint . '/shares', [
  124. 'body' => $share->getShare(),
  125. 'timeout' => 10,
  126. 'connect_timeout' => 10,
  127. ]);
  128. if ($response->getStatusCode() === Http::STATUS_CREATED) {
  129. $result = json_decode($response->getBody(), true);
  130. return (is_array($result)) ? $result : [];
  131. }
  132. } catch (\Exception $e) {
  133. // if flat re-sharing is not supported by the remote server
  134. // we re-throw the exception and fall back to the old behaviour.
  135. // (flat re-shares has been introduced in Nextcloud 9.1)
  136. if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
  137. $this->logger->debug($e->getMessage());
  138. throw $e;
  139. }
  140. }
  141. return false;
  142. }
  143. /**
  144. * @param string $url
  145. * @param ICloudFederationNotification $notification
  146. * @return mixed
  147. */
  148. public function sendNotification($url, ICloudFederationNotification $notification) {
  149. $ocmEndPoint = $this->getOCMEndPoint($url);
  150. if (empty($ocmEndPoint)) {
  151. return false;
  152. }
  153. $client = $this->httpClientService->newClient();
  154. try {
  155. $response = $client->post($ocmEndPoint . '/notifications', [
  156. 'body' => $notification->getMessage(),
  157. 'timeout' => 10,
  158. 'connect_timeout' => 10,
  159. ]);
  160. if ($response->getStatusCode() === Http::STATUS_CREATED) {
  161. $result = json_decode($response->getBody(), true);
  162. return (is_array($result)) ? $result : [];
  163. }
  164. } catch (\Exception $e) {
  165. // log the error and return false
  166. $this->logger->error('error while sending notification for federated share: ' . $e->getMessage());
  167. }
  168. return false;
  169. }
  170. /**
  171. * check if the new cloud federation API is ready to be used
  172. *
  173. * @return bool
  174. */
  175. public function isReady() {
  176. return $this->appManager->isEnabledForUser('cloud_federation_api');
  177. }
  178. /**
  179. * check if server supports the new OCM api and ask for the correct end-point
  180. *
  181. * @param string $url full base URL of the cloud server
  182. * @return string
  183. */
  184. protected function getOCMEndPoint($url) {
  185. if (isset($this->ocmEndPoints[$url])) {
  186. return $this->ocmEndPoints[$url];
  187. }
  188. $client = $this->httpClientService->newClient();
  189. try {
  190. $response = $client->get($url . '/ocm-provider/', ['timeout' => 10, 'connect_timeout' => 10]);
  191. } catch (\Exception $e) {
  192. $this->ocmEndPoints[$url] = '';
  193. return '';
  194. }
  195. $result = $response->getBody();
  196. $result = json_decode($result, true);
  197. $supportedVersion = isset($result['apiVersion']) && $result['apiVersion'] === $this->supportedAPIVersion;
  198. if (isset($result['endPoint']) && $supportedVersion) {
  199. $this->ocmEndPoints[$url] = $result['endPoint'];
  200. return $result['endPoint'];
  201. }
  202. $this->ocmEndPoints[$url] = '';
  203. return '';
  204. }
  205. }