Skip to content

Commit

Permalink
make share endpoints openapi compatible
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Hartmann <chris-hartmann@gmx.de>
  • Loading branch information
Chartman123 committed Oct 26, 2024
1 parent 6dcf19c commit 7447044
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 36 deletions.
76 changes: 50 additions & 26 deletions lib/Controller/ShareApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,21 @@
use OCA\Forms\Db\FormMapper;
use OCA\Forms\Db\Share;
use OCA\Forms\Db\ShareMapper;
use OCA\Forms\ResponseDefinitions;
use OCA\Forms\Service\CirclesService;
use OCA\Forms\Service\ConfigService;
use OCA\Forms\Service\FormsService;

use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\IMapperException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\Files\IRootFolder;
use OCP\IGroup;
Expand All @@ -56,6 +58,9 @@
use OCP\Share\IShare;
use Psr\Log\LoggerInterface;

/**
* @psalm-import-type FormsShare from ResponseDefinitions
*/
class ShareApiController extends OCSController {
private IUser $currentUser;

Expand Down Expand Up @@ -85,9 +90,23 @@ public function __construct(
* @param int $formId The form to share
* @param int $shareType Nextcloud-ShareType
* @param string $shareWith ID of user/group/... to share with. For Empty shareWith and shareType Link, this will be set as RandomID.
* @return DataResponse
* @throws OCSBadRequestException
* @throws OCSForbiddenException
* @param array<string> $permissions the permissions granted on the share, defaults to `submit`
* Possible values:
* - `submit` user can submit
* - `results` user can see the results
* - `results_delete` user can see and delete results
* @return DataResponse<array<FormsShare>, Http::STATUS_CREATED, array<>>
* @throws OCSBadRequestException Invalid shareType
* @throws OCSBadRequestException Invalid permission given
* @throws OCSBadRequestException Invalid user to share with
* @throws OCSBadRequestException Invalid group to share with
* @throws OCSBadRequestException Invalid team to share with
* @throws OCSBadRequestException Unknown shareType
* @throws OCSBadRequestException Share hash exists, please try again
* @throws OCSBadRequestException Teams app is disabled
* @throws OCSForbiddenException Link share not allowed
* @throws OCSForbiddenException This form is not owned by the current user
* @throws OCSNotFoundException Could not find form
*/
#[CORS()]
#[NoAdminRequired()]
Expand All @@ -109,20 +128,20 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
// Block LinkShares if not allowed
if ($shareType === IShare::TYPE_LINK && !$this->configService->getAllowPublicLink()) {
$this->logger->debug('Link Share not allowed.');
throw new OCSForbiddenException('Link Share not allowed.');
throw new OCSForbiddenException('Link share not allowed.');
}

try {
$form = $this->formMapper->findById($formId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find form', ['exception' => $e]);
throw new OCSBadRequestException('Could not find form');
throw new OCSNotFoundException('Could not find form');
}

// Check for permission to share form
if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();
throw new OCSForbiddenException('This form is not owned by the current user');
}

if (!$this->validatePermissions($permissions, $shareType)) {
Expand Down Expand Up @@ -157,11 +176,11 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
// Check if hash already exists. (Unfortunately not possible here by unique index on db.)
try {
// Try loading a share to the hash.
$nonex = $this->shareMapper->findPublicShareByHash($shareWith);
$this->shareMapper->findPublicShareByHash($shareWith);

// If we come here, a share has been found --> The share hash already exists, thus aborting.
$this->logger->debug('Share Hash already exists.');
throw new OCSException('Share Hash exists. Please retry.');
$this->logger->debug('Share hash already exists.');
throw new OCSBadRequestException('Share hash exists, please retry.');
} catch (DoesNotExistException $e) {
// Just continue, this is what we expect to happen (share hash not existing yet).
}
Expand All @@ -170,7 +189,7 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
case IShare::TYPE_CIRCLE:
if (!$this->circlesService->isCirclesEnabled()) {
$this->logger->debug('Teams app is disabled, sharing to teams not possible.');
throw new OCSException('Teams app is disabled.');
throw new OCSBadRequestException('Teams app is disabled.');
}
$circle = $this->circlesService->getCircle($shareWith);
if (is_null($circle)) {
Expand All @@ -182,7 +201,7 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
default:
// This passed the check for used shareTypes, but has not been found here.
$this->logger->warning('Unknown, but used shareType: {shareType}. Please file an issue on GitHub.', [ 'shareType' => $shareType ]);
throw new OCSException('Unknown shareType.');
throw new OCSBadRequestException('Unknown shareType.');
}

$share = new Share();
Expand All @@ -202,18 +221,22 @@ public function newShare(int $formId, int $shareType, string $shareWith = '', ar
$shareData = $share->read();
$shareData['displayName'] = $this->formsService->getShareDisplayName($shareData);

return new DataResponse($shareData);
return new DataResponse($shareData, Http::STATUS_CREATED);
}

/**
* Update permissions of a share
*
* @param int $formId of the form
* @param int $shareId of the share to update
* @param array $keyValuePairs Array of key=>value pairs to update.
* @return DataResponse
* @throws OCSBadRequestException
* @throws OCSForbiddenException
* @param array{key: string, values: mixed} $keyValuePairs Array of key=>value pairs to update.
* @return DataResponse<array<int>, Http::STATUS_OK, array<>>
* @throws OCSBadRequestException Share doesn't belong to given Form
* @throws OCSBadRequestException Invalid permission given
* @throws OCSForbiddenException This form is not owned by the current user
* @throws OCSForbiddenException Empty keyValuePairs, will not update
* @throws OCSForbiddenException Not allowed to update other properties than permissions
* @throws OCSNotFoundException Could not find share
*/
#[CORS()]
#[NoAdminRequired()]
Expand All @@ -230,7 +253,7 @@ public function updateShare(int $formId, int $shareId, array $keyValuePairs): Da
$form = $this->formMapper->findById($formId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find share', ['exception' => $e]);
throw new OCSBadRequestException('Could not find share');
throw new OCSNotFoundException('Could not find share');
}

if ($formId !== $formShare->getFormId()) {
Expand All @@ -240,19 +263,19 @@ public function updateShare(int $formId, int $shareId, array $keyValuePairs): Da

if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();
throw new OCSForbiddenException('This form is not owned by the current user');
}

// Don't allow empty array
if (sizeof($keyValuePairs) === 0) {
$this->logger->info('Empty keyValuePairs, will not update.');
throw new OCSForbiddenException();
throw new OCSForbiddenException('Empty keyValuePairs, will not update');
}

//Don't allow to change other properties than permissions
if (count($keyValuePairs) > 1 || !key_exists('permissions', $keyValuePairs)) {
$this->logger->debug('Not allowed to update other properties than permissions');
throw new OCSForbiddenException();
throw new OCSForbiddenException('Not allowed to update other properties than permissions');
}

if (!$this->validatePermissions($keyValuePairs['permissions'], $formShare->getShareType())) {
Expand Down Expand Up @@ -302,9 +325,10 @@ public function updateShare(int $formId, int $shareId, array $keyValuePairs): Da
*
* @param int $formId of the form
* @param int $shareId of the share to delete
* @return DataResponse
* @throws OCSBadRequestException
* @throws OCSForbiddenException
* @return DataResponse<array<int>, Http::STATUS_OK, array<>>
* @throws OCSBadRequestException Share doesn't belong to given Form
* @throws OCSForbiddenException This form is not owned by the current user
* @throws OCSNotFoundException Could not find share
*/
#[CORS()]
#[NoAdminRequired()]
Expand All @@ -320,7 +344,7 @@ public function deleteShare(int $formId, int $shareId): DataResponse {
$form = $this->formMapper->findById($formId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find share', ['exception' => $e]);
throw new OCSBadRequestException('Could not find share');
throw new OCSNotFoundException('Could not find share');
}

if ($formId !== $share->getFormId()) {
Expand All @@ -330,7 +354,7 @@ public function deleteShare(int $formId, int $shareId): DataResponse {

if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();
throw new OCSForbiddenException('This form is not owned by the current user');
}

$this->shareMapper->delete($share);
Expand Down
13 changes: 10 additions & 3 deletions lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
* "text": string,
* "name": string,
* "options": array<FormsOption>,
* "accept": array<FormsQuestionFileType>,
* "accept": array<string>,
* "extraSettings": stdClass
* }
*
Expand Down Expand Up @@ -99,8 +99,15 @@
* "uploadedFileId": int,
* "fileName": string
* }
*
* @psalm-type FormsQuestionFileType = string
*
* @psalm-type FormsShare = array{
* "id": int,
* "formId": int,
* "shareType": int,
* "shareWith": string,
* "permissions": array<string>,
* "displayName": string
* }
*/
class ResponseDefinitions {
}
Loading

0 comments on commit 7447044

Please sign in to comment.