diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
index ef77f2fa317de..fcc78d68808da 100644
--- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
+++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php
@@ -22,9 +22,14 @@
* along with this program. If not, see .
*
*/
+
namespace OCA\CloudFederationAPI\Controller;
+use Exception;
use OCA\CloudFederationAPI\Config;
+use OCA\CloudFederationAPI\Model\CloudFederationAddShare;
+use OCA\CloudFederationAPI\Model\CloudFederationError;
+use OCA\CloudFederationAPI\Model\CloudFederationValidationError;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
@@ -41,6 +46,7 @@
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Util;
use Psr\Log\LoggerInterface;
/**
@@ -108,46 +114,36 @@ public function __construct($appName,
*
* @param string $shareWith
* @param string $name resource name (e.g. document.odt)
- * @param string $description share description (optional)
+ * @param string|null $description share description
* @param string $providerId resource UID on the provider side
* @param string $owner provider specific UID of the user who owns the resource
- * @param string $ownerDisplayName display name of the user who shared the item
- * @param string $sharedBy provider specific UID of the user who shared the resource
- * @param string $sharedByDisplayName display name of the user who shared the resource
- * @param array $protocol (e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]])
- * @param string $shareType ('group' or 'user' share)
- * @param $resourceType ('file', 'calendar',...)
- * @return Http\DataResponse|JSONResponse
+ * @param string|null $ownerDisplayName display name of the user who shared the item
+ * @param string|null $sharedBy provider specific UID of the user who shared the resource
+ * @param string|null $sharedByDisplayName display name of the user who shared the resource
+ * @param array{name: string[], options: array{}} $protocol e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]]
+ * @param string $shareType 'group' or 'user' share
+ * @param string $resourceType 'file', 'calendar',...
+ *
+ * @return JSONResponse 201 The notification was successfully received. The display name of the recepient might be returned in the body.
+ * @return JSONResponse 400 Bad request due to invalid parameters, e.g. when `shareWith` is not found or required properties are missing.
+ * @return JSONResponse 501 Share type or the resource type is not supported.
*
* Example: curl -H "Content-Type: application/json" -X POST -d '{"shareWith":"admin1@serve1","name":"welcome server2.txt","description":"desc","providerId":"2","owner":"admin2@http://localhost/server2","ownerDisplayName":"admin2 display","shareType":"user","resourceType":"file","protocol":{"name":"webdav","options":{"sharedSecret":"secret","permissions":"webdav-property"}}}' http://localhost/server/index.php/ocm/shares
*/
- public function addShare($shareWith, $name, $description, $providerId, $owner, $ownerDisplayName, $sharedBy, $sharedByDisplayName, $protocol, $shareType, $resourceType) {
+ public function addShare(string $shareWith, string $name, ?string $description, string $providerId, string $owner, ?string $ownerDisplayName, ?string $sharedBy, ?string $sharedByDisplayName, array $protocol, string $shareType, string $resourceType) {
// check if all required parameters are set
- if ($shareWith === null ||
- $name === null ||
- $providerId === null ||
- $owner === null ||
- $resourceType === null ||
- $shareType === null ||
- !is_array($protocol) ||
- !isset($protocol['name']) ||
+ if (!isset($protocol['name']) ||
!isset($protocol['options']) ||
!is_array($protocol['options']) ||
!isset($protocol['options']['sharedSecret'])
) {
- return new JSONResponse(
- ['message' => 'Missing arguments'],
- Http::STATUS_BAD_REQUEST
- );
+ return new JSONResponse(new CloudFederationValidationError(message: 'Missing arguments'), Http::STATUS_BAD_REQUEST);
}
$supportedShareTypes = $this->config->getSupportedShareTypes($resourceType);
if (!in_array($shareType, $supportedShareTypes)) {
- return new JSONResponse(
- ['message' => 'Share type "' . $shareType . '" not implemented'],
- Http::STATUS_NOT_IMPLEMENTED
- );
+ return new JSONResponse(new CloudFederationError(message: 'Share type "' . $shareType . '" not implemented'), Http::STATUS_NOT_IMPLEMENTED);
}
$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
@@ -157,10 +153,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
$shareWith = $this->mapUid($shareWith);
if (!$this->userManager->userExists($shareWith)) {
- $response = new JSONResponse(
- ['message' => 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
- Http::STATUS_BAD_REQUEST
- );
+ $response = new JSONResponse(new CloudFederationValidationError(message: 'User "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()), Http::STATUS_BAD_REQUEST);
$response->throttle();
return $response;
}
@@ -168,10 +161,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
if ($shareType === 'group') {
if (!$this->groupManager->groupExists($shareWith)) {
- $response = new JSONResponse(
- ['message' => 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()],
- Http::STATUS_BAD_REQUEST
- );
+ $response = new JSONResponse(new CloudFederationValidationError(message: 'Group "' . $shareWith . '" does not exists at ' . $this->urlGenerator->getBaseUrl()), Http::STATUS_BAD_REQUEST);
$response->throttle();
return $response;
}
@@ -193,21 +183,12 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
$share->setProtocol($protocol);
$provider->shareReceived($share);
} catch (ProviderDoesNotExistsException $e) {
- return new JSONResponse(
- ['message' => $e->getMessage()],
- Http::STATUS_NOT_IMPLEMENTED
- );
+ return new JSONResponse(new CloudFederationError(message: $e->getMessage()), Http::STATUS_NOT_IMPLEMENTED);
} catch (ProviderCouldNotAddShareException $e) {
- return new JSONResponse(
- ['message' => $e->getMessage()],
- $e->getCode()
- );
- } catch (\Exception $e) {
+ return new JSONResponse(new CloudFederationError(message: $e->getMessage()), $e->getCode());
+ } catch (Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
- return new JSONResponse(
- ['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
- Http::STATUS_BAD_REQUEST
- );
+ return new JSONResponse(new CloudFederationValidationError(message: 'Internal error at ' . $this->urlGenerator->getBaseUrl()), Http::STATUS_BAD_REQUEST);
}
$user = $this->userManager->get($shareWith);
@@ -216,9 +197,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
$recipientDisplayName = $user->getDisplayName();
}
- return new JSONResponse(
- ['recipientDisplayName' => $recipientDisplayName],
- Http::STATUS_CREATED);
+ return new JSONResponse(new CloudFederationAddShare($recipientDisplayName), Http::STATUS_CREATED);
}
/**
@@ -228,60 +207,43 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $
* @PublicPage
* @BruteForceProtection(action=receiveFederatedShareNotification)
*
- * @param string $notificationType (notification type, e.g. SHARE_ACCEPTED)
- * @param string $resourceType (calendar, file, contact,...)
- * @param string $providerId id of the share
- * @param array $notification the actual payload of the notification
- * @return JSONResponse
+ * @param string $notificationType notification type, e.g. SHARE_ACCEPTED
+ * @param string $resourceType calendar, file, contact,...
+ * @param string|null $providerId id of the share
+ * @param array{}|null $notification the actual payload of the notification
+ * @return JSONResponse 201 The notification was successfully received
+ * @return JSONResponse 400 Bad request due to invalid parameters, e.g. when `type` is invalid or missing.
+ * @return JSONResponse 501 The resource type is not supported.
*/
- public function receiveNotification($notificationType, $resourceType, $providerId, array $notification) {
+ public function receiveNotification(string $notificationType, string $resourceType, ?string $providerId, ?array $notification) {
// check if all required parameters are set
- if ($notificationType === null ||
- $resourceType === null ||
- $providerId === null ||
- !is_array($notification)
- ) {
- return new JSONResponse(
- ['message' => 'Missing arguments'],
- Http::STATUS_BAD_REQUEST
- );
+ if ($providerId === null || !is_array($notification)) {
+ return new JSONResponse(new CloudFederationValidationError(message: 'Missing arguments'), Http::STATUS_BAD_REQUEST);
}
try {
$provider = $this->cloudFederationProviderManager->getCloudFederationProvider($resourceType);
$result = $provider->notificationReceived($notificationType, $providerId, $notification);
} catch (ProviderDoesNotExistsException $e) {
- return new JSONResponse(
- ['message' => $e->getMessage()],
- Http::STATUS_BAD_REQUEST
- );
+ return new JSONResponse(new CloudFederationValidationError(message: $e->getMessage()), Http::STATUS_BAD_REQUEST);
} catch (ShareNotFound $e) {
- $response = new JSONResponse(
- ['message' => $e->getMessage()],
- Http::STATUS_BAD_REQUEST
- );
+ $response = new JSONResponse(new CloudFederationValidationError(message: $e->getMessage()), Http::STATUS_BAD_REQUEST);
$response->throttle();
return $response;
} catch (ActionNotSupportedException $e) {
- return new JSONResponse(
- ['message' => $e->getMessage()],
- Http::STATUS_NOT_IMPLEMENTED
- );
+ return new JSONResponse(new CloudFederationError(message: $e->getMessage()), Http::STATUS_NOT_IMPLEMENTED);
} catch (BadRequestException $e) {
return new JSONResponse($e->getReturnMessage(), Http::STATUS_BAD_REQUEST);
} catch (AuthenticationFailedException $e) {
- $response = new JSONResponse(['message' => 'RESOURCE_NOT_FOUND'], Http::STATUS_FORBIDDEN);
+ $response = new JSONResponse(new CloudFederationError(message: 'RESOURCE_NOT_FOUND'), Http::STATUS_FORBIDDEN);
$response->throttle();
return $response;
- } catch (\Exception $e) {
- return new JSONResponse(
- ['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()],
- Http::STATUS_BAD_REQUEST
- );
+ } catch (Exception $e) {
+ return new JSONResponse(new CloudFederationValidationError(message: 'Internal error at ' . $this->urlGenerator->getBaseUrl()), Http::STATUS_BAD_REQUEST);
}
- return new JSONResponse($result,Http::STATUS_CREATED);
+ return new JSONResponse($result, Http::STATUS_CREATED);
}
/**
@@ -293,7 +255,7 @@ public function receiveNotification($notificationType, $resourceType, $providerI
private function mapUid($uid) {
// FIXME this should be a method in the user management instead
$this->logger->debug('shareWith before, ' . $uid, ['app' => $this->appName]);
- \OCP\Util::emitHook(
+ Util::emitHook(
'\OCA\Files_Sharing\API\Server2Server',
'preLoginNameUsedAsUserName',
['uid' => &$uid]
diff --git a/apps/cloud_federation_api/lib/Model/CloudFederationAddShare.php b/apps/cloud_federation_api/lib/Model/CloudFederationAddShare.php
new file mode 100644
index 0000000000000..3fc2da8a7ffbe
--- /dev/null
+++ b/apps/cloud_federation_api/lib/Model/CloudFederationAddShare.php
@@ -0,0 +1,40 @@
+
+ *
+ * @author Kate Döen
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+declare(strict_types=1);
+
+namespace OCA\CloudFederationAPI\Model;
+
+use JsonSerializable;
+
+class CloudFederationAddShare implements JsonSerializable {
+ /**
+ * @param string|null $recipientDisplayName
+ */
+ public function __construct(public ?string $recipientDisplayName) {
+ }
+
+ public function jsonSerialize(): array {
+ return ["recipientDisplayName" => $this->recipientDisplayName];
+ }
+}
diff --git a/apps/cloud_federation_api/lib/Model/CloudFederationError.php b/apps/cloud_federation_api/lib/Model/CloudFederationError.php
new file mode 100644
index 0000000000000..bf8ac358df5b0
--- /dev/null
+++ b/apps/cloud_federation_api/lib/Model/CloudFederationError.php
@@ -0,0 +1,40 @@
+
+ *
+ * @author Kate Döen
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+declare(strict_types=1);
+
+namespace OCA\CloudFederationAPI\Model;
+
+use JsonSerializable;
+
+class CloudFederationError implements JsonSerializable {
+ /**
+ * @param string $message
+ */
+ public function __construct(public string $message) {
+ }
+
+ public function jsonSerialize(): array {
+ return ["message" => $this->message];
+ }
+}
diff --git a/apps/cloud_federation_api/lib/Model/CloudFederationValidationError.php b/apps/cloud_federation_api/lib/Model/CloudFederationValidationError.php
new file mode 100644
index 0000000000000..d19b4a0fb17c0
--- /dev/null
+++ b/apps/cloud_federation_api/lib/Model/CloudFederationValidationError.php
@@ -0,0 +1,42 @@
+
+ *
+ * @author Kate Döen
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+declare(strict_types=1);
+
+namespace OCA\CloudFederationAPI\Model;
+
+use JsonSerializable;
+
+class CloudFederationValidationError extends CloudFederationError implements JsonSerializable {
+ /**
+ * @param string $message
+ * @param array{name: string|null, message: string|null}[]|null $validationErrors bla bla
+ */
+ public function __construct(string $message, public ?array $validationErrors = null) {
+ parent::__construct($message);
+ }
+
+ public function jsonSerialize(): array {
+ return array_merge(parent::jsonSerialize(), ["validationErrors" => $this->validationErrors]);
+ }
+}
diff --git a/apps/cloud_federation_api/openapi.json b/apps/cloud_federation_api/openapi.json
new file mode 100644
index 0000000000000..f435f3c12ee05
--- /dev/null
+++ b/apps/cloud_federation_api/openapi.json
@@ -0,0 +1,311 @@
+{
+ "openapi": "3.0.3",
+ "info": {
+ "title": "Cloud Federation API",
+ "description": "Enable clouds to communicate with each other and exchange data",
+ "license": {
+ "name": "agpl"
+ },
+ "version": "1.9.0"
+ },
+ "paths": {
+ "/index.php/ocm/shares": {
+ "post": {
+ "tags": [
+ "cloud_federation_api"
+ ],
+ "summary": "add share",
+ "description": "Example: curl -H \"Content-Type: application/json\" -X POST -d '{\"shareWith\":\"admin1@serve1\",\"name\":\"welcome server2.txt\",\"description\":\"desc\",\"providerId\":\"2\",\"owner\":\"admin2@http://localhost/server2\",\"ownerDisplayName\":\"admin2 display\",\"shareType\":\"user\",\"resourceType\":\"file\",\"protocol\":{\"name\":\"webdav\",\"options\":{\"sharedSecret\":\"secret\",\"permissions\":\"webdav-property\"}}}' http://localhost/server/index.php/ocm/shares",
+ "operationId": "add-share",
+ "parameters": [
+ {
+ "name": "shareWith",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "name",
+ "in": "query",
+ "description": "resource name (e.g. document.odt)",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "description",
+ "in": "query",
+ "description": "share description",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "providerId",
+ "in": "query",
+ "description": "resource UID on the provider side",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "owner",
+ "in": "query",
+ "description": "provider specific UID of the user who owns the resource",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "ownerDisplayName",
+ "in": "query",
+ "description": "display name of the user who shared the item",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "sharedBy",
+ "in": "query",
+ "description": "provider specific UID of the user who shared the resource",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "sharedByDisplayName",
+ "in": "query",
+ "description": "display name of the user who shared the resource",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "protocol",
+ "in": "query",
+ "description": "e,.g. ['name' => 'webdav', 'options' => ['username' => 'john', 'permissions' => 31]]",
+ "required": true,
+ "schema": {
+ "required": [
+ "name",
+ "options"
+ ],
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ },
+ {
+ "name": "shareType",
+ "in": "query",
+ "description": "'group' or 'user' share",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "resourceType",
+ "in": "query",
+ "description": "'file', 'calendar',...",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "The notification was successfully received. The display name of the recepient might be returned in the body.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CloudFederationAddShare"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad request due to invalid parameters, e.g. when `shareWith` is not found or required properties are missing.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CloudFederationValidationError"
+ }
+ }
+ }
+ },
+ "501": {
+ "description": "Share type or the resource type is not supported.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CloudFederationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/index.php/ocm/notifications": {
+ "post": {
+ "tags": [
+ "cloud_federation_api"
+ ],
+ "description": "receive notification about existing share",
+ "operationId": "receive-notification",
+ "parameters": [
+ {
+ "name": "notificationType",
+ "in": "query",
+ "description": "notification type, e.g. SHARE_ACCEPTED",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "resourceType",
+ "in": "query",
+ "description": "calendar, file, contact,...",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "providerId",
+ "in": "query",
+ "description": "id of the share",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "notification",
+ "in": "query",
+ "description": "the actual payload of the notification",
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "The notification was successfully received",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": true
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad request due to invalid parameters, e.g. when `type` is invalid or missing.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CloudFederationValidationError"
+ }
+ }
+ }
+ },
+ "501": {
+ "description": "The resource type is not supported.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CloudFederationError"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "CloudFederationAddShare": {
+ "type": "object",
+ "properties": {
+ "recipientDisplayName": {
+ "type": "string"
+ }
+ }
+ },
+ "CloudFederationError": {
+ "required": [
+ "message"
+ ],
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ }
+ }
+ },
+ "CloudFederationValidationError": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/CloudFederationError"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "validationErrors": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ "securitySchemes": {
+ "basic_auth": {
+ "type": "http",
+ "scheme": "basic"
+ }
+ }
+ },
+ "security": [
+ {
+ "basic_auth": []
+ }
+ ],
+ "tags": [
+ {
+ "name": "cloud_federation_api"
+ }
+ ]
+}
\ No newline at end of file