CloudFederationProviderManager.php 7.0 KB

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