CloudFederationProviderManager.php 6.0 KB

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