From 6d44de24768eac8ac3e5f70232f343569d457304 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Wed, 25 Jan 2023 15:07:56 +0100 Subject: [PATCH] cloud_federation_api: Introduce OpenAPI spec Signed-off-by: jld3103 --- .../Controller/RequestHandlerController.php | 71 ++-- .../lib/ResponseDefinitions.php | 46 +++ apps/cloud_federation_api/openapi.json | 317 ++++++++++++++++++ 3 files changed, 401 insertions(+), 33 deletions(-) create mode 100644 apps/cloud_federation_api/lib/ResponseDefinitions.php create mode 100644 apps/cloud_federation_api/openapi.json diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index ef77f2fa317de..ebd6dd66fb913 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -5,6 +5,7 @@ * @author Bjoern Schiessle * @author Christoph Wurst * @author Roeland Jago Douma + * @author Kate Döen * * @license GNU AGPL version 3 or any later version * @@ -22,8 +23,10 @@ * along with this program. If not, see . * */ + namespace OCA\CloudFederationAPI\Controller; +use Exception; use OCA\CloudFederationAPI\Config; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -41,6 +44,7 @@ use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Share\Exceptions\ShareNotFound; +use OCP\Util; use Psr\Log\LoggerInterface; /** @@ -50,7 +54,8 @@ * * @package OCA\CloudFederationAPI\Controller */ -class RequestHandlerController extends Controller { +class RequestHandlerController extends Controller +{ /** @var LoggerInterface */ private $logger; @@ -108,30 +113,29 @@ 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',... + * + * @psalm-import-type CloudFederationAddShare + * @psalm-import-type CloudFederationValidationError + * @psalm-import-type CloudFederationError + * @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): JSONResponse { // 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']) @@ -202,7 +206,7 @@ public function addShare($shareWith, $name, $description, $providerId, $owner, $ ['message' => $e->getMessage()], $e->getCode() ); - } catch (\Exception $e) { + } catch (Exception $e) { $this->logger->error($e->getMessage(), ['exception' => $e]); return new JSONResponse( ['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()], @@ -228,20 +232,21 @@ 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 + * + * @psalm-import-type CloudFederationValidationError + * @psalm-import-type CloudFederationError + * @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): JSONResponse { // check if all required parameters are set - if ($notificationType === null || - $resourceType === null || - $providerId === null || - !is_array($notification) - ) { + if ($providerId === null || !is_array($notification)) { return new JSONResponse( ['message' => 'Missing arguments'], Http::STATUS_BAD_REQUEST @@ -274,14 +279,14 @@ public function receiveNotification($notificationType, $resourceType, $providerI $response = new JSONResponse(['message' => 'RESOURCE_NOT_FOUND'], Http::STATUS_FORBIDDEN); $response->throttle(); return $response; - } catch (\Exception $e) { + } catch (Exception $e) { return new JSONResponse( ['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 +298,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/ResponseDefinitions.php b/apps/cloud_federation_api/lib/ResponseDefinitions.php new file mode 100644 index 0000000000000..2c5b5e715c77f --- /dev/null +++ b/apps/cloud_federation_api/lib/ResponseDefinitions.php @@ -0,0 +1,46 @@ + + * + * @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 . + * + */ + +namespace OCA\CloudFederationAPI; + +/** + * @psalm-type CloudFederationAddShare = array{ + * recipientDisplayName: ?string, + * } + * + * @psalm-type CloudFederationError = array{ + * message: string, + * } + * + * @psalm-type CloudFederationValidationError = CloudFederationError&array{ + * validationErrors: array{ + * name: string, + * message: string|null, + * }[], + * } + */ +class ResponseDefinitions +{ +} diff --git a/apps/cloud_federation_api/openapi.json b/apps/cloud_federation_api/openapi.json new file mode 100644 index 0000000000000..f3ff5304433b7 --- /dev/null +++ b/apps/cloud_federation_api/openapi.json @@ -0,0 +1,317 @@ +{ + "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" + }, + { + "required": [ + "validationErrors" + ], + "type": "object", + "properties": { + "validationErrors": { + "type": "array", + "items": { + "required": [ + "name" + ], + "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