You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
6.9 KiB

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