diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
index 5476e546..1fcfd9a0 100644
--- a/.github/workflows/integration.yml
+++ b/.github/workflows/integration.yml
@@ -27,6 +27,7 @@ jobs:
databases: ['sqlite', 'mysql', 'pgsql']
server-versions: ['stable31']
richdocuments-versions: ['stable31']
+ groupfolders-versions: ['stable31']
primary-storage: ['local', 'minio']
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}-${{ matrix.primary-storage}}
@@ -36,11 +37,11 @@ jobs:
env:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
- image: ghcr.io/nextcloud/continuous-integration-minio:latest
+ image: ghcr.io/nextcloud/continuous-integration-minio:latest # zizmor: ignore[unpinned-images]
ports:
- "9000:9000"
postgres:
- image: ghcr.io/nextcloud/continuous-integration-postgres-14:latest
+ image: ghcr.io/nextcloud/continuous-integration-postgres-14:latest # zizmor: ignore[unpinned-images]
ports:
- 4445:5432/tcp
env:
@@ -49,7 +50,7 @@ jobs:
POSTGRES_DB: nextcloud
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5
mysql:
- image: ghcr.io/nextcloud/continuous-integration-mariadb-10.6:latest
+ image: ghcr.io/nextcloud/continuous-integration-mariadb-10.6:latest # zizmor: ignore[unpinned-images]
ports:
- 4444:3306/tcp
env:
@@ -83,6 +84,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2.31.1
with:
@@ -108,6 +117,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud for S3 primary storage
if: matrix.primary-storage == 'minio'
run: |
diff --git a/.github/workflows/phpunit-mariadb.yml b/.github/workflows/phpunit-mariadb.yml
index 0aec893c..933a591b 100644
--- a/.github/workflows/phpunit-mariadb.yml
+++ b/.github/workflows/phpunit-mariadb.yml
@@ -72,6 +72,7 @@ jobs:
server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }}
mariadb-versions: ['10.6', '11.4']
richdocuments-versions: ['stable31']
+ groupfolders-versions: ['stable31']
name: MariaDB ${{ matrix.mariadb-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }}
@@ -113,6 +114,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
@@ -149,6 +158,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud
env:
DB_PORT: 4444
diff --git a/.github/workflows/phpunit-mysql.yml b/.github/workflows/phpunit-mysql.yml
index f31f17d9..e9f2051f 100644
--- a/.github/workflows/phpunit-mysql.yml
+++ b/.github/workflows/phpunit-mysql.yml
@@ -32,7 +32,7 @@ jobs:
id: versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
with:
- matrix: '{"mysql-versions": ["8.4"], "richdocuments-versions": ["stable31"]}'
+ matrix: '{"mysql-versions": ["8.4"], "richdocuments-versions": ["stable31"], "groupfolders-versions": ["stable31"]}'
changes:
runs-on: ubuntu-latest-low
@@ -110,6 +110,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
@@ -146,6 +154,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud
env:
DB_PORT: 4444
diff --git a/.github/workflows/phpunit-oci.yml b/.github/workflows/phpunit-oci.yml
index 06174594..cf3193e1 100644
--- a/.github/workflows/phpunit-oci.yml
+++ b/.github/workflows/phpunit-oci.yml
@@ -71,6 +71,7 @@ jobs:
php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }}
server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }}
richdocuments-versions: ['stable31']
+ groupfolders-versions: ['stable31']
oci-versions: ['18', '21', '23']
name: OCI ${{ matrix.oci-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }}
@@ -123,6 +124,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
@@ -154,6 +163,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud
env:
DB_PORT: 1521
diff --git a/.github/workflows/phpunit-pgsql.yml b/.github/workflows/phpunit-pgsql.yml
index 67f7ce4b..f04533cd 100644
--- a/.github/workflows/phpunit-pgsql.yml
+++ b/.github/workflows/phpunit-pgsql.yml
@@ -71,6 +71,7 @@ jobs:
php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }}
server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }}
richdocuments-versions: ['stable31']
+ groupfolders-versions: ['stable31']
name: PostgreSQL PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }}
@@ -114,6 +115,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
@@ -145,6 +154,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud
env:
DB_PORT: 4444
diff --git a/.github/workflows/phpunit-sqlite.yml b/.github/workflows/phpunit-sqlite.yml
index 7d1bafda..617edab2 100644
--- a/.github/workflows/phpunit-sqlite.yml
+++ b/.github/workflows/phpunit-sqlite.yml
@@ -71,6 +71,7 @@ jobs:
php-versions: ${{ fromJson(needs.matrix.outputs.php-version) }}
server-versions: ${{ fromJson(needs.matrix.outputs.server-max) }}
richdocuments-versions: ['stable31']
+ groupfolders-versions: ['stable31']
name: SQLite PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }}
@@ -103,6 +104,14 @@ jobs:
repository: nextcloud/richdocuments
ref: ${{ matrix.richdocuments-versions }}
+ - name: Checkout app (groupfolders)
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+ path: apps/groupfolders
+ repository: nextcloud/groupfolders
+ ref: ${{ matrix.groupfolders-versions }}
+
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
@@ -134,6 +143,10 @@ jobs:
working-directory: apps/richdocuments
run: composer i --no-dev
+ - name: Set up dependencies (groupfolders)
+ working-directory: apps/groupfolders
+ run: composer i --no-dev
+
- name: Set up Nextcloud
env:
DB_PORT: 4444
diff --git a/lib/Operation.php b/lib/Operation.php
index 769ef4d8..a240612f 100644
--- a/lib/Operation.php
+++ b/lib/Operation.php
@@ -12,6 +12,7 @@
use OC\Files\FileInfo;
use OC\Files\Node\Folder;
use OC\Files\View;
+use OCA\GroupFolders\Mount\GroupMountPoint;
use OCA\WorkflowEngine\Entity\File;
use OCP\EventDispatcher\Event;
use OCP\Files\Cache\ICacheEntry;
@@ -62,7 +63,7 @@ public function checkFileAccess(string $path, IMountPoint $mountPoint, bool $isD
$this->nestingLevel++;
- $filePath = $this->translatePath($storage, $path);
+ $filePath = $this->translatePath($mountPoint, $storage, $path);
$ruleMatcher = $this->manager->getRuleMatcher();
$ruleMatcher->setFileInfo($storage, $filePath, $isDir);
$node = $this->getNode($path, $mountPoint, $cacheEntry);
@@ -119,8 +120,31 @@ protected function isBlockablePath(IMountPoint $mountPoint, string $path): bool
/**
* For thumbnails and versions we want to check the tags of the original file
*/
- protected function translatePath(IStorage $storage, string $path): string {
- if (substr_count($path, '/') < 1) {
+ protected function translatePath(IMountPoint $mountPoint, IStorage $storage, string $path): string {
+ if ($mountPoint instanceof GroupMountPoint) {
+ /**
+ * Case | Mount point path | Path ($path)
+ * --------+---------------------------------------+--------------------------------
+ * Files | /user/files/$folderName/ | Subfolder/File.txt
+ * Trash | /user/files_trashbin/groupfolder/$id/ | Subfolder/File.txt.v{timestamp}
+ * Version | /user/files_versions/groupfolder/$id/ | Subfolder/File.txt.d{timestamp}
+ */
+ $mountPath = $mountPoint->getMountPoint();
+ if (substr_count($mountPath, '/') >= 3) {
+ [,, $folder] = explode('/', $mountPath);
+ if ($folder === 'files_versions' && preg_match('/.+\.v\d{10}$/', basename($path))) {
+ // Remove trailing ".v{timestamp}"
+ return substr($path, 0, -12);
+ }
+ if ($folder === 'files_trashbin' && preg_match('/.+\.d\d{10}$/', basename($path))) {
+ // Remove trailing ".d{timestamp}"
+ return substr($path, 0, -12);
+ }
+ }
+ return $path;
+ }
+
+ if (substr_count($path, '/') === 0) {
return $path;
}
@@ -138,7 +162,7 @@ protected function translatePath(IStorage $storage, string $path): string {
// 'versions', 'path/to/file.txt'
$segments = explode('/', $innerPath, 2);
if (isset($segments[1])) {
- $innerPath = $segments[1];
+ [$folder, $innerPath] = $segments;
}
if (preg_match('/.+\.d\d{10}$/', basename($innerPath))) {
@@ -176,6 +200,10 @@ protected function isCreatingSkeletonFiles(): bool {
isset($step['function']) && $step['function'] === 'tryLogin') {
return true;
}
+ if (isset($step['class']) && $step['class'] === \OCA\GroupFolders\Trash\TrashBackend::class
+ && isset($step['function']) && $step['function'] === 'setupTrashFolder') {
+ return true;
+ }
}
return false;
diff --git a/psalm.xml b/psalm.xml
index 82e912be..4ca98817 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -31,5 +31,6 @@
+
diff --git a/tests/Integration/features/bootstrap/CommandLineTrait.php b/tests/Integration/features/bootstrap/CommandLineTrait.php
new file mode 100644
index 00000000..574f9375
--- /dev/null
+++ b/tests/Integration/features/bootstrap/CommandLineTrait.php
@@ -0,0 +1,217 @@
+ escapeshellarg($arg), $args);
+ $args[] = '--no-ansi';
+ $argString = implode(' ', $args);
+
+ $descriptor = [
+ 0 => ['pipe', 'r'],
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ];
+ $process = proc_open('php console.php ' . $argString, $descriptor, $pipes, $this->ocPath, $env);
+ $this->lastStdOut = stream_get_contents($pipes[1]);
+ $this->lastStdErr = stream_get_contents($pipes[2]);
+ $this->lastCode = proc_close($process);
+
+ if ($clearOpcodeCache) {
+ // Clean opcode cache
+ $client = new GuzzleHttp\Client();
+
+ if ($this->currentServer === 'REMOTE') {
+ $client->request('GET', $this->remoteServerUrl . 'apps/testing/clean_opcode_cache.php');
+ } else {
+ $client->request('GET', $this->localServerUrl . 'apps/testing/clean_opcode_cache.php');
+ }
+ }
+
+ return $this->lastCode;
+ }
+
+ /**
+ * @Given /^invoking occ with "([^"]*)"$/
+ */
+ public function invokingTheCommand(string $cmd, ?\Behat\Gherkin\Node\TableNode $table = null) {
+ if ($cmd !== 'table') {
+ if (str_contains($cmd, '{LAST_COMMAND_OUTPUT}')) {
+ echo 'Replacing {LAST_COMMAND_OUTPUT} with "' . trim($this->lastStdOut) . '"';
+ }
+ $cmd = str_replace('{LAST_COMMAND_OUTPUT}', trim($this->lastStdOut), $cmd);
+ $args = explode(' ', $cmd);
+ } else {
+ $args = [];
+ foreach ($table->getRows() as $row) {
+ if ($row[0] === '') {
+ $args[] = $row[1];
+ } elseif ($row[1] === '') {
+ $args[] = $row[0];
+ } else {
+ $args[] = $row[0] . '=' . $row[1];
+ }
+ }
+ }
+
+ $this->runOcc($args);
+ }
+
+ public function getLastStdOut(): string {
+ return $this->lastStdOut;
+ }
+
+ /**
+ * Find exception texts in stderr
+ */
+ public function findExceptions() {
+ $exceptions = [];
+ $captureNext = false;
+ // the exception text usually appears after an "[Exception"] row
+ foreach (explode("\n", $this->lastStdErr) as $line) {
+ if (preg_match('/\[Exception\]/', $line)) {
+ $captureNext = true;
+ continue;
+ }
+ if ($captureNext) {
+ $exceptions[] = trim($line);
+ $captureNext = false;
+ }
+ }
+
+ return $exceptions;
+ }
+
+ /**
+ * @Then /^the command was successful$/
+ */
+ public function theCommandWasSuccessful() {
+ $exceptions = $this->findExceptions();
+ if ($this->lastCode !== 0) {
+ echo $this->lastStdErr;
+
+ $msg = 'The command was not successful, exit code was ' . $this->lastCode . '.';
+ if (!empty($exceptions)) {
+ $msg .= "\n" . ' Exceptions: ' . implode(', ', $exceptions);
+ } else {
+ $msg .= "\n" . ' ' . $this->lastStdOut;
+ $msg .= "\n" . ' ' . $this->lastStdErr;
+ }
+ throw new \Exception($msg);
+ } elseif (!empty($exceptions)) {
+ $msg = 'The command was successful but triggered exceptions: ' . implode(', ', $exceptions);
+ throw new \Exception($msg);
+ }
+ }
+
+ /**
+ * @Then /^the command failed with exit code ([0-9]+)$/
+ */
+ public function theCommandFailedWithExitCode(int $exitCode) {
+ Assert::assertEquals($exitCode, $this->lastCode, 'The commands exit code did not match');
+ }
+
+ /**
+ * @Then /^the command failed with exception text "([^"]*)"$/
+ */
+ public function theCommandFailedWithException($exceptionText) {
+ $exceptions = $this->findExceptions();
+ if (empty($exceptions)) {
+ throw new \Exception('The command did not throw any exceptions');
+ }
+
+ if (!in_array($exceptionText, $exceptions)) {
+ throw new \Exception('The command did not throw any exception with the text "' . $exceptionText . '"');
+ }
+ }
+
+ /**
+ * @Then /^the command output contains the text:$/
+ * @Then /^the command output contains the text "([^"]*)"$/
+ */
+ public function theCommandOutputContainsTheText($text) {
+ if ($this->lastStdOut === '' && $this->lastStdErr !== '') {
+ Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stdout');
+ Assert::assertTrue(false, 'The command did not output the expected text on stdout but stderr');
+ }
+
+ Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout');
+ }
+
+ /**
+ * @Then /^the command output is empty$/
+ */
+ public function theCommandOutputIsEmpty() {
+ Assert::assertEmpty($this->lastStdOut, 'The command did output unexpected text on stdout');
+ }
+
+ /**
+ * @Then /^the command output contains the list entry '([^']*)' with value '([^']*)'$/
+ */
+ public function theCommandOutputContainsTheListEntry(string $key, string $value): void {
+ if (preg_match('/^"ROOM\(([^"]+)\)"$/', $key, $matches)) {
+ $key = '"' . self::$identifierToToken[$matches[1]] . '"';
+ }
+ $text = '- ' . $key . ': ' . $value;
+
+ if ($this->lastStdOut === '' && $this->lastStdErr !== '') {
+ Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stdout');
+ Assert::assertTrue(false, 'The command did not output the expected text on stdout but stderr');
+ }
+
+ Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout');
+ }
+
+ /**
+ * @Then /^the command error output contains the text "([^"]*)"$/
+ */
+ public function theCommandErrorOutputContainsTheText($text) {
+ if ($this->lastStdErr === '' && $this->lastStdOut !== '') {
+ Assert::assertStringContainsString($text, $this->lastStdOut, 'The command did not output the expected text on stdout');
+ Assert::assertTrue(false, 'The command did not output the expected text on stdout but stderr');
+ }
+
+ Assert::assertStringContainsString($text, $this->lastStdErr, 'The command did not output the expected text on stderr');
+ }
+}
diff --git a/tests/Integration/features/bootstrap/FeatureContext.php b/tests/Integration/features/bootstrap/FeatureContext.php
index be307711..c5733f0c 100644
--- a/tests/Integration/features/bootstrap/FeatureContext.php
+++ b/tests/Integration/features/bootstrap/FeatureContext.php
@@ -13,6 +13,11 @@
use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\TableNode;
+use Behat\Hook\AfterScenario;
+use Behat\Hook\BeforeScenario;
+use Behat\Step\Given;
+use Behat\Step\Then;
+use Behat\Step\When;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\ClientException;
@@ -26,6 +31,7 @@
*/
class FeatureContext implements Context {
use WebDav;
+ use CommandLineTrait;
protected $regularUser = '123456';
protected $adminUser = ['admin', 'admin'];
@@ -44,6 +50,8 @@ class FeatureContext implements Context {
protected string $tagId = '';
protected array $createdUsers = [];
+ protected array $createdGroups = [];
+ protected array $createdGroupFolders = [];
protected array $changedConfigs = [];
@@ -55,10 +63,8 @@ public function __construct() {
$this->baseUrl = getenv('TEST_SERVER_URL');
}
- /**
- * @BeforeScenario
- * @AfterScenario
- */
+ #[BeforeScenario]
+ #[AfterScenario]
public function cleanUpBetweenTests() {
$this->setCurrentUser('admin');
$this->sendingTo('DELETE', '/apps/files_accesscontrol_testing');
@@ -71,18 +77,21 @@ public function cleanUpBetweenTests() {
}
}
- /**
- * @AfterScenario
- */
+ #[AfterScenario]
public function tearDown() {
foreach ($this->createdUsers as $user) {
$this->deleteUser($user);
}
+ foreach ($this->createdGroups as $group) {
+ $this->deleteGroup($group);
+ }
+ foreach ($this->createdGroupFolders as $folderId) {
+ $this->runOcc(['groupfolders:delete', '--force', $folderId]);
+ $this->theCommandWasSuccessful();
+ }
}
- /**
- * @Given /^Ensure tag exists$/
- */
+ #[Given(pattern: 'Ensure tag exists')]
public function createTag() {
$this->setCurrentUser('admin');
$this->sendingTo('POST', '/apps/files_accesscontrol_testing');
@@ -93,9 +102,7 @@ public function createTag() {
$this->tagId = $data['tagId'];
}
- /**
- * @Given /^user "([^"]*)" tags file "([^"]*)"$/
- */
+ #[Given(pattern: '/^user "([^"]*)" tags file "([^"]*)"$/')]
public function tagFile(string $user, string $path) {
// TODO: Remove all created tags?
$this->setCurrentUser($user);
@@ -105,9 +112,7 @@ public function tagFile(string $user, string $path) {
$this->assertStatusCode($this->response, 200);
}
- /**
- * @Given /^user "([^"]*)" creates (global|user) flow with (\d+)$/
- */
+ #[Given(pattern: '/^user "([^"]*)" creates (global|user) flow with (\d+)$/')]
public function createFlow(string $user, string $scope, int $statusCode, TableNode $tableNode) {
$this->setCurrentUser($user);
@@ -128,9 +133,7 @@ public function createFlow(string $user, string $scope, int $statusCode, TableNo
Assert::assertSame($statusCode, $this->response->getStatusCode(), 'HTTP status code mismatch:' . "\n" . $this->response->getBody()->getContents());
}
- /**
- * @Given /^user "([^"]*)" shares file "([^"]*)" with user "([^"]*)"$/
- */
+ #[Given(pattern: '/^user "([^"]*)" shares file "([^"]*)" with user "([^"]*)"$/')]
public function userSharesFile(string $sharer, string $file, string $sharee): void {
$this->setCurrentUser($sharer);
$this->sendingToWith('POST', '/apps/files_sharing/api/v1/shares', [
@@ -141,9 +144,7 @@ public function userSharesFile(string $sharer, string $file, string $sharee): vo
]);
}
- /**
- * @Given /^user "([^"]*)" shares file "([^"]*)" publicly$/
- */
+ #[Given(pattern: '/^user "([^"]*)" shares file "([^"]*)" publicly$/')]
public function userSharesFilePublicly(string $sharer, string $file): void {
$this->setCurrentUser($sharer);
$this->sendingToWith('POST', '/apps/files_sharing/api/v1/shares', [
@@ -155,13 +156,19 @@ public function userSharesFilePublicly(string $sharer, string $file): void {
$this->lastShareData = $responseBody['ocs']['data'];
}
+ #[Given(pattern: '/^setup group folder "([^"]*)" for group "([^"]*)"$/')]
+ public function createGroupFolderForGroup(string $folder, string $group): void {
+ $this->runOcc(['groupfolders:create', $folder]);
+ $this->theCommandWasSuccessful();
+ $folderId = (string)(int)$this->lastStdOut;
+ $this->createdGroupFolders[] = $folderId;
+ $this->runOcc(['groupfolders:group', $folderId, $group, 'read', 'write', 'delete']);
+ $this->theCommandWasSuccessful();
+ }
+
// ChecksumsContext
- /**
- * @Then The webdav response should have a status code :statusCode
- * @param string $statusCode
- * @throws \Exception
- */
- public function theWebdavResponseShouldHaveAStatusCode($statusCode) {
+ #[Then(pattern: 'The webdav response should have a status code :statusCode')]
+ public function theWebdavResponseShouldHaveAStatusCode(string $statusCode) {
if (str_contains($statusCode, '|')) {
$statusCodes = array_map('intval', explode('|', $statusCode));
} else {
@@ -173,7 +180,7 @@ public function theWebdavResponseShouldHaveAStatusCode($statusCode) {
}
- #[\Behat\Step\Given('the following :appId app config is set')]
+ #[Given('the following :appId app config is set')]
public function setAppConfig(string $appId, TableNode $formData): void {
$this->setCurrentUser('admin');
foreach ($formData->getRows() as $row) {
@@ -187,19 +194,14 @@ public function setAppConfig(string $appId, TableNode $formData): void {
/**
* User management
*/
-
- /**
- * @Given /^as user "([^"]*)"$/
- * @param string $user
- */
- public function setCurrentUser(string $user): void {
+ #[Given('/^as user "([^"]*)"$/')]
+ public function setCurrentUser(string $user): string {
+ $before = $this->currentUser;
$this->currentUser = $user;
+ return $before ?? 'admin';
}
- /**
- * @Given /^user "([^"]*)" exists$/
- * @param string $user
- */
+ #[Given('/^user "([^"]*)" exists$/')]
public function assureUserExists(string $user): void {
try {
$this->userExists($user);
@@ -271,26 +273,60 @@ private function deleteUser(string $user): void {
$this->currentUser = $previous_user;
}
+ #[Given('/^group "([^"]*)" exists$/')]
+ public function assureGroupExists(string $group): void {
+ $currentUser = $this->setCurrentUser('admin');
+ $this->sendingToWith('POST', '/cloud/groups', [
+ 'groupid' => $group,
+ ]);
+
+ $jsonBody = json_decode($this->response->getBody()->getContents(), true);
+ if (isset($jsonBody['ocs']['meta'])) {
+ // 102 = group exists
+ // 200 = created with success
+ Assert::assertContains(
+ $jsonBody['ocs']['meta']['statuscode'],
+ [102, 200],
+ $jsonBody['ocs']['meta']['message']
+ );
+ } else {
+ throw new \Exception('Invalid response when create group');
+ }
+
+ $this->setCurrentUser($currentUser);
+
+ $this->createdGroups[$group] = $group;
+ }
+
+ private function deleteGroup(string $group): void {
+ $currentUser = $this->setCurrentUser('admin');
+ $this->sendingTo('DELETE', '/cloud/groups/' . $group);
+ $this->setCurrentUser($currentUser);
+
+ unset($this->createdGroups[$group]);
+ $this->setCurrentUser($currentUser);
+ }
+
+ #[When('/^user "([^"]*)" is member of group "([^"]*)"$/')]
+ public function addingUserToGroup(string $user, string $group): void {
+ $currentUser = $this->setCurrentUser('admin');
+ $this->sendingToWith('POST', "/cloud/users/$user/groups", [
+ 'groupid' => $group,
+ ]);
+ $this->assertStatusCode($this->response, 200);
+ $this->setCurrentUser($currentUser);
+ }
+
/*
* Requests
*/
- /**
- * @When /^sending "([^"]*)" to "([^"]*)"$/
- * @param string $verb
- * @param string $url
- */
+ #[When(pattern: '/^sending "([^"]*)" to "([^"]*)"$/')]
public function sendingTo(string $verb, string $url): void {
- $this->sendingToWith($verb, $url, null);
+ $this->sendingToWith($verb, $url);
}
- /**
- * @When /^sending "([^"]*)" to "([^"]*)" with$/
- * @param string $verb
- * @param string $url
- * @param array|null $body
- * @param array $headers
- */
+ #[When(pattern: '/^sending "([^"]*)" to "([^"]*)" with$/')]
public function sendingToWith(string $verb, string $url, ?array $body = null, array $headers = []): void {
$fullUrl = $this->baseUrl . 'ocs/v2.php' . $url;
$client = new Client();
@@ -320,10 +356,6 @@ public function sendingToWith(string $verb, string $url, ?array $body = null, ar
}
}
- /**
- * @param ResponseInterface $response
- * @param int $statusCode
- */
protected function assertStatusCode(ResponseInterface $response, int $statusCode): void {
Assert::assertEquals($statusCode, $response->getStatusCode());
}
diff --git a/tests/Integration/features/mimetypes.feature b/tests/Integration/features/mimetypes.feature
index ec39d98f..daa26dda 100644
--- a/tests/Integration/features/mimetypes.feature
+++ b/tests/Integration/features/mimetypes.feature
@@ -98,3 +98,20 @@
| checks-1 | {"class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType", "operator": "!is", "value": "text/plain"} |
When User "test1" deletes file "/foobar.txt"
Then The webdav response should have a status code "204"
+
+ Scenario: Blocking by mimetype works in trashbin of groupfolders
+ Given group "group886" exists
+ And user "test1" is member of group "group886"
+ And setup group folder "Folder886" for group "group886"
+ Given User "test1" uploads file "data/textfile.txt" to "/Folder886/foobar.txt"
+ Then The webdav response should have a status code "201"
+ And user "admin" creates global flow with 200
+ | name | Admin flow |
+ | class | OCA\FilesAccessControl\Operation |
+ | entity | OCA\WorkflowEngine\Entity\File |
+ | events | [] |
+ | operation | deny |
+ | checks-0 | {"class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType", "operator": "!is", "value": "httpd/unix-directory"} |
+ | checks-1 | {"class":"OCA\\\\WorkflowEngine\\\\Check\\\\FileMimeType", "operator": "!is", "value": "text/plain"} |
+ When User "test1" deletes file "/Folder886/foobar.txt"
+ Then The webdav response should have a status code "204"
diff --git a/tests/Integration/run.sh b/tests/Integration/run.sh
index e6ab34b1..bf8b2871 100755
--- a/tests/Integration/run.sh
+++ b/tests/Integration/run.sh
@@ -17,6 +17,7 @@ cp -R ./app "../../../${APP_NAME}_testing"
${ROOT_DIR}/occ app:enable $APP_NAME
${ROOT_DIR}/occ app:enable --force "${APP_NAME}_testing"
${ROOT_DIR}/occ app:enable --force richdocuments
+${ROOT_DIR}/occ app:enable --force groupfolders
${ROOT_DIR}/occ app:list | grep $APP_NAME
export TEST_SERVER_URL="http://localhost:8080/"
diff --git a/tests/stubs/oca_groupfolders.php b/tests/stubs/oca_groupfolders.php
new file mode 100644
index 00000000..238d2837
--- /dev/null
+++ b/tests/stubs/oca_groupfolders.php
@@ -0,0 +1,18 @@
+