Merge pull request #4179 from nextcloud/backport/4173/stable25
This commit is contained in:
@@ -194,7 +194,7 @@ class AttachmentService {
|
|||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function create($cardId, $type, $data) {
|
public function create($cardId, $type, $data) {
|
||||||
$this->attachmentServiceValidator->check(compact('cardId', 'type', 'data'));
|
$this->attachmentServiceValidator->check(compact('cardId', 'type'));
|
||||||
|
|
||||||
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
|
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
|
|
||||||
public function extendData(Attachment $attachment) {
|
public function extendData(Attachment $attachment) {
|
||||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||||
$share = $this->shareProvider->getShareById($attachment->getId());
|
$share = $this->getShareForAttachment($attachment);
|
||||||
$files = $userFolder->getById($share->getNode()->getId());
|
$files = $userFolder->getById($share->getNode()->getId());
|
||||||
if (count($files) === 0) {
|
if (count($files) === 0) {
|
||||||
return $attachment;
|
return $attachment;
|
||||||
@@ -161,7 +161,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
// Problem: Folders
|
// Problem: Folders
|
||||||
/** @psalm-suppress InvalidCatch */
|
/** @psalm-suppress InvalidCatch */
|
||||||
try {
|
try {
|
||||||
$share = $this->shareProvider->getShareById($attachment->getId());
|
$share = $this->getShareForAttachment($attachment);
|
||||||
} catch (ShareNotFound $e) {
|
} catch (ShareNotFound $e) {
|
||||||
throw new NotFoundException('File not found');
|
throw new NotFoundException('File not found');
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function update(Attachment $attachment) {
|
public function update(Attachment $attachment) {
|
||||||
$share = $this->shareProvider->getShareById($attachment->getId());
|
$share = $this->getShareForAttachment($attachment);
|
||||||
$target = $share->getNode();
|
$target = $share->getNode();
|
||||||
$file = $this->getUploadedFile();
|
$file = $this->getUploadedFile();
|
||||||
$fileName = $file['name'];
|
$fileName = $file['name'];
|
||||||
@@ -258,8 +258,13 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
return $attachment;
|
return $attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NoPermissionException
|
||||||
|
* @throws NotFoundException
|
||||||
|
* @throws ShareNotFound
|
||||||
|
*/
|
||||||
public function delete(Attachment $attachment) {
|
public function delete(Attachment $attachment) {
|
||||||
$share = $this->shareProvider->getShareById($attachment->getId());
|
$share = $this->getShareForAttachment($attachment);
|
||||||
$file = $share->getNode();
|
$file = $share->getNode();
|
||||||
$attachment->setData($file->getName());
|
$attachment->setData($file->getName());
|
||||||
|
|
||||||
@@ -282,4 +287,21 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
public function markAsDeleted(Attachment $attachment) {
|
public function markAsDeleted(Attachment $attachment) {
|
||||||
throw new \Exception('Not implemented');
|
throw new \Exception('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NoPermissionException
|
||||||
|
*/
|
||||||
|
private function getShareForAttachment(Attachment $attachment): IShare {
|
||||||
|
try {
|
||||||
|
$share = $this->shareProvider->getShareById($attachment->getId());
|
||||||
|
} catch (ShareNotFound $e) {
|
||||||
|
throw new NoPermissionException('No permission to access the attachment from the card');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int)$share->getSharedWith() !== (int)$attachment->getCardId()) {
|
||||||
|
throw new NoPermissionException('No permission to access the attachment from the card');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $share;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ default:
|
|||||||
- RequestContext
|
- RequestContext
|
||||||
- BoardContext
|
- BoardContext
|
||||||
- CommentContext
|
- CommentContext
|
||||||
|
- AttachmentContext
|
||||||
- SearchContext
|
- SearchContext
|
||||||
|
|||||||
90
tests/integration/features/bootstrap/AttachmentContext.php
Normal file
90
tests/integration/features/bootstrap/AttachmentContext.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Behat\Behat\Context\Context;
|
||||||
|
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
|
||||||
|
class AttachmentContext implements Context {
|
||||||
|
use RequestTrait;
|
||||||
|
|
||||||
|
/** @var BoardContext */
|
||||||
|
protected $boardContext;
|
||||||
|
/** @var ServerContext */
|
||||||
|
private $serverContext;
|
||||||
|
|
||||||
|
protected $lastAttachment = null;
|
||||||
|
protected array $rememberedAttachments = [];
|
||||||
|
|
||||||
|
/** @BeforeScenario */
|
||||||
|
public function gatherContexts(BeforeScenarioScope $scope) {
|
||||||
|
$environment = $scope->getEnvironment();
|
||||||
|
|
||||||
|
$this->boardContext = $environment->getContext('BoardContext');
|
||||||
|
$this->serverContext = $environment->getContext('ServerContext');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(int $cardId, int $attachmentId) {
|
||||||
|
$this->requestContext->sendPlainRequest('DELETE', '/index.php/apps/deck/cards/' . $cardId . '/attachment/file:' . $attachmentId);
|
||||||
|
$response = $this->requestContext->getResponseBodyFromJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When deleting the attachment :attachmentReference for the card :cardReference
|
||||||
|
*/
|
||||||
|
public function deletingTheAttachmentForTheCard($attachmentReference, $cardReference) {
|
||||||
|
$cardId = $this->boardContext->getRememberedCard($cardReference)['id'] ?? null;
|
||||||
|
$attachmentId = $this->getRememberedAttachment($attachmentReference)['id'] ?? null;
|
||||||
|
Assert::assertNotNull($cardId, 'Card needs to be available');
|
||||||
|
Assert::assertNotNull($attachmentId, 'Attachment needs to be available');
|
||||||
|
$this->delete($cardId, $attachmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given /^uploads an attachment to the last used card$/
|
||||||
|
*/
|
||||||
|
public function uploadsAnAttachmentToTheLastUsedCard() {
|
||||||
|
$cardId = $this->boardContext->getLastUsedCard()['id'] ?? null;
|
||||||
|
Assert::assertNotNull($cardId, 'Card data is not set');
|
||||||
|
|
||||||
|
$this->requestContext->sendPlainRequest('POST', '/index.php/apps/deck/cards/' . $cardId . '/attachment', [
|
||||||
|
'multipart' => [
|
||||||
|
[
|
||||||
|
'name' => 'file',
|
||||||
|
'contents' => 'Example content',
|
||||||
|
'filename' => 'test.txt',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'type',
|
||||||
|
'contents' => 'file'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given remember the last attachment as :arg1
|
||||||
|
*/
|
||||||
|
public function rememberTheLastAttachmentAs($arg1) {
|
||||||
|
$this->requestContext->theResponseShouldHaveStatusCode(200);
|
||||||
|
$this->lastAttachment = $this->requestContext->getResponseBodyFromJson();
|
||||||
|
$this->rememberedAttachments[$arg1] = $this->lastAttachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRememberedAttachment($name) {
|
||||||
|
return $this->rememberedAttachments[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When fetching the attachment :attachmentReference for the card :cardReference
|
||||||
|
*/
|
||||||
|
public function fetchingTheAttachmentForTheCard($attachmentReference, $cardReference) {
|
||||||
|
$cardId = $this->boardContext->getRememberedCard($cardReference)['id'] ?? null;
|
||||||
|
$attachmentId = $this->getRememberedAttachment($attachmentReference)['id'] ?? null;
|
||||||
|
Assert::assertNotNull($cardId, 'Card needs to be available');
|
||||||
|
Assert::assertNotNull($attachmentId, 'Attachment needs to be available');
|
||||||
|
|
||||||
|
$this->requestContext->sendPlainRequest('GET', '/index.php/apps/deck/cards/' . $cardId . '/attachment/file:' . $attachmentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ class BoardContext implements Context {
|
|||||||
private $stack = null;
|
private $stack = null;
|
||||||
/** @var array last card response */
|
/** @var array last card response */
|
||||||
private $card = null;
|
private $card = null;
|
||||||
|
private array $storedCards = [];
|
||||||
|
|
||||||
/** @var ServerContext */
|
/** @var ServerContext */
|
||||||
private $serverContext;
|
private $serverContext;
|
||||||
@@ -31,6 +32,15 @@ class BoardContext implements Context {
|
|||||||
return $this->card;
|
return $this->card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Given /^creates a board with example content$/
|
||||||
|
*/
|
||||||
|
public function createExampleContent() {
|
||||||
|
$this->createsABoardNamedWithColor('Example board', 'ff0000');
|
||||||
|
$this->createAStackNamed('ToDo');
|
||||||
|
$this->createACardNamed('My example card');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^creates a board named "([^"]*)" with color "([^"]*)"$/
|
* @Given /^creates a board named "([^"]*)" with color "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
@@ -232,4 +242,15 @@ class BoardContext implements Context {
|
|||||||
$this->requestContext->sendJSONrequest('POST', '/index.php/apps/deck/cards/' . $this->card['id'] .'/label/' . $label['id']);
|
$this->requestContext->sendJSONrequest('POST', '/index.php/apps/deck/cards/' . $this->card['id'] .'/label/' . $label['id']);
|
||||||
$this->requestContext->getResponse()->getBody()->seek(0);
|
$this->requestContext->getResponse()->getBody()->seek(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @When remember the last card as :arg1
|
||||||
|
*/
|
||||||
|
public function rememberTheLastCardAs($arg1) {
|
||||||
|
$this->storedCards[$arg1] = $this->getLastUsedCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRememberedCard($arg1) {
|
||||||
|
return $this->storedCards[$arg1] ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,36 @@ class RequestContext implements Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sendPlainRequest(string $method, $uri = '', array $options = []) {
|
||||||
|
$client = new Client;
|
||||||
|
try {
|
||||||
|
$this->response = $client->request(
|
||||||
|
$method,
|
||||||
|
rtrim($this->serverContext->getBaseUrl(), '/') . '/' . ltrim($uri, '/'),
|
||||||
|
array_merge(
|
||||||
|
[
|
||||||
|
'cookies' => $this->serverContext->getCookieJar(),
|
||||||
|
'headers' => [
|
||||||
|
'requesttoken' => $this->serverContext->getReqestToken(),
|
||||||
|
'OCS-APIREQUEST' => 'true',
|
||||||
|
'Accept' => 'application/json'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
$options,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (ClientException $e) {
|
||||||
|
$this->response = $e->getResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getResponse(): ResponseInterface {
|
public function getResponse(): ResponseInterface {
|
||||||
return $this->response;
|
return $this->response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getResponseBodyFromJson() {
|
||||||
|
$this->getResponse()->getBody()->seek(0);
|
||||||
|
return json_decode((string)$this->getResponse()->getBody(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class ServerContext implements Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getCookieJar(): CookieJar {
|
public function getCookieJar(): CookieJar {
|
||||||
|
echo $this->currentUser;
|
||||||
return $this->cookieJar;
|
return $this->cookieJar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,3 +135,31 @@ Feature: File sharing
|
|||||||
When Deleting last share
|
When Deleting last share
|
||||||
And as "user2" the file "/Deck/user0-file2.txt" does not exist
|
And as "user2" the file "/Deck/user0-file2.txt" does not exist
|
||||||
And as "user3" the file "/Deck/user0-file2.txt" does not exist
|
And as "user3" the file "/Deck/user0-file2.txt" does not exist
|
||||||
|
|
||||||
|
Scenario: Remove a share through the deck API
|
||||||
|
Given acting as user "user0"
|
||||||
|
When creates a board with example content
|
||||||
|
And remember the last card as "user0-card"
|
||||||
|
And uploads an attachment to the last used card
|
||||||
|
And remember the last attachment as "user0-attachment"
|
||||||
|
|
||||||
|
Given acting as user "user1"
|
||||||
|
When creates a board with example content
|
||||||
|
And remember the last card as "user1-card"
|
||||||
|
And uploads an attachment to the last used card
|
||||||
|
And remember the last attachment as "user1-attachment"
|
||||||
|
|
||||||
|
Given acting as user "user0"
|
||||||
|
When fetching the attachment "user1-attachment" for the card "user0-card"
|
||||||
|
Then the response should have a status code 403
|
||||||
|
When deleting the attachment "user1-attachment" for the card "user0-card"
|
||||||
|
Then the response should have a status code 403
|
||||||
|
|
||||||
|
When fetching the attachment "user0-attachment" for the card "user0-card"
|
||||||
|
Then the response should have a status code 200
|
||||||
|
When deleting the attachment "user0-attachment" for the card "user0-card"
|
||||||
|
Then the response should have a status code 200
|
||||||
|
|
||||||
|
Given acting as user "user1"
|
||||||
|
When deleting the attachment "user1-attachment" for the card "user1-card"
|
||||||
|
Then the response should have a status code 200
|
||||||
|
|||||||
Reference in New Issue
Block a user