OCMProvider.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\OCM\Model;
  8. use NCU\Security\Signature\Model\Signatory;
  9. use OCP\EventDispatcher\IEventDispatcher;
  10. use OCP\OCM\Events\ResourceTypeRegisterEvent;
  11. use OCP\OCM\Exceptions\OCMArgumentException;
  12. use OCP\OCM\Exceptions\OCMProviderException;
  13. use OCP\OCM\IOCMProvider;
  14. use OCP\OCM\IOCMResource;
  15. /**
  16. * @since 28.0.0
  17. */
  18. class OCMProvider implements IOCMProvider {
  19. private bool $enabled = false;
  20. private string $apiVersion = '';
  21. private string $endPoint = '';
  22. /** @var IOCMResource[] */
  23. private array $resourceTypes = [];
  24. private ?Signatory $signatory = null;
  25. private bool $emittedEvent = false;
  26. public function __construct(
  27. protected IEventDispatcher $dispatcher,
  28. ) {
  29. }
  30. /**
  31. * @param bool $enabled
  32. *
  33. * @return $this
  34. */
  35. public function setEnabled(bool $enabled): static {
  36. $this->enabled = $enabled;
  37. return $this;
  38. }
  39. /**
  40. * @return bool
  41. */
  42. public function isEnabled(): bool {
  43. return $this->enabled;
  44. }
  45. /**
  46. * @param string $apiVersion
  47. *
  48. * @return $this
  49. */
  50. public function setApiVersion(string $apiVersion): static {
  51. $this->apiVersion = $apiVersion;
  52. return $this;
  53. }
  54. /**
  55. * @return string
  56. */
  57. public function getApiVersion(): string {
  58. return $this->apiVersion;
  59. }
  60. /**
  61. * @param string $endPoint
  62. *
  63. * @return $this
  64. */
  65. public function setEndPoint(string $endPoint): static {
  66. $this->endPoint = $endPoint;
  67. return $this;
  68. }
  69. /**
  70. * @return string
  71. */
  72. public function getEndPoint(): string {
  73. return $this->endPoint;
  74. }
  75. /**
  76. * create a new resource to later add it with {@see IOCMProvider::addResourceType()}
  77. * @return IOCMResource
  78. */
  79. public function createNewResourceType(): IOCMResource {
  80. return new OCMResource();
  81. }
  82. /**
  83. * @param IOCMResource $resource
  84. *
  85. * @return $this
  86. */
  87. public function addResourceType(IOCMResource $resource): static {
  88. $this->resourceTypes[] = $resource;
  89. return $this;
  90. }
  91. /**
  92. * @param IOCMResource[] $resourceTypes
  93. *
  94. * @return $this
  95. */
  96. public function setResourceTypes(array $resourceTypes): static {
  97. $this->resourceTypes = $resourceTypes;
  98. return $this;
  99. }
  100. /**
  101. * @return IOCMResource[]
  102. */
  103. public function getResourceTypes(): array {
  104. if (!$this->emittedEvent) {
  105. $this->emittedEvent = true;
  106. $event = new ResourceTypeRegisterEvent($this);
  107. $this->dispatcher->dispatchTyped($event);
  108. }
  109. return $this->resourceTypes;
  110. }
  111. /**
  112. * @param string $resourceName
  113. * @param string $protocol
  114. *
  115. * @return string
  116. * @throws OCMArgumentException
  117. */
  118. public function extractProtocolEntry(string $resourceName, string $protocol): string {
  119. foreach ($this->getResourceTypes() as $resource) {
  120. if ($resource->getName() === $resourceName) {
  121. $entry = $resource->getProtocols()[$protocol] ?? null;
  122. if (is_null($entry)) {
  123. throw new OCMArgumentException('protocol not found');
  124. }
  125. return (string)$entry;
  126. }
  127. }
  128. throw new OCMArgumentException('resource not found');
  129. }
  130. public function setSignatory(Signatory $signatory): void {
  131. $this->signatory = $signatory;
  132. }
  133. public function getSignatory(): ?Signatory {
  134. return $this->signatory;
  135. }
  136. /**
  137. * import data from an array
  138. *
  139. * @param array $data
  140. *
  141. * @return $this
  142. * @throws OCMProviderException in case a descent provider cannot be generated from data
  143. * @see self::jsonSerialize()
  144. */
  145. public function import(array $data): static {
  146. $this->setEnabled(is_bool($data['enabled'] ?? '') ? $data['enabled'] : false)
  147. // Fall back to old apiVersion for Nextcloud 30 compatibility
  148. ->setApiVersion((string)($data['version'] ?? $data['apiVersion'] ?? ''))
  149. ->setEndPoint($data['endPoint'] ?? '');
  150. $resources = [];
  151. foreach (($data['resourceTypes'] ?? []) as $resourceData) {
  152. $resource = new OCMResource();
  153. $resources[] = $resource->import($resourceData);
  154. }
  155. $this->setResourceTypes($resources);
  156. if (isset($data['publicKey'])) {
  157. // import details about the remote request signing public key, if available
  158. $signatory = new Signatory();
  159. $signatory->setKeyId($data['publicKey']['keyId'] ?? '');
  160. $signatory->setPublicKey($data['publicKey']['publicKeyPem'] ?? '');
  161. if ($signatory->getKeyId() !== '' && $signatory->getPublicKey() !== '') {
  162. $this->setSignatory($signatory);
  163. }
  164. }
  165. if (!$this->looksValid()) {
  166. throw new OCMProviderException('remote provider does not look valid');
  167. }
  168. return $this;
  169. }
  170. /**
  171. * @return bool
  172. */
  173. private function looksValid(): bool {
  174. return ($this->getApiVersion() !== '' && $this->getEndPoint() !== '');
  175. }
  176. /**
  177. * @return array{
  178. * enabled: bool,
  179. * apiVersion: '1.0-proposal1',
  180. * endPoint: string,
  181. * publicKey: array{
  182. * keyId: string,
  183. * publicKeyPem: string
  184. * },
  185. * resourceTypes: list<array{
  186. * name: string,
  187. * shareTypes: list<string>,
  188. * protocols: array<string, string>
  189. * }>,
  190. * version: string
  191. * }
  192. */
  193. public function jsonSerialize(): array {
  194. $resourceTypes = [];
  195. foreach ($this->getResourceTypes() as $res) {
  196. $resourceTypes[] = $res->jsonSerialize();
  197. }
  198. return [
  199. 'enabled' => $this->isEnabled(),
  200. 'apiVersion' => '1.0-proposal1', // deprecated, but keep it to stay compatible with old version
  201. 'version' => $this->getApiVersion(), // informative but real version
  202. 'endPoint' => $this->getEndPoint(),
  203. 'publicKey' => $this->getSignatory()->jsonSerialize(),
  204. 'resourceTypes' => $resourceTypes
  205. ];
  206. }
  207. }