From cef928972c5d788d3cce21f9ff602d1d5d51f2c4 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 21 Aug 2024 05:11:31 +0200 Subject: [PATCH 01/12] fix: Do not DI the database connection to prevent cyclic dependency Signed-off-by: Ferdinand Thiessen --- lib/private/Files/FilenameValidator.php | 8 ++++++- tests/lib/Files/FilenameValidatorTest.php | 29 ++++++++++++++--------- tests/lib/TestCase.php | 7 +----- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/private/Files/FilenameValidator.php b/lib/private/Files/FilenameValidator.php index 2fe3c93d02664..5972fdb9af1e1 100644 --- a/lib/private/Files/FilenameValidator.php +++ b/lib/private/Files/FilenameValidator.php @@ -27,6 +27,8 @@ class FilenameValidator implements IFilenameValidator { private IL10N $l10n; + private ?IDBConnection $database; + /** * @var list */ @@ -48,7 +50,6 @@ class FilenameValidator implements IFilenameValidator { public function __construct( IFactory $l10nFactory, - private IDBConnection $database, private IConfig $config, private LoggerInterface $logger, ) { @@ -187,6 +188,11 @@ public function validateFilename(string $filename): void { throw new FileNameTooLongException(); } + // We need to lazy load the database as otherwise there is a cyclic dependency + if (!isset($this->database)) { + $this->database = \OCP\Server::get(IDBConnection::class); + } + if (!$this->database->supports4ByteText()) { // verify database - e.g. mysql only 3-byte chars if (preg_match('%(?: diff --git a/tests/lib/Files/FilenameValidatorTest.php b/tests/lib/Files/FilenameValidatorTest.php index ac9ac032b6435..09e6067c8c0db 100644 --- a/tests/lib/Files/FilenameValidatorTest.php +++ b/tests/lib/Files/FilenameValidatorTest.php @@ -24,12 +24,16 @@ use Psr\Log\LoggerInterface; use Test\TestCase; +/** + * @group DB + */ class FilenameValidatorTest extends TestCase { protected IFactory&MockObject $l10n; protected IConfig&MockObject $config; protected IDBConnection&MockObject $database; protected LoggerInterface&MockObject $logger; + protected bool $dbSupportsUtf8 = true; protected function setUp(): void { parent::setUp(); @@ -45,7 +49,13 @@ protected function setUp(): void { $this->config = $this->createMock(IConfig::class); $this->logger = $this->createMock(LoggerInterface::class); $this->database = $this->createMock(IDBConnection::class); - $this->database->method('supports4ByteText')->willReturn(true); + $this->database->method('supports4ByteText')->willReturnCallback(fn () => $this->dbSupportsUtf8); + $this->overwriteService(IDBConnection::class, $this->database); + } + + protected function tearDown(): void { + $this->restoreAllServices(); + parent::tearDown(); } /** @@ -67,7 +77,7 @@ public function testValidateFilename( 'getForbiddenExtensions', 'getForbiddenFilenames', ]) - ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->setConstructorArgs([$this->l10n, $this->config, $this->logger]) ->getMock(); $validator->method('getForbiddenBasenames') @@ -106,7 +116,7 @@ public function testIsFilenameValid( 'getForbiddenFilenames', 'getForbiddenCharacters', ]) - ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->setConstructorArgs([$this->l10n, $this->config, $this->logger]) ->getMock(); $validator->method('getForbiddenBasenames') @@ -186,12 +196,10 @@ public function dataValidateFilename(): array { * @dataProvider data4ByteUnicode */ public function testDatabaseDoesNotSupport4ByteText($filename): void { - $database = $this->createMock(IDBConnection::class); - $database->expects($this->once()) - ->method('supports4ByteText') - ->willReturn(false); + $this->dbSupportsUtf8 = false; + $this->expectException(InvalidCharacterInPathException::class); - $validator = new FilenameValidator($this->l10n, $database, $this->config, $this->logger); + $validator = new FilenameValidator($this->l10n, $this->config, $this->logger); $validator->validateFilename($filename); } @@ -199,7 +207,6 @@ public function data4ByteUnicode(): array { return [ ['plane 1 𐪅'], ['emoji 😶‍🌫️'], - ]; } @@ -208,7 +215,7 @@ public function data4ByteUnicode(): array { */ public function testInvalidAsciiCharactersAreAlwaysForbidden(string $filename): void { $this->expectException(InvalidPathException::class); - $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $validator = new FilenameValidator($this->l10n, $this->config, $this->logger); $validator->validateFilename($filename); } @@ -256,7 +263,7 @@ public function testIsForbidden(string $filename, array $forbiddenNames, bool $e /** @var FilenameValidator&MockObject */ $validator = $this->getMockBuilder(FilenameValidator::class) ->onlyMethods(['getForbiddenFilenames']) - ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->setConstructorArgs([$this->l10n, $this->config, $this->logger]) ->getMock(); $validator->method('getForbiddenFilenames') diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php index 1594bced7c77c..125100eecbc6d 100644 --- a/tests/lib/TestCase.php +++ b/tests/lib/TestCase.php @@ -508,11 +508,6 @@ protected function getGroupAnnotations(): array { } protected function IsDatabaseAccessAllowed() { - // on travis-ci.org we allow database access in any case - otherwise - // this will break all apps right away - if (getenv('TRAVIS') == true) { - return true; - } $annotations = $this->getGroupAnnotations(); if (isset($annotations)) { if (in_array('DB', $annotations) || in_array('SLOWDB', $annotations)) { @@ -548,7 +543,7 @@ protected function assertTemplate($expectedHtml, $template, $vars = []) { return vsprintf($text, $parameters); }); - $t = new Base($template, $requestToken, $l10n, $theme); + $t = new Base($template, $requestToken, $l10n, $theme, base64_encode('csp-nonce')); $buf = $t->fetchPage($vars); $this->assertHtmlStringEqualsHtmlString($expectedHtml, $buf); } From f997a2196914edabd19020a2d4197ff5de4bec54 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 27 Aug 2024 01:05:12 +0200 Subject: [PATCH 02/12] fix(files): Move requires DELETE permissions Signed-off-by: Ferdinand Thiessen --- apps/files/src/actions/moveOrCopyActionUtils.ts | 2 +- apps/files/src/actions/renameAction.spec.ts | 4 ++-- apps/files/src/actions/renameAction.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/files/src/actions/moveOrCopyActionUtils.ts b/apps/files/src/actions/moveOrCopyActionUtils.ts index d2e276b2b93ff..0c7822390ac93 100644 --- a/apps/files/src/actions/moveOrCopyActionUtils.ts +++ b/apps/files/src/actions/moveOrCopyActionUtils.ts @@ -38,7 +38,7 @@ export type MoveCopyResult = { export const canMove = (nodes: Node[]) => { const minPermission = nodes.reduce((min, node) => Math.min(min, node.permissions), Permission.ALL) - return (minPermission & Permission.UPDATE) !== 0 + return Boolean(minPermission & Permission.DELETE) } export const canDownload = (nodes: Node[]) => { diff --git a/apps/files/src/actions/renameAction.spec.ts b/apps/files/src/actions/renameAction.spec.ts index b309c11d9a66a..954eca5820f0b 100644 --- a/apps/files/src/actions/renameAction.spec.ts +++ b/apps/files/src/actions/renameAction.spec.ts @@ -30,14 +30,14 @@ describe('Rename action enabled tests', () => { source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', owner: 'admin', mime: 'text/plain', - permissions: Permission.UPDATE, + permissions: Permission.UPDATE | Permission.DELETE, }) expect(action.enabled).toBeDefined() expect(action.enabled!([file], view)).toBe(true) }) - test('Disabled for node without UPDATE permission', () => { + test('Disabled for node without DELETE permission', () => { const file = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', diff --git a/apps/files/src/actions/renameAction.ts b/apps/files/src/actions/renameAction.ts index c00a99b4de10f..e4dbb0ed129d2 100644 --- a/apps/files/src/actions/renameAction.ts +++ b/apps/files/src/actions/renameAction.ts @@ -17,7 +17,7 @@ export const action = new FileAction({ enabled: (nodes: Node[]) => { return nodes.length > 0 && nodes .map(node => node.permissions) - .every(permission => (permission & Permission.UPDATE) !== 0) + .every(permission => Boolean(permission & Permission.DELETE)) }, async exec(node: Node) { From b5552b984ff859135eae3ff33e9ec4d547481a45 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 17:36:53 +0200 Subject: [PATCH 03/12] fixup --- apps/dav/lib/Connector/Sabre/Directory.php | 2 +- lib/private/Files/View.php | 48 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index 503acf8dae52a..c1b69323a44e4 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -173,7 +173,7 @@ public function getChild($name, $info = null, ?IRequest $request = null, ?IL10N $path = $this->path . '/' . $name; if (is_null($info)) { try { - $this->fileView->verifyPath($this->path, $name); + $this->fileView->verifyPath($this->path, $name, true); $info = $this->fileView->getFileInfo($path); } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), 0, $e); diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 494b4493d2cba..3fdc51f9928c1 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -57,6 +57,7 @@ class View { private bool $lockingEnabled; private bool $updaterEnabled = true; private UserManager $userManager; + private FilenameValidator $filenameValidator; private LoggerInterface $logger; /** @@ -71,6 +72,7 @@ public function __construct(string $root = '') { $this->lockingProvider = \OC::$server->get(ILockingProvider::class); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); $this->userManager = \OC::$server->getUserManager(); + $this->filenameValidator = \OCP\Server::get(FilenameValidator::class); $this->logger = \OC::$server->get(LoggerInterface::class); } @@ -1097,7 +1099,7 @@ public function free_space($path = '/') { * @throws LockedException * * This method takes requests for basic filesystem functions (e.g. reading & writing - * files), processes hooks and proxies, sanitises paths, and finally passes them on to + * files), processes hooks and proxies, sanitizes paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation(string $operation, string $path, array $hooks = [], $extraParam = null) { @@ -1355,11 +1357,18 @@ public function getFileInfo($path, $includeMountPoints = true) { return false; } + if ($internalPath === '' && $data['name']) { + $data['name'] = basename($path); + } if ($mount instanceof MoveableMount && $internalPath === '') { $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } - if ($internalPath === '' && $data['name']) { - $data['name'] = basename($path); + // Ensure that parent path is valid (so it is writable) + try { + $this->verifyPath(dirname($path), basename($path)); + } catch (InvalidPathException) { + // Not a valid parent path so remove update and create permissions + $data['permissions'] &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE); } $ownerId = $storage->getOwner($internalPath); @@ -1439,6 +1448,12 @@ public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Fil $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); + $folderIsReadonly = false; + try { + $this->verifyPath(dirname($directory), basename($directory)); + } catch (InvalidPathException) { + $folderIsReadonly = true; + } $fileNames = array_map(function (ICacheEntry $content) { return $content->getName(); @@ -1446,10 +1461,13 @@ public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Fil /** * @var \OC\Files\FileInfo[] $fileInfos */ - $fileInfos = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { + $fileInfos = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled, $folderIsReadonly) { if ($sharingDisabled) { $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } + if ($folderIsReadonly) { + $content['permissions'] = $content['permissions'] & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE); + } $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); }, $contents); @@ -1517,6 +1535,9 @@ public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Fil if ($sharingDisabled) { $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } + if ($folderIsReadonly) { + $rootEntry['permissions'] = $rootEntry['permissions'] & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE); + } $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); $files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); @@ -1826,28 +1847,39 @@ private function getPartFileInfo(string $path): \OC\Files\FileInfo { /** * @param string $path * @param string $fileName + * @param bool $readonly If the path should be verified for read-only operations * @throws InvalidPathException */ - public function verifyPath($path, $fileName): void { + public function verifyPath($path, $fileName, bool $readonly = false): void { // All of the view's functions disallow '..' in the path so we can short cut if the path is invalid if (!Filesystem::isValidPath($path ?: '/')) { $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('Path contains invalid segments')); } + // Handle filename validation for read-only operations, as we still allow to rename invalid files. + if ($readonly) { + // There is one exception: Forbidden files like `.htaccess` must not be accessible + if ($this->filenameValidator->isForbidden($fileName)) { + $l = \OCP\Util::getL10N('lib'); + throw new InvalidPathException($l->t('Filename is a reserved word')); + } + return; + } + try { /** @type \OCP\Files\Storage $storage */ [$storage, $internalPath] = $this->resolvePath($path); $storage->verifyPath($internalPath, $fileName); } catch (ReservedWordException $ex) { $l = \OCP\Util::getL10N('lib'); - throw new InvalidPathException($l->t('File name is a reserved word')); + throw new InvalidPathException($ex->getMessage() ?: $l->t('Filename is a reserved word')); } catch (InvalidCharacterInPathException $ex) { $l = \OCP\Util::getL10N('lib'); - throw new InvalidPathException($l->t('File name contains at least one invalid character')); + throw new InvalidPathException($ex->getMessage() ?: $l->t('Filename contains at least one invalid character')); } catch (FileNameTooLongException $ex) { $l = \OCP\Util::getL10N('lib'); - throw new InvalidPathException($l->t('File name is too long')); + throw new InvalidPathException($l->t('Filename is too long')); } catch (InvalidDirectoryException $ex) { $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('Dot files are not allowed')); From 8aa52f52cc32cdaf7dd717269aafa0c0de947c68 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 17:41:20 +0200 Subject: [PATCH 04/12] fixup: Remove workflows not needed for testing --- .github/workflows/autocheckers.yml | 98 ---------- .github/workflows/block-merge-eol.yml | 40 ---- .github/workflows/block-merge-freeze.yml | 35 ---- .github/workflows/block-outdated-3rdparty.yml | 55 ------ .../block-unconventional-commits.yml | 34 ---- .github/workflows/command-compile.yml | 179 ----------------- .github/workflows/command-pull-3rdparty.yml | 68 ------- .github/workflows/cypress.yml | 184 ------------------ .../workflows/dependabot-approve-merge.yml | 49 ----- .github/workflows/files-external-ftp.yml | 121 ------------ .github/workflows/files-external-sftp.yml | 111 ----------- .../workflows/files-external-smb-kerberos.yml | 95 --------- .github/workflows/files-external-smb.yml | 110 ----------- .github/workflows/files-external-webdav.yml | 107 ---------- .github/workflows/files-external.yml | 95 --------- .github/workflows/fixup.yml | 36 ---- .github/workflows/integration-litmus.yml | 112 ----------- .github/workflows/integration-s3-primary.yml | 119 ----------- .github/workflows/lint-eslint.yml | 95 --------- .github/workflows/lint-php-cs.yml | 82 -------- .github/workflows/lint-php.yml | 82 -------- .github/workflows/node-test.yml | 173 ---------------- .github/workflows/node.yml | 105 ---------- .github/workflows/npm-audit-fix.yml | 75 ------- .github/workflows/object-storage-azure.yml | 127 ------------ .github/workflows/object-storage-s3.yml | 133 ------------- .github/workflows/object-storage-swift.yml | 123 ------------ .github/workflows/openapi.yml | 45 ----- .github/workflows/performance.yml | 120 ------------ .github/workflows/phpunit-32bits.yml | 66 ------- .github/workflows/phpunit-memcached.yml | 126 ------------ .github/workflows/phpunit-mysql.yml | 147 -------------- .github/workflows/phpunit-oci.yml | 151 -------------- .github/workflows/phpunit-pgsql.yml | 146 -------------- .github/workflows/pr-feedback.yml | 50 ----- .github/workflows/reuse.yml | 22 --- .github/workflows/stale.yml | 35 ---- .github/workflows/static-code-analysis.yml | 103 ---------- .github/workflows/update-cacert-bundle.yml | 45 ----- .github/workflows/update-code-signing-crl.yml | 48 ----- .../update-psalm-baseline-approve-merge.yml | 52 ----- .github/workflows/update-psalm-baseline.yml | 69 ------- 42 files changed, 3868 deletions(-) delete mode 100644 .github/workflows/autocheckers.yml delete mode 100644 .github/workflows/block-merge-eol.yml delete mode 100644 .github/workflows/block-merge-freeze.yml delete mode 100644 .github/workflows/block-outdated-3rdparty.yml delete mode 100644 .github/workflows/block-unconventional-commits.yml delete mode 100644 .github/workflows/command-compile.yml delete mode 100644 .github/workflows/command-pull-3rdparty.yml delete mode 100644 .github/workflows/cypress.yml delete mode 100644 .github/workflows/dependabot-approve-merge.yml delete mode 100644 .github/workflows/files-external-ftp.yml delete mode 100644 .github/workflows/files-external-sftp.yml delete mode 100644 .github/workflows/files-external-smb-kerberos.yml delete mode 100644 .github/workflows/files-external-smb.yml delete mode 100644 .github/workflows/files-external-webdav.yml delete mode 100644 .github/workflows/files-external.yml delete mode 100644 .github/workflows/fixup.yml delete mode 100644 .github/workflows/integration-litmus.yml delete mode 100644 .github/workflows/integration-s3-primary.yml delete mode 100644 .github/workflows/lint-eslint.yml delete mode 100644 .github/workflows/lint-php-cs.yml delete mode 100644 .github/workflows/lint-php.yml delete mode 100644 .github/workflows/node-test.yml delete mode 100644 .github/workflows/node.yml delete mode 100644 .github/workflows/npm-audit-fix.yml delete mode 100644 .github/workflows/object-storage-azure.yml delete mode 100644 .github/workflows/object-storage-s3.yml delete mode 100644 .github/workflows/object-storage-swift.yml delete mode 100644 .github/workflows/openapi.yml delete mode 100644 .github/workflows/performance.yml delete mode 100644 .github/workflows/phpunit-32bits.yml delete mode 100644 .github/workflows/phpunit-memcached.yml delete mode 100644 .github/workflows/phpunit-mysql.yml delete mode 100644 .github/workflows/phpunit-oci.yml delete mode 100644 .github/workflows/phpunit-pgsql.yml delete mode 100644 .github/workflows/pr-feedback.yml delete mode 100644 .github/workflows/reuse.yml delete mode 100644 .github/workflows/stale.yml delete mode 100644 .github/workflows/static-code-analysis.yml delete mode 100644 .github/workflows/update-cacert-bundle.yml delete mode 100644 .github/workflows/update-code-signing-crl.yml delete mode 100644 .github/workflows/update-psalm-baseline-approve-merge.yml delete mode 100644 .github/workflows/update-psalm-baseline.yml diff --git a/.github/workflows/autocheckers.yml b/.github/workflows/autocheckers.yml deleted file mode 100644 index 3233a48e9e7ca..0000000000000 --- a/.github/workflows/autocheckers.yml +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Code checkers - -on: - pull_request: - -permissions: - contents: read - -concurrency: - group: autocheckers-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src }} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - autocheckers: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - matrix: - php-versions: ['8.3'] - - name: PHP checkers - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - extensions: ctype, json, mbstring - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: Check auto loaders - run: bash ./build/autoloaderchecker.sh - - - name: Check translations are JSON decodeable - run: php ./build/translation-checker.php - - - name: Check translations do not contain triple dot but ellipsis - run: php ./build/triple-dot-checker.php - - - name: Check .htaccess does not contain invalid changes - run: php ./build/htaccess-checker.php - - - name: Check that all and only expected files are included - run: php ./build/files-checker.php - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, autocheckers] - - if: always() - - name: autocheckers-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.autocheckers.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/block-merge-eol.yml b/.github/workflows/block-merge-eol.yml deleted file mode 100644 index 292494c72cdf9..0000000000000 --- a/.github/workflows/block-merge-eol.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block merges for EOL - -on: pull_request - -permissions: - contents: read - -concurrency: - group: block-merge-eol-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-merges-eol: - name: Block merges for EOL branches - - # Only run on stableXX branches - if: startsWith( github.base_ref, 'stable') - runs-on: ubuntu-latest-low - - steps: - - name: Set server major version environment - run: | - # retrieve version number from branch reference - server_major=$(echo "${{ github.base_ref }}" | sed -En 's/stable//p') - echo "server_major=$server_major" >> $GITHUB_ENV - echo "current_month=$(date +%Y-%m)" >> $GITHUB_ENV - - - name: Checking if ${{ env.server_major }} is EOL - run: | - curl -s https://raw.githubusercontent.com/nextcloud-releases/updater_server/production/config/major_versions.json \ - | jq '.["${{ env.server_major }}"]["eol"] // "9999-99" | . >= "${{ env.current_month }}"' \ - | grep -q true diff --git a/.github/workflows/block-merge-freeze.yml b/.github/workflows/block-merge-freeze.yml deleted file mode 100644 index d052668b310ba..0000000000000 --- a/.github/workflows/block-merge-freeze.yml +++ /dev/null @@ -1,35 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block merges during freezes - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: block-merge-freeze-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-merges-during-freeze: - name: Block merges during freezes - - if: github.event.pull_request.draft == false - - runs-on: ubuntu-latest-low - - steps: - - name: Download version.php from ${{ github.base_ref }} - run: curl 'https://raw.githubusercontent.com/nextcloud/server/${{ github.base_ref }}/version.php' --output version.php - - - name: Run check - run: cat version.php | grep 'OC_VersionString' | grep -i -v 'RC' diff --git a/.github/workflows/block-outdated-3rdparty.yml b/.github/workflows/block-outdated-3rdparty.yml deleted file mode 100644 index 013a103c6349f..0000000000000 --- a/.github/workflows/block-outdated-3rdparty.yml +++ /dev/null @@ -1,55 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Block merging with outdated 3rdparty/ - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: block-outdated-3rdparty-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-outdated-3rdparty: - name: Block merging with outdated 3rdparty/ - - runs-on: ubuntu-latest-low - - steps: - - name: Check requirement - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '3rdparty' - - 'version.php' - - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: 3rdparty commit hash on current branch - id: actual - run: | - echo "commit=$(git submodule status | grep ' 3rdparty' | egrep -o '[a-f0-9]{40}')" >> "$GITHUB_OUTPUT" - - - name: Last 3rdparty commit on target branch - id: target - run: | - echo "commit=$(git ls-remote https://github.com/nextcloud/3rdparty refs/heads/${{ github.base_ref }} | awk '{ print $1}')" >> "$GITHUB_OUTPUT" - - - name: Compare if 3rdparty commits are different - run: | - echo '3rdparty/ seems to not point to the last commit of the dedicated branch:' - echo 'Branch has: ${{ steps.actual.outputs.commit }}' - echo '${{ github.base_ref }} has: ${{ steps.target.outputs.commit }}' - - - name: Fail if 3rdparty commits are different - if: ${{ steps.changes.outputs.src != 'false' && steps.actual.outputs.commit != steps.target.outputs.commit }} - run: | - exit 1 diff --git a/.github/workflows/block-unconventional-commits.yml b/.github/workflows/block-unconventional-commits.yml deleted file mode 100644 index 0e7d81efc6fab..0000000000000 --- a/.github/workflows/block-unconventional-commits.yml +++ /dev/null @@ -1,34 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block unconventional commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: block-unconventional-commits-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-unconventional-commits: - name: Block unconventional commits - - runs-on: ubuntu-latest-low - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - uses: webiny/action-conventional-commits@8bc41ff4e7d423d56fa4905f6ff79209a78776c7 # v1.3.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/command-compile.yml b/.github/workflows/command-compile.yml deleted file mode 100644 index 3d9f618612c4a..0000000000000 --- a/.github/workflows/command-compile.yml +++ /dev/null @@ -1,179 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Compile Command -on: - issue_comment: - types: [created] - -jobs: - init: - runs-on: ubuntu-latest - - # On pull requests and if the comment starts with `/compile` - if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/compile') - - outputs: - git_path: ${{ steps.git-path.outputs.path }} - arg1: ${{ steps.command.outputs.arg1 }} - arg2: ${{ steps.command.outputs.arg2 }} - head_ref: ${{ steps.comment-branch.outputs.head_ref }} - base_ref: ${{ steps.comment-branch.outputs.base_ref }} - - steps: - - name: Get repository from pull request comment - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - id: get-repository - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const pull = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number - }); - - const repositoryName = pull.data.head?.repo?.full_name - console.log(repositoryName) - return repositoryName - - - name: Disabled on forks - if: ${{ fromJSON(steps.get-repository.outputs.result) != github.repository }} - run: | - echo 'Can not execute /compile on forks' - exit 1 - - - name: Check actor permission - uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v2 - with: - require: write - - - name: Add reaction on start - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '+1' - - - name: Parse command - uses: skjnldsv/parse-command-comment@5c955203c52424151e6d0e58fb9de8a9f6a605a1 # v2 - id: command - - # Init path depending on which command is run - - name: Init path - id: git-path - run: | - if ${{ startsWith(steps.command.outputs.arg1, '/') }}; then - echo "path=${{steps.command.outputs.arg1}}" >> $GITHUB_OUTPUT - else - echo "path=${{steps.command.outputs.arg2}}" >> $GITHUB_OUTPUT - fi - - - name: Init branch - uses: xt0rted/pull-request-comment-branch@d97294d304604fa98a2600a6e2f916a84b596dc7 # v1 - id: comment-branch - - - name: Add reaction on failure - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - if: failure() - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: "-1" - - process: - runs-on: ubuntu-latest - needs: init - - steps: - - name: Restore cached git repository - uses: buildjet/cache@e376f15c6ec6dc595375c78633174c7e5f92dc0e # v3 - with: - path: .git - key: git-repo - - - name: Checkout ${{ needs.init.outputs.head_ref }} - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - fetch-depth: 0 - ref: ${{ needs.init.outputs.head_ref }} - - - name: Setup git - run: | - git config --local user.email 'nextcloud-command@users.noreply.github.com' - git config --local user.name 'nextcloud-command' - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: package-engines-versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.package-engines-versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v3 - with: - node-version: ${{ steps.package-engines-versions.outputs.nodeVersion }} - cache: npm - - - name: Set up npm ${{ steps.package-engines-versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.package-engines-versions.outputs.npmVersion }}' - - - name: Rebase to ${{ needs.init.outputs.base_ref }} - if: ${{ contains(needs.init.outputs.arg1, 'rebase') }} - run: | - git fetch origin '${{ needs.init.outputs.base_ref }}:${{ needs.init.outputs.base_ref }}' - git rebase 'origin/${{ needs.init.outputs.base_ref }}' - - - name: Install dependencies & build - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - npm run build --if-present - - - name: Commit default - if: ${{ !contains(needs.init.outputs.arg1, 'fixup') && !contains(needs.init.outputs.arg1, 'amend') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --signoff -m 'chore(assets): Recompile assets' - - - name: Commit fixup - if: ${{ contains(needs.init.outputs.arg1, 'fixup') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --fixup=HEAD --signoff - - - name: Commit amend - if: ${{ contains(needs.init.outputs.arg1, 'amend') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --amend --no-edit --signoff - # Remove any [skip ci] from the amended commit - git commit --amend -m "$(git log -1 --format='%B' | sed '/\[skip ci\]/d')" - - - name: Push normally - if: ${{ !contains(needs.init.outputs.arg1, 'rebase') && !contains(needs.init.outputs.arg1, 'amend') }} - run: git push origin '${{ needs.init.outputs.head_ref }}' - - - name: Force push - if: ${{ contains(needs.init.outputs.arg1, 'rebase') || contains(needs.init.outputs.arg1, 'amend') }} - run: git push --force origin '${{ needs.init.outputs.head_ref }}' - - - name: Add reaction on failure - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - if: failure() - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: "-1" diff --git a/.github/workflows/command-pull-3rdparty.yml b/.github/workflows/command-pull-3rdparty.yml deleted file mode 100644 index 5090193d424fc..0000000000000 --- a/.github/workflows/command-pull-3rdparty.yml +++ /dev/null @@ -1,68 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Update 3rdparty command - -on: - issue_comment: - types: created - -permissions: - contents: read - -jobs: - rebase: - runs-on: ubuntu-latest - permissions: - contents: none - - # On pull requests and if the comment starts with `/update-3rdparty` - if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/update-3rdparty') - - steps: - - name: Add reaction on start - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v3.0.1 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '+1' - - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not execute /update-3rdparty on forks' - exit 1 - - - name: Init branch - uses: xt0rted/pull-request-comment-branch@d97294d304604fa98a2600a6e2f916a84b596dc7 # v1 - id: comment-branch - - - name: Checkout ${{ steps.comment-branch.outputs.head_ref }} - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - fetch-depth: 0 - token: ${{ secrets.COMMAND_BOT_PAT }} - ref: ${{ steps.comment-branch.outputs.head_ref }} - - - name: Setup git - run: | - git config --local user.email 'nextcloud-command@users.noreply.github.com' - git config --local user.name 'nextcloud-command' - - - name: Pull 3rdparty - run: git submodule foreach 'if [ "$sm_path" == "3rdparty" ]; then git pull origin '"'"'${{ github.event.issue.pull_request.base.ref }}'"'"'; fi' - - - name: Commit and push changes - run: | - git add 3rdparty - git commit -s -m 'Update submodule 3rdparty to latest ${{ github.event.issue.pull_request.base.ref }}' - git push - - - name: Add reaction on failure - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v3.0.1 - if: failure() - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '-1' diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml deleted file mode 100644 index 7ce5e72045eaf..0000000000000 --- a/.github/workflows/cypress.yml +++ /dev/null @@ -1,184 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Cypress - -on: pull_request - -concurrency: - group: cypress-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - # Adjust APP_NAME if your repository name is different - APP_NAME: ${{ github.event.repository.name }} - - # Server requires head_ref instead of base_ref, as we want to test the PR branch - BRANCH: ${{ github.head_ref || github.ref_name }} - -jobs: - init: - runs-on: ubuntu-latest - outputs: - nodeVersion: ${{ steps.versions.outputs.nodeVersion }} - npmVersion: ${{ steps.versions.outputs.npmVersion }} - - env: - # We'll install cypress in the cypress job - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not run cypress on forks' - exit 1 - - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - # We need to checkout submodules for 3rdparty - submodules: true - - - name: Check composer.json - id: check_composer - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 - with: - files: "composer.json" - - - name: Install composer dependencies - if: steps.check_composer.outputs.files_exists == 'true' - run: composer install --no-dev - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: "^20" - fallbackNpm: "^10" - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install node dependencies & build app - run: | - npm ci - TESTING=true npm run build --if-present - - - name: Show cypress version - run: npm run cypress:version - - - name: Save context - uses: buildjet/cache/save@e376f15c6ec6dc595375c78633174c7e5f92dc0e # v3 - with: - key: cypress-context-${{ github.run_id }} - path: ./ - - cypress: - runs-on: ubuntu-latest - needs: init - - strategy: - fail-fast: false - matrix: - # Run multiple copies of the current job in parallel - # Please increase the number or runners as your tests suite grows (0 based index for e2e tests) - containers: ["component", '0', '1', '2', '3', '4', '5', '6', '7'] - # Hack as strategy.job-total includes the component and GitHub does not allow math expressions - # Always align this number with the total of e2e runners (max. index + 1) - total-containers: [8] - - name: runner ${{ matrix.containers }} - - steps: - - name: Restore context - uses: buildjet/cache/restore@e376f15c6ec6dc595375c78633174c7e5f92dc0e # v3 - with: - fail-on-cache-miss: true - key: cypress-context-${{ github.run_id }} - path: ./ - - - name: Set up node ${{ needs.init.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ needs.init.outputs.nodeVersion }} - - - name: Set up npm ${{ needs.init.outputs.npmVersion }} - run: npm i -g 'npm@${{ needs.init.outputs.npmVersion }}' - - - name: Install cypress - run: ./node_modules/cypress/bin/cypress install - - - name: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }} cypress tests - uses: cypress-io/github-action@df7484c5ba85def7eef30db301afa688187bc378 # v6.7.2 - with: - # We already installed the dependencies in the init job - install: false - component: ${{ matrix.containers == 'component' }} - group: ${{ matrix.use-cypress-cloud && matrix.containers == 'component' && 'Run component' || matrix.use-cypress-cloud && 'Run E2E' || '' }} - # cypress env - ci-build-id: ${{ matrix.use-cypress-cloud && format('{0}-{1}', github.sha, github.run_number) || '' }} - tag: ${{ matrix.use-cypress-cloud && github.event_name || '' }} - env: - # Needs to be prefixed with CYPRESS_ - CYPRESS_BRANCH: ${{ env.BRANCH }} - # https://github.com/cypress-io/github-action/issues/124 - COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} - # Needed for some specific code workarounds - TESTING: true - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - SPLIT: ${{ matrix.total-containers }} - SPLIT_INDEX: ${{ matrix.containers == 'component' && 0 || matrix.containers }} - - - name: Upload snapshots - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 - if: always() - with: - name: snapshots_${{ matrix.containers }} - path: cypress/snapshots - - - name: Extract NC logs - if: failure() && matrix.containers != 'component' - run: docker logs nextcloud-cypress-tests-${{ env.APP_NAME }} > nextcloud.log - - - name: Upload NC logs - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 - if: failure() && matrix.containers != 'component' - with: - name: nc_logs_${{ matrix.containers }} - path: nextcloud.log - - - name: Create data dir archive - if: failure() && matrix.containers != 'component' - run: docker exec nextcloud-cypress-tests-server tar -cvjf - data > data.tar - - - name: Upload data dir archive - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 - if: failure() && matrix.containers != 'component' - with: - name: nc_data_${{ matrix.containers }} - path: data.tar - - summary: - runs-on: ubuntu-latest-low - needs: [init, cypress] - - if: always() - - name: cypress-summary - - steps: - - name: Summary status - run: if ${{ needs.init.result != 'success' || ( needs.cypress.result != 'success' && needs.cypress.result != 'skipped' ) }}; then exit 1; fi diff --git a/.github/workflows/dependabot-approve-merge.yml b/.github/workflows/dependabot-approve-merge.yml deleted file mode 100644 index efe8bfe37f78d..0000000000000 --- a/.github/workflows/dependabot-approve-merge.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Dependabot - -on: - pull_request_target: - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: dependabot-approve-merge-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - auto-approve-merge: - if: github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]' - runs-on: ubuntu-latest-low - permissions: - # for hmarr/auto-approve-action to approve PRs - pull-requests: write - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not approve PRs from forks' - exit 1 - - # GitHub actions bot approve - - uses: hmarr/auto-approve-action@b40d6c9ed2fa10c9a2749eca7eb004418a705501 # v2 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - # Nextcloud bot approve and merge request - - uses: ahmadnassri/action-dependabot-auto-merge@45fc124d949b19b6b8bf6645b6c9d55f4f9ac61a # v2 - with: - target: minor - github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }} diff --git a/.github/workflows/files-external-ftp.yml b/.github/workflows/files-external-ftp.yml deleted file mode 100644 index b14ac0652af6b..0000000000000 --- a/.github/workflows/files-external-ftp.yml +++ /dev/null @@ -1,121 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit files_external FTP -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-ftp-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - files-external-ftp: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - # do not stop on another job's failure - fail-fast: false - matrix: - php-versions: ['8.1', '8.3'] - ftpd: ['proftpd', 'vsftpd', 'pure-ftpd'] - include: - - php-versions: '8.1' - coverage: ${{ github.event_name != 'pull_request' }} - - name: php${{ matrix.php-versions }}-${{ matrix.ftpd }} - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up ftpd - run: | - sudo mkdir /tmp/ftp - sudo chmod -R 0777 /tmp/ftp - if [[ "${{ matrix.ftpd }}" == 'proftpd' ]]; then echo '$6$Q7V2n3q2GRVv5YeQ$/AhLu07H76Asojy7bxGXMY1caKLAbp5Vt82LOZYMkD/8uDzyMAEXwk0c1Bdz1DkBsk2Vh/9SF130mOPavRGMo.' > /tmp/secret.txt; fi - if [[ "${{ matrix.ftpd }}" == 'proftpd' ]]; then echo 'FTP_ROOT=/home/test' > $GITHUB_ENV; fi - if [[ "${{ matrix.ftpd }}" == 'proftpd' ]]; then docker run --name ftp -d --net host -e PASV_ADDRESS=127.0.0.1 -e FTPUSER_NAME=test -v /tmp/secret.txt:/run/secrets/ftp-user-password-secret -v /tmp/ftp:/home/test instantlinux/proftpd; fi - if [[ "${{ matrix.ftpd }}" == 'vsftpd' ]]; then docker run --name ftp -d --net host -e FTP_USER=test -e FTP_PASS=test -e PASV_ADDRESS=127.0.0.1 -v /tmp/ftp:/home/vsftpd/test fauria/vsftpd; fi - if [[ "${{ matrix.ftpd }}" == 'pure-ftpd' ]]; then docker run --name ftp -d --net host -e "PUBLICHOST=localhost" -e FTP_USER_NAME=test -e FTP_USER_PASS=test -e FTP_USER_HOME=/home/test -v /tmp/ftp:/home/test -v /tmp/ftp:/etc/pure-ftpd/passwd stilliard/pure-ftpd; fi - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - run: | - composer install - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - ./occ app:enable --force files_external - echo " true,'host' => 'localhost','user' => 'test','password' => 'test', 'root' => '${{ env.FTP_ROOT }}'];" > apps/files_external/tests/config.ftp.php - - - name: smoketest ftp - run: | - php -r 'var_dump(file_put_contents("ftp://test:test@localhost${{ env.FTP_ROOT }}/ftp.txt", "asd"));' - php -r 'var_dump(file_get_contents("ftp://test:test@localhost${{ env.FTP_ROOT }}/ftp.txt"));' - php -r 'var_dump(mkdir("ftp://test:test@localhost${{ env.FTP_ROOT }}/asdads"));' - ls -l /tmp/ftp - [ -f /tmp/ftp/ftp.txt ] - - - name: PHPUnit - run: composer run test:files_external -- \ - apps/files_external/tests/Storage/FtpTest.php \ - ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-files-external-ftp - - - name: ftpd logs - if: always() - run: | - docker logs ftp - - ftp-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-ftp] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-ftp.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/files-external-sftp.yml b/.github/workflows/files-external-sftp.yml deleted file mode 100644 index 279f175895c9b..0000000000000 --- a/.github/workflows/files-external-sftp.yml +++ /dev/null @@ -1,111 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit files_external sFTP -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-sftp-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - files-external-sftp: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - # do not stop on another job's failure - fail-fast: false - matrix: - php-versions: ['8.1', '8.3'] - sftpd: ['openssh'] - include: - - php-versions: '8.1' - coverage: ${{ github.event_name != 'pull_request' }} - - name: php${{ matrix.php-versions }}-${{ matrix.sftpd }} - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up sftpd - run: | - sudo mkdir /tmp/sftp - sudo chown -R 0777 /tmp/sftp - if [[ '${{ matrix.sftpd }}' == 'openssh' ]]; then docker run -p 2222:22 --name sftp -d -v /tmp/sftp:/home/test atmoz/sftp 'test:test:::data'; fi - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - run: | - composer install - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - ./occ app:enable --force files_external - echo " true, 'host' => 'localhost:2222','user' => 'test','password' => 'test', 'root' => 'data'];" > apps/files_external/tests/config.sftp.php - - - name: PHPUnit - run: composer run test:files_external -- \ - apps/files_external/tests/Storage/SftpTest.php \ - apps/files_external/tests/Storage/SFTP_KeyTest.php \ - ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-files-external-sftp - - - name: sftpd logs - if: always() - run: | - ls -l /tmp/sftp - docker logs sftp - - sftp-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-sftp] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-sftp.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/files-external-smb-kerberos.yml b/.github/workflows/files-external-smb-kerberos.yml deleted file mode 100644 index 2e09d3386d828..0000000000000 --- a/.github/workflows/files-external-smb-kerberos.yml +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Samba Kerberos SSO -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-smb-kerberos-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - files-external-smb-kerberos: - runs-on: ubuntu-22.04 - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - name: smb-kerberos-sso - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Checkout user_saml - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - repository: nextcloud/user_saml - path: apps/user_saml - - - name: Pull images - run: | - docker pull ghcr.io/icewind1991/samba-krb-test-dc - docker pull ghcr.io/icewind1991/samba-krb-test-apache - docker pull ghcr.io/icewind1991/samba-krb-test-client - docker tag ghcr.io/icewind1991/samba-krb-test-dc icewind1991/samba-krb-test-dc - docker tag ghcr.io/icewind1991/samba-krb-test-apache icewind1991/samba-krb-test-apache - docker tag ghcr.io/icewind1991/samba-krb-test-client icewind1991/samba-krb-test-client - - - name: Setup AD-DC - run: | - DC_IP=$(apps/files_external/tests/sso-setup/start-dc.sh) - sleep 1 - apps/files_external/tests/sso-setup/start-apache.sh $DC_IP $PWD - echo "DC_IP=$DC_IP" >> $GITHUB_ENV - - - name: Set up Nextcloud - run: | - apps/files_external/tests/sso-setup/setup-sso-nc.sh - - - name: Test SSO - run: | - apps/files_external/tests/sso-setup/test-sso-smb.sh ${{ env.DC_IP }} - - - name: Show logs - if: always() - run: | - FILEPATH=$(docker exec --user 33 apache ./occ log:file | grep "Log file:" | cut -d' ' -f3) - echo "$FILEPATH:" - docker exec --user 33 apache cat $FILEPATH - - sftp-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-smb-kerberos] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-smb-kerberos.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/files-external-smb.yml b/.github/workflows/files-external-smb.yml deleted file mode 100644 index 6dfbc66c53206..0000000000000 --- a/.github/workflows/files-external-smb.yml +++ /dev/null @@ -1,110 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit files_external SMB -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-smb-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - files-external-smb: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.3'] - include: - - php-versions: '8.1' - coverage: ${{ github.event_name != 'pull_request' }} - - name: php${{ matrix.php-versions }}-smb - - services: - samba: - image: ghcr.io/nextcloud/continuous-integration-samba:latest - ports: - - 445:445 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, smbclient, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up smbclient - # This is needed as icewind/smb php library for notify - run: sudo apt-get install -y smbclient - - - name: Set up Nextcloud - run: | - composer install - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - ./occ config:system:set --value true --type boolean allow_local_remote_servers - ./occ app:enable --force files_external - echo "true, 'host'=>'localhost', 'user'=>'test', 'password'=>'test', 'root'=>'', 'share'=>'public'];" > apps/files_external/tests/config.smb.php - - - name: Wait for smb - run: | - apps/files_external/tests/env/wait-for-connection 127.0.0.1 445 60 - - - name: PHPUnit - run: composer run test:files_external -- --verbose \ - apps/files_external/tests/Storage/SmbTest.php \ - ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@922d8d7b314a529f2be903c1e79ee8283c492863 # v4.1.1 - with: - files: ./clover.xml - flags: phpunit-files-external-smb - - files-external-smb-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-smb] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-smb.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/files-external-webdav.yml b/.github/workflows/files-external-webdav.yml deleted file mode 100644 index 86d8d539d93bd..0000000000000 --- a/.github/workflows/files-external-webdav.yml +++ /dev/null @@ -1,107 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit files_external WebDAV -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-webdav-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - '**.php' - - files-external-webdav-apache: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.2' - coverage: ${{ github.event_name != 'pull_request' }} - - name: php${{ matrix.php-versions }}-webdav - - services: - apache: - image: ghcr.io/nextcloud/continuous-integration-webdav-apache:latest - ports: - - 8081:80 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - run: | - composer install - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - ./occ config:system:set --value true --type boolean allow_local_remote_servers - ./occ app:enable --force files_external - echo " true, 'host' => 'localhost:8081/webdav/', 'user' => 'test', 'password'=>'pass', 'root' => '', 'wait' => 0];" > apps/files_external/tests/config.webdav.php - - - name: Wait for WebDAV - run: | - sleep 5 - curl -f -m 1 --retry-connrefused --retry 10 --retry-delay 10 http://test:pass@localhost:8081/webdav/ - - - name: PHPUnit - run: composer run test:files_external -- --verbose \ - apps/files_external/tests/Storage/WebdavTest.php \ - ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@922d8d7b314a529f2be903c1e79ee8283c492863 # v4.1.1 - with: - files: ./clover.xml - flags: phpunit-files-external-webdav - - files-external-webdav-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-webdav-apache] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-webdav-apache.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/files-external.yml b/.github/workflows/files-external.yml deleted file mode 100644 index f6bc46faba750..0000000000000 --- a/.github/workflows/files-external.yml +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit files_external generic -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -concurrency: - group: files-external-generic-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - 'apps/files_external/**' - - 'vendor/**' - - 'vendor-bin/**' - - 'composer.json' - - 'composer.lock' - - files-external-generic: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.2' - coverage: ${{ github.event_name != 'pull_request' }} - - name: php${{ matrix.php-versions }}-generic - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - env: - OBJECT_STORE_KEY: nextcloud - OBJECT_STORE_SECRET: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ= - run: | - composer install - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - ./occ app:enable --force files_external - - - name: PHPUnit - run: composer run test:files_external \ - ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-files-external-generic - - files-external-summary: - runs-on: ubuntu-latest-low - needs: [changes, files-external-generic ] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.files-external-generic.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/fixup.yml b/.github/workflows/fixup.yml deleted file mode 100644 index 69da2bbb039f2..0000000000000 --- a/.github/workflows/fixup.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block fixup and squash commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: fixup-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - commit-message-check: - if: github.event.pull_request.draft == false - - permissions: - pull-requests: write - name: Block fixup and squash commits - - runs-on: ubuntu-latest-low - - steps: - - name: Run check - uses: skjnldsv/block-fixup-merge-action@c138ea99e45e186567b64cf065ce90f7158c236a # v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration-litmus.yml b/.github/workflows/integration-litmus.yml deleted file mode 100644 index f18537d082c00..0000000000000 --- a/.github/workflows/integration-litmus.yml +++ /dev/null @@ -1,112 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Litmus integration tests -on: - pull_request: - -concurrency: - group: integration-litmus-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/*.php' - - '**/lib/**' - - '**/tests/**' - - '**/vendor-bin/**' - - 'build/integration/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-litmus: - runs-on: ubuntu-latest - needs: changes - - if: needs.changes.outputs.src != 'false' && github.repository_owner != 'nextcloud-gmbh' - - strategy: - # do not stop on another job's failure - fail-fast: false - matrix: - php-versions: ['8.3'] - endpoint: ['webdav', 'dav'] - - name: Litmus WebDAV ${{ matrix.endpoint }} - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: 'none' - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - run: | - mkdir data - ./occ maintenance:install \ - --verbose \ - --database=sqlite \ - --database-name=nextcloud \ - --database-user=root \ - --database-pass=rootpassword \ - --admin-user admin \ - --admin-pass admin - ./occ config:system:set trusted_domains 2 --value=host.docker.internal:8080 - - - name: Run Nextcloud - run: | - php -S 0.0.0.0:8080 & - - - name: Run Litmus test - run: | - docker run \ - --rm \ - --add-host=host.docker.internal:host-gateway \ - ghcr.io/nextcloud/continuous-integration-litmus-php8.3:latest \ - bash -c '\ - cd /tmp/litmus/litmus-0.13; - make URL=http://host.docker.internal:8080/remote.php/${{ matrix.endpoint }}${{ matrix.endpoint == 'dav' && '/files/admin' || ''}} CREDS="admin admin" TESTS="basic copymove props largefile" check; - status=$?; - cat debug.log; - exit $status;' - - - name: Print Nextcloud logs - if: always() - run: cat data/nextcloud.log - - integration-litmus-summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-litmus] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-litmus.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-s3-primary.yml b/.github/workflows/integration-s3-primary.yml deleted file mode 100644 index a11f8ec81d95f..0000000000000 --- a/.github/workflows/integration-s3-primary.yml +++ /dev/null @@ -1,119 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: S3 primary storage integration tests -on: - pull_request: - -concurrency: - group: integration-s3-primary-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/*.php' - - '**/lib/**' - - '**/tests/**' - - '**/vendor-bin/**' - - 'build/integration/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-s3-primary: - runs-on: ubuntu-latest - needs: changes - - if: needs.changes.outputs.src != 'false' && github.repository_owner != 'nextcloud-gmbh' - - strategy: - # do not stop on another job's failure - fail-fast: false - matrix: - php-versions: ['8.1'] - key: ['objectstore', 'objectstore_multibucket'] - - name: php${{ matrix.php-versions }}-${{ matrix.key }}-minio - - services: - redis: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - ports: - - 6379:6379/tcp - minio: - image: bitnami/minio - env: - MINIO_ROOT_USER: nextcloud - MINIO_ROOT_PASSWORD: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ= - MINIO_DEFAULT_BUCKETS: nextcloud - ports: - - "9000:9000" - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: 'none' - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Wait for S3 - run: | - sleep 10 - curl -f -m 1 --retry-connrefused --retry 10 --retry-delay 10 http://localhost:9000/minio/health/ready - - - name: Set up Nextcloud - run: | - mkdir data - echo ' ["class" => "OC\Files\ObjectStore\S3", "arguments" => ["bucket" => "nextcloud", "autocreate" => true, "key" => "nextcloud", "secret" => "bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ=", "hostname" => "localhost", "port" => 9000, "use_ssl" => false, "use_path_style" => true, "uploadPartSize" => 52428800]]];' > config/config.php - echo ' ["host" => "localhost", "port" => 6379], "memcache.local" => "\OC\Memcache\Redis", "memcache.distributed" => "\OC\Memcache\Redis"];' > config/redis.config.php - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - php -f index.php - - - name: Integration - run: | - cd build/integration - bash run.sh --tags "~@failure-s3" dav_features/webdav-related.feature - - - name: S3 logs - if: always() - run: | - cat data/nextcloud.log - docker ps -a - docker ps -aq | while read container ; do IMAGE=$(docker inspect --format='{{.Config.Image}}' $container); echo $IMAGE; docker logs $container; echo "\n\n" ; done - - - s3-primary-integration-summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-s3-primary] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-s3-primary.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/lint-eslint.yml b/.github/workflows/lint-eslint.yml deleted file mode 100644 index e53cc1977f279..0000000000000 --- a/.github/workflows/lint-eslint.yml +++ /dev/null @@ -1,95 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint eslint - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-eslint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '**/src/**' - - '**/appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '.eslintrc.*' - - '.eslintignore' - - '**.js' - - '**.ts' - - '**.vue' - - lint: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM lint - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v3 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: npm ci - - - name: Lint - run: npm run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, lint] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: eslint - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.lint.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/lint-php-cs.yml b/.github/workflows/lint-php-cs.yml deleted file mode 100644 index f2af7aea5351c..0000000000000 --- a/.github/workflows/lint-php-cs.yml +++ /dev/null @@ -1,82 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php-cs - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-cs-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/lib/**' - - '**/tests/**' - - '**/vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - lint: - runs-on: ubuntu-latest - - name: PHP CS fixer lint - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up php8.1 - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: 8.1 - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - run: composer i - - - name: Lint - run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, lint] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: php-cs - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.lint.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml deleted file mode 100644 index 4941de4be16b4..0000000000000 --- a/.github/workflows/lint-php.yml +++ /dev/null @@ -1,82 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - outputs: - src: ${{ steps.changes.outputs.src}} - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/lib/**' - - '**/tests/**' - - '**/vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - lint: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - matrix: - php-versions: [ '8.1', '8.2', '8.3' ] - - name: php-lint - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Lint - run: composer run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, lint] - - if: always() - - name: php-lint-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.lint.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/node-test.yml b/.github/workflows/node-test.yml deleted file mode 100644 index 63c079c32f37b..0000000000000 --- a/.github/workflows/node-test.yml +++ /dev/null @@ -1,173 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Node tests - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: node-tests-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '**/__tests__/**' - - '**/__mocks__/**' - - 'apps/*/src/**' - - 'apps/*/appinfo/info.xml' - - 'core/src/**' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.js' - - '**.ts' - - '**.vue' - - versions: - runs-on: ubuntu-latest-low - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - outputs: - nodeVersion: ${{ steps.versions.outputs.nodeVersion }} - npmVersion: ${{ steps.versions.outputs.npmVersion }} - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - test: - runs-on: ubuntu-latest - needs: [versions, changes] - - if: ${{ needs.versions.result != 'failure' && needs.changes.outputs.src != 'false' }} - - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up node ${{ needs.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ needs.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ needs.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ needs.versions.outputs.npmVersion }}' - - - name: Install dependencies & build - run: | - npm ci - npm run build --if-present - - - name: Test and process coverage - run: npm run test:coverage --if-present - - - name: Collect coverage - uses: codecov/codecov-action@922d8d7b314a529f2be903c1e79ee8283c492863 # v4.3.1 - with: - files: ./coverage/lcov.info - - jsunit: - runs-on: ubuntu-latest - needs: [versions, changes] - - if: ${{ needs.versions.result != 'failure' && needs.changes.outputs.src != 'false' }} - - env: - CYPRESS_INSTALL_BINARY: 0 - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up node ${{ needs.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ needs.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ needs.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ needs.versions.outputs.npmVersion }}' - - - name: Install dependencies - run: npm ci - - - name: Test - run: npm run test:jsunit - - handlebars: - runs-on: ubuntu-latest - needs: [versions, changes] - - if: ${{ needs.versions.result != 'failure' && needs.changes.outputs.src != 'false' }} - - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up node ${{ needs.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: ${{ needs.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ needs.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ needs.versions.outputs.npmVersion }}' - - - name: Install dependencies - run: npm ci - - - name: Run compile - run: ./build/compile-handlebars-templates.sh - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, test, jsunit, handlebars] - - if: always() - - name: node-test-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && (needs.test.result != 'success' || needs.jsunit.result != 'success' || needs.handlebars.result != 'success') }}; then exit 1; fi diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml deleted file mode 100644 index 913768c24925f..0000000000000 --- a/.github/workflows/node.yml +++ /dev/null @@ -1,105 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Node - -on: pull_request - -permissions: - contents: read - -concurrency: - group: node-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '**/src/**' - - '**/appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.js' - - '**.ts' - - '**.vue' - - 'core/css/*' - - 'core/img/**' - - 'version.php' - - build: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM build - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v3 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies & build - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - npm run build --if-present - - - name: Check webpack build changes - run: | - bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please recompile and commit the assets, see the section \"Show changes on failure\" for details' && exit 1)" - - - name: Show changes on failure - if: failure() - run: | - git status - git --no-pager diff - exit 1 # make it red to grab attention - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, build] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: node - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.build.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/npm-audit-fix.yml b/.github/workflows/npm-audit-fix.yml deleted file mode 100644 index 13caf6990ff7f..0000000000000 --- a/.github/workflows/npm-audit-fix.yml +++ /dev/null @@ -1,75 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Npm audit fix and compile - -on: - workflow_dispatch: - schedule: - # At 2:30 on Sundays - - cron: '30 2 * * 0' - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: ['main', 'master', 'stable30', 'stable29', 'stable28'] - - name: npm-audit-fix-${{ matrix.branches }} - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - ref: ${{ matrix.branches }} - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v3 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Fix npm audit - id: npm-audit - uses: nextcloud-libraries/npm-audit-action@2a60bd2e79cc77f2cc4d9a3fe40f1a69896f3a87 # v0.1.0 - - - name: Run npm ci and npm run build - if: always() - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci - npm run build --if-present - - - name: Create Pull Request - if: always() - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'fix(deps): Fix npm audit' - committer: GitHub - author: nextcloud-command - signoff: true - branch: automated/noid/${{ matrix.branches }}-fix-npm-audit - title: '[${{ matrix.branches }}] Fix npm audit' - body: ${{ steps.npm-audit.outputs.markdown }} - labels: | - dependencies - 3. to review diff --git a/.github/workflows/object-storage-azure.yml b/.github/workflows/object-storage-azure.yml deleted file mode 100644 index bb1dc52a9ad48..0000000000000 --- a/.github/workflows/object-storage-azure.yml +++ /dev/null @@ -1,127 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Object storage azure -on: - pull_request: - schedule: - - cron: "15 2 * * *" - -concurrency: - group: object-storage-azure-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - azure-primary-tests: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.3' - coverage: true - - name: php${{ matrix.php-versions }}-azure - - services: - azurite: - image: mcr.microsoft.com/azure-storage/azurite - env: - AZURITE_ACCOUNTS: nextcloud:bmV4dGNsb3Vk - ports: - - 10000:10000 - options: --health-cmd="nc 127.0.0.1 10000 -z" --health-interval=1s --health-retries=30 - - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - env: - OBJECT_STORE: azure - OBJECT_STORE_KEY: nextcloud - OBJECT_STORE_SECRET: bmV4dGNsb3Vk - run: | - composer install - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit - env: - OBJECT_STORE: azure - OBJECT_STORE_KEY: nextcloud - OBJECT_STORE_SECRET: bmV4dGNsb3Vk - run: composer run test -- --group PRIMARY-azure ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-azure - - - name: Azurite logs - if: always() - run: | - docker ps -a - docker ps -aq | while read container ; do IMAGE=$(docker inspect --format='{{.Config.Image}}' $container); echo $IMAGE; docker logs $container; echo "\n\n" ; done - - azure-primary-summary: - runs-on: ubuntu-latest-low - needs: [changes, azure-primary-tests] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.azure-primary-tests.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/object-storage-s3.yml b/.github/workflows/object-storage-s3.yml deleted file mode 100644 index b256174c0616e..0000000000000 --- a/.github/workflows/object-storage-s3.yml +++ /dev/null @@ -1,133 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Object storage S3 -on: - pull_request: - schedule: - - cron: "15 2 * * *" - -concurrency: - group: object-storage-s3-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - s3-primary-tests-minio: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.3' - coverage: true - - name: php${{ matrix.php-versions }}-s3 - - services: - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - minio: - image: bitnami/minio - env: - MINIO_ROOT_USER: nextcloud - MINIO_ROOT_PASSWORD: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ= - MINIO_DEFAULT_BUCKETS: nextcloud - ports: - - "9000:9000" - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - env: - OBJECT_STORE: s3 - OBJECT_STORE_KEY: nextcloud - OBJECT_STORE_SECRET: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ= - run: | - composer install - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: Wait for S3 - run: | - sleep 10 - curl -f -m 1 --retry-connrefused --retry 10 --retry-delay 10 http://localhost:9000/minio/health/ready - - - name: PHPUnit - env: - OBJECT_STORE: s3 - OBJECT_STORE_KEY: nextcloud - OBJECT_STORE_SECRET: bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ= - run: composer run test -- --group PRIMARY-s3 ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-s3 - - - name: S3 logs - if: always() - run: | - docker ps -a - docker ps -aq | while read container ; do IMAGE=$(docker inspect --format='{{.Config.Image}}' $container); echo $IMAGE; docker logs $container; echo "\n\n" ; done - - s3-primary-summary: - runs-on: ubuntu-latest-low - needs: [changes,s3-primary-tests-minio] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.s3-primary-tests-minio.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/object-storage-swift.yml b/.github/workflows/object-storage-swift.yml deleted file mode 100644 index 9f7bc8c2806b3..0000000000000 --- a/.github/workflows/object-storage-swift.yml +++ /dev/null @@ -1,123 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Object storage Swift -on: - pull_request: - schedule: - - cron: "15 2 * * *" - -concurrency: - group: object-storage-swift-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - swift-primary-tests: - runs-on: ubuntu-latest - needs: changes - - if: ${{ github.repository_owner != 'nextcloud-gmbh' && needs.changes.outputs.src != 'false' }} - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.3' - coverage: true - - name: php${{ matrix.php-versions }}-swift - - services: - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - swift: - image: ghcr.io/cscfi/docker-keystone-swift - ports: - - 5000:5000 - - 8080:8080 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - env: - OBJECT_STORE: swift - OBJECT_STORE_SECRET: veryfast - run: | - composer install - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit - env: - OBJECT_STORE: swift - OBJECT_STORE_SECRET: veryfast - run: composer run test -- --group PRIMARY-swift ${{ matrix.coverage && ' --coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-swift - - - name: Swift logs - if: always() - run: | - docker ps -a - docker ps -aq | while read container ; do IMAGE=$(docker inspect --format='{{.Config.Image}}' $container); echo $IMAGE; docker logs $container; echo "\n\n" ; done - - swift-primary-summary: - runs-on: ubuntu-latest-low - needs: [changes,swift-primary-tests] - - if: always() - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.swift-primary-tests.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml deleted file mode 100644 index f03ecec16c5b8..0000000000000 --- a/.github/workflows/openapi.yml +++ /dev/null @@ -1,45 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-FileCopyrightText: 2024 Arthur Schiwon -# SPDX-License-Identifier: MIT - -name: OpenAPI - -on: pull_request - -permissions: - contents: read - -concurrency: - group: openapi-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - openapi: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: Set up php - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: '8.2' - extensions: ctype, curl, dom, fileinfo, gd, json, libxml, mbstring, openssl, pcntl, pdo, posix, session, simplexml, xml, xmlreader, xmlwriter, zip, zlib - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: OpenAPI checker - run: build/openapi-checker.sh diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml deleted file mode 100644 index e4670be058862..0000000000000 --- a/.github/workflows/performance.yml +++ /dev/null @@ -1,120 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Performance testing -on: - pull_request: - -concurrency: - group: performance-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - performance-testing: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - strategy: - fail-fast: false - matrix: - php-versions: ['8.1'] - - name: performance-${{ matrix.php-versions }} - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not run performance tests on forks' - exit 1 - - - name: Checkout server before PR - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - ref: ${{ github.event.pull_request.base.ref }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring, fileinfo, intl, sqlite, pdo_sqlite, zip, gd - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - run: | - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password - - php -S localhost:8080 & - - name: Apply blueprint - uses: icewind1991/blueprint@v0.1.2 - with: - blueprint: tests/blueprints/basic.toml - ref: ${{ github.event.pull_request.head.ref }} - - - name: Run before measurements - uses: nextcloud/profiler@6801ee10fc80f10b444388fb6ca9b36ad8a2ea83 - with: - run: | - curl -s -X PROPFIND -u test:test http://localhost:8080/remote.php/dav/files/test - curl -s -u test:test http://localhost:8080/remote.php/dav/files/test/test.txt - curl -s -X PROPFIND -u test:test http://localhost:8080/remote.php/dav/files/test/many_files - curl -s -u test:test -T README.md http://localhost:8080/remote.php/dav/files/test/new_file.txt - curl -s -u test:test -X DELETE http://localhost:8080/remote.php/dav/files/test/new_file.txt - output: before.json - profiler-branch: master - - - name: Apply PR - run: | - git remote add pr '${{ github.event.pull_request.head.repo.clone_url }}' - git fetch pr '${{ github.event.pull_request.head.ref }}' - git checkout -b 'pr/${{ github.event.pull_request.head.ref }}' - git submodule update - - ./occ upgrade - - - name: Run after measurements - id: compare - uses: nextcloud/profiler@6801ee10fc80f10b444388fb6ca9b36ad8a2ea83 - with: - run: | - curl -s -X PROPFIND -u test:test http://localhost:8080/remote.php/dav/files/test - curl -s -u test:test http://localhost:8080/remote.php/dav/files/test/test.txt - curl -s -X PROPFIND -u test:test http://localhost:8080/remote.php/dav/files/test/many_files - curl -s -u test:test -T README.md http://localhost:8080/remote.php/dav/files/test/new_file.txt - curl -s -u test:test -X DELETE http://localhost:8080/remote.php/dav/files/test/new_file.txt - output: after.json - profiler-branch: master - compare-with: before.json - - - name: Upload profiles - if: always() - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b - with: - name: profiles - path: | - before.json - after.json - - - uses: actions/github-script@v7 - if: failure() && steps.compare.outcome == 'failure' - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - let comment = `Possible performance regression detected\n`; - comment += `
Show Output - - \`\`\` - ${{ steps.compare.outputs.compare }} - \`\`\` - -
`; - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }) diff --git a/.github/workflows/phpunit-32bits.yml b/.github/workflows/phpunit-32bits.yml deleted file mode 100644 index 321d833dd068d..0000000000000 --- a/.github/workflows/phpunit-32bits.yml +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: PHPUnit 32bits - -on: - pull_request: - paths: - - 'version.php' - - '.github/workflows/phpunit-32bits.yml' - workflow_dispatch: - schedule: - - cron: "15 1 * * 1-6" - -permissions: - contents: read - -concurrency: - group: phpunit-32bits-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - phpunit-32bits: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - container: shivammathur/node:latest-i386 - - strategy: - matrix: - php-versions: ['8.1','8.3'] - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Install tools - run: | - sudo apt-get update - sudo apt-get install -y ffmpeg imagemagick libmagickcore-6.q16-3-extra - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - extensions: ctype, curl, dom, fileinfo, gd, imagick, intl, json, mbstring, openssl, pdo_sqlite, posix, sqlite, xml, zip, apcu - coverage: none - ini-file: development - ini-values: - apc.enabled=on, apc.enable_cli=on, disable_functions= # https://github.com/shivammathur/setup-php/discussions/573 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - composer install - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=autotest --database-pass=rootpassword --admin-user admin --admin-pass admin - php -f index.php - - - name: PHPUnit - run: composer run test -- --exclude-group PRIMARY-azure,PRIMARY-s3,PRIMARY-swift,Memcached,Redis,RoutingWeirdness diff --git a/.github/workflows/phpunit-memcached.yml b/.github/workflows/phpunit-memcached.yml deleted file mode 100644 index 51987dda27f2f..0000000000000 --- a/.github/workflows/phpunit-memcached.yml +++ /dev/null @@ -1,126 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit memcached - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: phpunit-memcached-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - phpunit-memcached: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - matrix: - php-versions: ['8.1', '8.2', '8.3'] - include: - - php-versions: '8.2' - coverage: ${{ github.event_name != 'pull_request' }} - - name: Memcached (PHP ${{ matrix.php-versions }}) - - services: - memcached: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 11212:11212/tcp - - 11212:11212/udp - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, memcached, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: Set up Nextcloud - run: | - mkdir data - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit memcached tests - run: composer run test -- --group Memcache,Memcached ${{ matrix.coverage && '--coverage-clover ./clover.xml' || '' }} - - - name: Upload code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.xml - flags: phpunit-memcached - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, phpunit-memcached] - - if: always() - - name: phpunit-memcached-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-memcached.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-mysql.yml b/.github/workflows/phpunit-mysql.yml deleted file mode 100644 index 110a2da9c7402..0000000000000 --- a/.github/workflows/phpunit-mysql.yml +++ /dev/null @@ -1,147 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit mysql - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: phpunit-mysql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src }} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - phpunit-mysql: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - matrix: - php-versions: ['8.1'] - mysql-versions: ['8.0', '8.4'] - include: - - mysql-versions: '8.0' - php-versions: '8.3' - coverage: ${{ github.event_name != 'pull_request' }} - - name: MySQL ${{ matrix.mysql-versions }} (PHP ${{ matrix.php-versions }}) - database tests - - services: - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - mysql: - image: ghcr.io/nextcloud/continuous-integration-mysql-${{ matrix.mysql-versions }}:latest - ports: - - 4444:3306/tcp - env: - MYSQL_ROOT_PASSWORD: rootpassword - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: nextcloud - MYSQL_DATABASE: oc_autotest - options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: Enable ONLY_FULL_GROUP_BY MySQL option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo "SELECT @@sql_mode;" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit - run: composer run test:db ${{ matrix.coverage && ' -- --coverage-clover ./clover.db.xml' || '' }} - - - name: Upload db code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.db.xml - flags: phpunit-mysql - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, phpunit-mysql] - - if: always() - - name: phpunit-mysql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-mysql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-oci.yml b/.github/workflows/phpunit-oci.yml deleted file mode 100644 index fe449820a7840..0000000000000 --- a/.github/workflows/phpunit-oci.yml +++ /dev/null @@ -1,151 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit OCI - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: phpunit-oci-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src }} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - phpunit-oci: - runs-on: ubuntu-latest - - needs: changes - if: ${{ needs.changes.outputs.src != 'false' && github.repository_owner != 'nextcloud-gmbh' }} - - strategy: - fail-fast: false - matrix: - include: - - oracle-versions: '11' - php-versions: '8.1' - - oracle-versions: '18' - php-versions: '8.1' - coverage: ${{ github.event_name != 'pull_request' }} - - oracle-versions: '21' - php-versions: '8.2' - - oracle-versions: '23' - php-versions: '8.3' - - name: Oracle ${{ matrix.oracle-versions }} (PHP ${{ matrix.php-versions }}) - database tests - - services: - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - oracle: - image: ghcr.io/gvenzl/oracle-${{ matrix.oracle-versions < 23 && 'xe' || 'free' }}:${{ matrix.oracle-versions }} - - # Provide passwords and other environment variables to container - env: - ORACLE_PASSWORD: oracle - - # Forward Oracle port - ports: - - 1521:1521 - - # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 20s - --health-timeout 10s - --health-retries 10 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, oci8 - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: Set up Nextcloud - run: | - mkdir data - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=oci --database-name=${{ matrix.oracle-versions < 23 && 'XE' || 'FREE' }} --database-host=127.0.0.1 --database-port=1521 --database-user=system --database-pass=oracle --admin-user admin --admin-pass admin - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit - run: composer run test:db ${{ matrix.coverage && ' -- --coverage-clover ./clover.db.xml' || '' }} - - - name: Upload db code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.db.xml - flags: phpunit-oci - - - name: Run repair steps - run: | - ./occ maintenance:repair --include-expensive - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, phpunit-oci] - - if: always() - - name: phpunit-oci-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-oci.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-pgsql.yml b/.github/workflows/phpunit-pgsql.yml deleted file mode 100644 index 957bae2079b37..0000000000000 --- a/.github/workflows/phpunit-pgsql.yml +++ /dev/null @@ -1,146 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit PostgreSQL - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: phpunit-pgsql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src }} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '3rdparty/**' - - '**/appinfo/**' - - '**/lib/**' - - '**/templates/**' - - '**/tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - '**.php' - - phpunit-pgsql: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - matrix: - php-versions: ['8.1'] - # To keep the matrix smaller we ignore PostgreSQL '13', '14', and '15' as we already test 12 and 16 as lower and upper bound - postgres-versions: ['12', '16'] - include: - - php-versions: '8.3' - postgres-versions: '16' - coverage: ${{ github.event_name != 'pull_request' }} - - name: PostgreSQL ${{ matrix.postgres-versions }} (PHP ${{ matrix.php-versions }}) - database tests - - services: - cache: - image: ghcr.io/nextcloud/continuous-integration-redis:latest - ports: - - 6379:6379/tcp - options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 - - postgres: - image: ghcr.io/nextcloud/continuous-integration-postgres-${{ matrix.postgres-versions }}:latest - ports: - - 4444:5432/tcp - env: - POSTGRES_USER: root - POSTGRES_PASSWORD: rootpassword - POSTGRES_DB: nextcloud - options: --mount type=tmpfs,destination=/var/lib/postgresql/data --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Checkout server - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: true - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql - coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up dependencies - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - cp tests/redis.config.php config/ - cp tests/preseed-config.php config/config.php - ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - php -f tests/enable_all.php | grep -i -C9999 error && echo "Error during app setup" && exit 1 || exit 0 - - - name: PHPUnit database tests - run: composer run test:db ${{ matrix.coverage && ' -- --coverage-clover ./clover.db.xml' || '' }} - - - name: Upload db code coverage - if: ${{ !cancelled() && matrix.coverage }} - uses: codecov/codecov-action@v4.5.0 - with: - files: ./clover.db.xml - flags: phpunit-postgres - - - name: Run repair steps - run: | - ./occ maintenance:repair --include-expensive - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, phpunit-pgsql] - - if: always() - - name: phpunit-pgsql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.phpunit-pgsql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/pr-feedback.yml b/.github/workflows/pr-feedback.yml deleted file mode 100644 index 7b68226f89d53..0000000000000 --- a/.github/workflows/pr-feedback.yml +++ /dev/null @@ -1,50 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization - -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-FileCopyrightText: 2023 Marcel Klehr -# SPDX-FileCopyrightText: 2023 Joas Schilling <213943+nickvergessen@users.noreply.github.com> -# SPDX-FileCopyrightText: 2023 Daniel Kesselberg -# SPDX-FileCopyrightText: 2023 Florian Steffens -# SPDX-License-Identifier: MIT - -name: 'Ask for feedback on PRs' -on: - schedule: - - cron: '30 1 * * *' - -jobs: - pr-feedback: - runs-on: ubuntu-latest - steps: - - name: The get-github-handles-from-website action - uses: marcelklehr/get-github-handles-from-website-action@a739600f6b91da4957f51db0792697afbb2f143c # v1.0.0 - id: scrape - with: - website: 'https://nextcloud.com/team/' - - - name: Get blocklist - id: blocklist - run: | - blocklist=$(curl https://raw.githubusercontent.com/nextcloud/.github/master/non-community-usernames.txt | paste -s -d, -) - echo "blocklist=$blocklist" >> "$GITHUB_OUTPUT" - - - uses: marcelklehr/pr-feedback-action@1883b38a033fb16f576875e0cf45f98b857655c4 - with: - feedback-message: | - Hello there, - Thank you so much for taking the time and effort to create a pull request to our Nextcloud project. - - We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. - - Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 - - Thank you for contributing to Nextcloud and we hope to hear from you soon! - - (If you believe you should not receive this message, you can add yourself to the [blocklist](https://github.com/nextcloud/.github/blob/master/non-community-usernames.txt).) - days-before-feedback: 14 - start-date: '2024-04-30' - exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }},nextcloud-command,nextcloud-android-bot' - exempt-bots: true diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml deleted file mode 100644 index 031e80a835554..0000000000000 --- a/.github/workflows/reuse.yml +++ /dev/null @@ -1,22 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization - -# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. -# -# SPDX-License-Identifier: CC0-1.0 - -name: REUSE Compliance Check - -on: [pull_request] - -jobs: - reuse-compliance-check: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - - name: REUSE Compliance Check - uses: fsfe/reuse-action@3ae3c6bdf1257ab19397fab11fd3312144692083 # v4.0.0 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index cced0a2d468fb..0000000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Close stale issues - -on: - workflow_dispatch: - schedule: - - cron: "0 0 * * *" - -jobs: - stale: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - permissions: - issues: write - - steps: - - uses: actions/stale@v9 - with: - repo-token: ${{ secrets.COMMAND_BOT_PAT }} - stale-issue-message: > - This issue has been automatically marked as stale because it has not had - recent activity and seems to be missing some essential information. - It will be closed if no further activity occurs. Thank you - for your contributions. - stale-issue-label: 'stale' - only-labels: 'needs info' - labels-to-remove-when-unstale: 'needs info,stale' - exempt-issue-labels: '1. to develop,2. developing,3. to review,4. to release,security' - days-before-stale: 30 - days-before-close: 14 - # debug-only: true - diff --git a/.github/workflows/static-code-analysis.yml b/.github/workflows/static-code-analysis.yml deleted file mode 100644 index 3c78c093b9c4e..0000000000000 --- a/.github/workflows/static-code-analysis.yml +++ /dev/null @@ -1,103 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Psalm static code analysis - -on: - pull_request: - -concurrency: - group: static-code-analysis-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - static-code-analysis: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: '8.1' - extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Composer install - run: composer i - - - name: Psalm - run: composer run psalm:ci -- --monochrome --no-progress --output-format=github --update-baseline --report=results.sarif - - - name: Show potential changes in Psalm baseline - if: always() - run: git diff -- . ':!lib/composer' - - - name: Upload Analysis results to GitHub - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif - - static-code-analysis-security: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: '8.1' - extensions: ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip - coverage: none - - - name: Composer install - run: composer i - - - name: Psalm taint analysis - run: composer run psalm:ci -- --monochrome --no-progress --output-format=github --report=results.sarif --taint-analysis - - - name: Upload Security Analysis results to GitHub - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: results.sarif - - static-code-analysis-ocp: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - submodules: true - - - name: Set up php - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: '8.1' - extensions: ctype,curl,dom,fileinfo,gd,intl,json,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Composer install - run: composer i - - - name: Psalm - run: composer run psalm:ci -- -c psalm-ocp.xml --monochrome --no-progress --output-format=github --update-baseline - - - name: Show potential changes in Psalm baseline - if: always() - run: git diff -- . ':!lib/composer' diff --git a/.github/workflows/update-cacert-bundle.yml b/.github/workflows/update-cacert-bundle.yml deleted file mode 100644 index 29fe83e743fb9..0000000000000 --- a/.github/workflows/update-cacert-bundle.yml +++ /dev/null @@ -1,45 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Update CA certificate bundle - -on: - workflow_dispatch: - schedule: - - cron: "5 2 * * *" - -jobs: - update-ca-certificate-bundle: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: ['master', 'stable30', 'stable29', 'stable28', 'stable27', 'stable26', 'stable25', 'stable24', 'stable23', 'stable22'] - - name: update-ca-certificate-bundle-${{ matrix.branches }} - - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - ref: ${{ matrix.branches }} - submodules: true - - - name: Download CA certificate bundle from curl - run: curl --etag-compare build/ca-bundle-etag.txt --etag-save build/ca-bundle-etag.txt --output resources/config/ca-bundle.crt https://curl.se/ca/cacert.pem - - - name: Create Pull Request - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'fix(security): Update CA certificate bundle' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-ca-cert-bundle' - title: '[${{ matrix.branches }}] fix(security): Update CA certificate bundle' - body: | - Auto-generated update of CA certificate bundle from [https://curl.se/docs/caextract.html](https://curl.se/docs/caextract.html) - labels: | - dependencies - 3. to review - reviewers: ChristophWurst, miaulalala, nickvergessen diff --git a/.github/workflows/update-code-signing-crl.yml b/.github/workflows/update-code-signing-crl.yml deleted file mode 100644 index 7595eb456add0..0000000000000 --- a/.github/workflows/update-code-signing-crl.yml +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Update code signing revocation list - -on: - workflow_dispatch: - schedule: - - cron: "5 2 * * *" - -jobs: - update-code-signing-crl: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: ['master', 'stable30', 'stable29', 'stable28', 'stable27', 'stable26', 'stable25', 'stable24', 'stable23', 'stable22'] - - name: update-code-signing-crl-${{ matrix.branches }} - - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - ref: ${{ matrix.branches }} - submodules: true - - - name: Download CRL file from Appstore repository - run: curl --output resources/codesigning/root.crl https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/certificate/nextcloud.crl - - - name: Verify CRL is from CRT - run: openssl crl -verify -in resources/codesigning/root.crl -CAfile resources/codesigning/root.crt -noout - - - name: Create Pull Request - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'fix(security): Update code signing revocation list' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-code-signing-crl' - title: '[${{ matrix.branches }}] fix(security): Update code signing revocation list' - body: | - Auto-generated update of code signing revocation list from [Appstore](https://github.com/nextcloud/appstore/commits/master/nextcloudappstore/certificate/nextcloud.crl) - labels: | - dependencies - 3. to review - reviewers: mgallien, miaulalala, nickvergessen diff --git a/.github/workflows/update-psalm-baseline-approve-merge.yml b/.github/workflows/update-psalm-baseline-approve-merge.yml deleted file mode 100644 index d554527c7e89d..0000000000000 --- a/.github/workflows/update-psalm-baseline-approve-merge.yml +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Auto approve psalm baseline update - -on: - pull_request_target: - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: update-psalm-baseline-approve-merge-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - auto-approve-merge: - if: github.actor == 'nextcloud-command' - runs-on: ubuntu-latest-low - permissions: - # for hmarr/auto-approve-action to approve PRs - pull-requests: write - # for alexwilson/enable-github-automerge-action to approve PRs - contents: write - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not approve PRs from forks' - exit 1 - - - uses: mdecoleman/pr-branch-name@55795d86b4566d300d237883103f052125cc7508 # v3.0.0 - id: branchname - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - # GitHub actions bot approve - - uses: hmarr/auto-approve-action@b40d6c9ed2fa10c9a2749eca7eb004418a705501 # v2 - if: startsWith(steps.branchname.outputs.branch, 'automated/noid/') && endsWith(steps.branchname.outputs.branch, 'update-psalm-baseline') - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - # Enable GitHub auto merge - - name: Auto merge - uses: alexwilson/enable-github-automerge-action@56e3117d1ae1540309dc8f7a9f2825bc3c5f06ff # main - if: startsWith(steps.branchname.outputs.branch, 'automated/noid/') && endsWith(steps.branchname.outputs.branch, 'update-psalm-baseline') - with: - github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/update-psalm-baseline.yml b/.github/workflows/update-psalm-baseline.yml deleted file mode 100644 index 1fdf399ebeaf3..0000000000000 --- a/.github/workflows/update-psalm-baseline.yml +++ /dev/null @@ -1,69 +0,0 @@ -# SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Update Psalm baseline - -on: - workflow_dispatch: - schedule: - - cron: "5 2 * * *" - -jobs: - update-psalm-baseline: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - strategy: - fail-fast: false - matrix: - branches: ['master', 'stable30', 'stable29', 'stable28'] - - name: update-psalm-baseline-${{ matrix.branches }} - - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - with: - ref: ${{ matrix.branches }} - submodules: true - - - name: Set up php - uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 #v2.31.1 - with: - php-version: '8.1' - extensions: apcu,ctype,curl,dom,fileinfo,ftp,gd,intl,json,ldap,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Composer install - run: composer install - - - name: Psalm - run: composer run psalm:ci -- --monochrome --no-progress --output-format=text --update-baseline - continue-on-error: true - - - name: Psalm OCP - run: composer run psalm:ci -- -c psalm-ocp.xml --monochrome --no-progress --output-format=github --update-baseline - continue-on-error: true - - - name: Reset composer - run: | - git clean -f lib/composer - git checkout composer.json composer.lock lib/composer - - - name: Create Pull Request - uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'chore(tests): Update psalm baseline' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-psalm-baseline' - title: '[${{ matrix.branches }}] Update psalm-baseline.xml' - body: | - Auto-generated update psalm-baseline.xml with fixed psalm warnings - labels: | - automated pr - 3. to review - team-reviewers: server-backend From f0f14ffdef84e6750f00b9eec83e7045068c4fec Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 17:45:48 +0200 Subject: [PATCH 05/12] fix tests --- lib/private/Files/Node/Node.php | 15 +++++++++------ lib/private/Files/View.php | 4 +++- tests/lib/Files/Node/IntegrationTest.php | 6 +++--- tests/lib/Files/PathVerificationTest.php | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index b49a04e6f62f9..962d1bb6d4400 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -432,11 +432,14 @@ public function move($targetPath) { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ( - $parent instanceof Folder and - $this->isValidPath($targetPath) and - ( - $parent->isCreatable() || - ($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount) + ($parent instanceof Folder) + && $this->isValidPath($targetPath) + && ( + $parent->isCreatable() + || ( + $parent->getInternalPath() === '' + && ($parent->getMountPoint() instanceof MoveableMount) + ) ) ) { $nonExisting = $this->createNonExistingNode($targetPath); @@ -460,7 +463,7 @@ public function move($targetPath) { $this->path = $targetPath; return $targetNode; } else { - throw new NotPermittedException('No permission to move to path ' . $targetPath); + throw new NotPermittedException('No permission to move to path ' . $targetPath . ($parent instanceof Folder ? 'true':'faöse'). ($this->isValidPath($targetPath) ? 'true':'false')); } } diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 3fdc51f9928c1..ca0320f55219d 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -1365,7 +1365,9 @@ public function getFileInfo($path, $includeMountPoints = true) { } // Ensure that parent path is valid (so it is writable) try { - $this->verifyPath(dirname($path), basename($path)); + if ($path && $path !== '/') { + $this->verifyPath(dirname($path), basename($path)); + } } catch (InvalidPathException) { // Not a valid parent path so remove update and create permissions $data['permissions'] &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE); diff --git a/tests/lib/Files/Node/IntegrationTest.php b/tests/lib/Files/Node/IntegrationTest.php index 7d87cdb5dd0ee..fe6fe779ad00d 100644 --- a/tests/lib/Files/Node/IntegrationTest.php +++ b/tests/lib/Files/Node/IntegrationTest.php @@ -12,6 +12,7 @@ use OC\Files\View; use OC\Memcache\ArrayCache; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountManager; use OCP\ICacheFactory; use OCP\IUserManager; @@ -46,8 +47,7 @@ class IntegrationTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); - /** @var IMountManager $manager */ - $manager = \OC::$server->get(IMountManager::class); + $manager = \OCP\Server::get(IMountManager::class); \OC_Hook::clear('OC_Filesystem'); @@ -64,7 +64,7 @@ protected function setUp(): void { $manager, $this->view, $user, - \OC::$server->getUserMountCache(), + \OCP\Server::get(IUserMountCache::class), $this->createMock(LoggerInterface::class), $this->createMock(IUserManager::class), $this->createMock(IEventDispatcher::class), diff --git a/tests/lib/Files/PathVerificationTest.php b/tests/lib/Files/PathVerificationTest.php index 0fd0c330ea5bb..f256a954f9f1d 100644 --- a/tests/lib/Files/PathVerificationTest.php +++ b/tests/lib/Files/PathVerificationTest.php @@ -32,7 +32,7 @@ protected function setUp(): void { public function testPathVerificationFileNameTooLong() { $this->expectException(\OCP\Files\InvalidPathException::class); - $this->expectExceptionMessage('File name is too long'); + $this->expectExceptionMessage('Filename is too long'); $fileName = str_repeat('a', 500); $this->view->verifyPath('', $fileName); From dcf9aa5b4a4ca94bfb794ae2eda66f25fe6092fe Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 19:31:43 +0200 Subject: [PATCH 06/12] fix(dav): Validate target path before doing a MOVE Signed-off-by: Ferdinand Thiessen --- apps/dav/lib/Connector/Sabre/FilesPlugin.php | 53 +++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index d4c996df145a9..ba9443c5ff3df 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -8,8 +8,11 @@ namespace OCA\DAV\Connector\Sabre; use OC\AppFramework\Http\Request; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCP\Constants; use OCP\Files\ForbiddenException; +use OCP\Files\IFilenameValidator; +use OCP\Files\InvalidPathException; use OCP\Files\StorageNotAvailableException; use OCP\FilesMetadata\Exceptions\FilesMetadataException; use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; @@ -65,33 +68,26 @@ class FilesPlugin extends ServerPlugin { /** Reference to main server object */ private ?Server $server = null; - private Tree $tree; - private IUserSession $userSession; /** - * Whether this is public webdav. - * If true, some returned information will be stripped off. + * @param Tree $tree + * @param IConfig $config + * @param IRequest $request + * @param IPreview $previewManager + * @param IUserSession $userSession + * @param bool $isPublic Whether this is public WebDAV. If true, some returned information will be stripped off. + * @param bool $downloadAttachment + * @return void */ - private bool $isPublic; - private bool $downloadAttachment; - private IConfig $config; - private IRequest $request; - private IPreview $previewManager; - - public function __construct(Tree $tree, - IConfig $config, - IRequest $request, - IPreview $previewManager, - IUserSession $userSession, - bool $isPublic = false, - bool $downloadAttachment = true) { - $this->tree = $tree; - $this->config = $config; - $this->request = $request; - $this->userSession = $userSession; - $this->isPublic = $isPublic; - $this->downloadAttachment = $downloadAttachment; - $this->previewManager = $previewManager; + public function __construct( + private Tree $tree, + private IConfig $config, + private IRequest $request, + private IPreview $previewManager, + private IUserSession $userSession, + private bool $isPublic = false, + private bool $downloadAttachment = true, + ) { } /** @@ -158,7 +154,7 @@ public function checkMove($source, $destination) { return; } [$sourceDir,] = \Sabre\Uri\split($source); - [$destinationDir,] = \Sabre\Uri\split($destination); + [$destinationDir, $destinationName] = \Sabre\Uri\split($destination); if ($sourceDir !== $destinationDir) { $sourceNodeFileInfo = $sourceNode->getFileInfo(); @@ -169,6 +165,13 @@ public function checkMove($source, $destination) { if (!$sourceNodeFileInfo->isDeletable()) { throw new Forbidden($source . ' cannot be deleted'); } + + $validator = \OCP\Server::get(IFilenameValidator::class); + try { + $validator->validateFilename($destinationName); + } catch (InvalidPathException $e) { + throw new InvalidPath($e->getMessage(), false); + } } } From d89d3bf63170821d0938f25cdca4f4b5351b3e64 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 22:13:32 +0200 Subject: [PATCH 07/12] fix-files-plugin --- apps/dav/lib/Connector/Sabre/FilesPlugin.php | 58 +++++++++++++------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index ba9443c5ff3df..78ca99533ff0d 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -138,6 +138,36 @@ public function initialize(Server $server) { } }); $this->server->on('beforeMove', [$this, 'checkMove']); + $this->server->on('beforeCopy', [$this, 'checkCopy']); + } + + /** + * Plugin that checks if a copy can actually be performed. + * + * @param string $source source path + * @param string $destination destination path + * @throws NotFound If the source does not exist + * @throws InvalidPath If the destination is invalid + */ + public function checkCopy($source, $destination) { + $sourceNode = $this->tree->getNodeForPath($source); + if (!$sourceNode instanceof Node) { + return; + } + + // Ensure source exists + $sourceNodeFileInfo = $sourceNode->getFileInfo(); + if ($sourceNodeFileInfo === null) { + throw new NotFound($source . ' does not exist'); + } + // Ensure the target name is valid + try { + [, $targetName] = \Sabre\Uri\split($destination); + $validator = \OCP\Server::get(IFilenameValidator::class); + $validator->validateFilename($targetName); + } catch (InvalidPathException $e) { + throw new InvalidPath($e->getMessage(), false); + } } /** @@ -145,33 +175,23 @@ public function initialize(Server $server) { * * @param string $source source path * @param string $destination destination path - * @throws Forbidden - * @throws NotFound + * @throws Forbidden If the source is not deletable + * @throws NotFound If the source does not exist + * @throws InvalidPath If the destination name is invalid */ public function checkMove($source, $destination) { $sourceNode = $this->tree->getNodeForPath($source); if (!$sourceNode instanceof Node) { return; } - [$sourceDir,] = \Sabre\Uri\split($source); - [$destinationDir, $destinationName] = \Sabre\Uri\split($destination); - if ($sourceDir !== $destinationDir) { - $sourceNodeFileInfo = $sourceNode->getFileInfo(); - if ($sourceNodeFileInfo === null) { - throw new NotFound($source . ' does not exist'); - } - - if (!$sourceNodeFileInfo->isDeletable()) { - throw new Forbidden($source . ' cannot be deleted'); - } + // First check copyable (move only needs additional delete permission) + $this->checkCopy($source, $destination); - $validator = \OCP\Server::get(IFilenameValidator::class); - try { - $validator->validateFilename($destinationName); - } catch (InvalidPathException $e) { - throw new InvalidPath($e->getMessage(), false); - } + // The source needs to be deletable for moving + $sourceNodeFileInfo = $sourceNode->getFileInfo(); + if (!$sourceNodeFileInfo->isDeletable()) { + throw new Forbidden($source . ' cannot be deleted'); } } From 6527ea7c6b91eebeeb466fa967216e2882900aa4 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 26 Aug 2024 23:18:56 +0200 Subject: [PATCH 08/12] plugi --- apps/dav/lib/Connector/Sabre/FilesPlugin.php | 4 +- .../dav/lib/Connector/Sabre/ServerFactory.php | 2 + apps/dav/lib/Server.php | 14 +- .../unit/Connector/Sabre/FilesPluginTest.php | 273 ++++++++++-------- .../Connector/Sabre/FilesReportPluginTest.php | 61 ++-- 5 files changed, 191 insertions(+), 163 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index 78ca99533ff0d..adc3e892484bd 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -85,6 +85,7 @@ public function __construct( private IRequest $request, private IPreview $previewManager, private IUserSession $userSession, + private IFilenameValidator $validator, private bool $isPublic = false, private bool $downloadAttachment = true, ) { @@ -163,8 +164,7 @@ public function checkCopy($source, $destination) { // Ensure the target name is valid try { [, $targetName] = \Sabre\Uri\split($destination); - $validator = \OCP\Server::get(IFilenameValidator::class); - $validator->validateFilename($targetName); + $this->validator->validateFilename($targetName); } catch (InvalidPathException $e) { throw new InvalidPath($e->getMessage(), false); } diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php index 89d9b86becc83..fc35d78a2f754 100644 --- a/apps/dav/lib/Connector/Sabre/ServerFactory.php +++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php @@ -13,6 +13,7 @@ use OCA\DAV\Files\BrowserErrorPagePlugin; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Folder; +use OCP\Files\IFilenameValidator; use OCP\Files\Mount\IMountManager; use OCP\IConfig; use OCP\IDBConnection; @@ -129,6 +130,7 @@ public function createServer(string $baseUri, $this->request, $this->previewManager, $this->userSession, + \OCP\Server::get(IFilenameValidator::class), false, !$this->config->getSystemValue('debug', false) ) diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 4fdd70d05c738..449b8ef0ceb81 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -53,9 +53,13 @@ use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\IFilenameValidator; use OCP\FilesMetadata\IFilesMetadataManager; use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\IPreview; use OCP\IRequest; +use OCP\IUserSession; use OCP\Profiler\IProfiler; use OCP\SabrePluginEvent; use Psr\Log\LoggerInterface; @@ -236,15 +240,17 @@ public function __construct(IRequest $request, string $baseUri) { $user = $userSession->getUser(); if ($user !== null) { $view = \OC\Files\Filesystem::getView(); + $config = \OCP\Server::get(IConfig::class); $this->server->addPlugin( new FilesPlugin( $this->server->tree, - \OC::$server->getConfig(), + $config, $this->request, - \OC::$server->getPreviewManager(), - \OC::$server->getUserSession(), + \OCP\Server::get(IPreview::class), + \OCP\Server::get(IUserSession::class), + \OCP\Server::get(IFilenameValidator::class), false, - !\OC::$server->getConfig()->getSystemValue('debug', false) + $config->getSystemValueBool('debug', false) === false, ) ); $this->server->addPlugin(new ChecksumUpdatePlugin()); diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 144b8af76598f..9d8f4e8d4c4f7 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -9,10 +9,13 @@ use OC\User\User; use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\Node; use OCP\Files\FileInfo; +use OCP\Files\IFilenameValidator; +use OCP\Files\InvalidPathException; use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\IPreview; @@ -32,51 +35,15 @@ * @group DB */ class FilesPluginTest extends TestCase { - public const GETETAG_PROPERTYNAME = FilesPlugin::GETETAG_PROPERTYNAME; - public const FILEID_PROPERTYNAME = FilesPlugin::FILEID_PROPERTYNAME; - public const INTERNAL_FILEID_PROPERTYNAME = FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; - public const SIZE_PROPERTYNAME = FilesPlugin::SIZE_PROPERTYNAME; - public const PERMISSIONS_PROPERTYNAME = FilesPlugin::PERMISSIONS_PROPERTYNAME; - public const LASTMODIFIED_PROPERTYNAME = FilesPlugin::LASTMODIFIED_PROPERTYNAME; - public const CREATIONDATE_PROPERTYNAME = FilesPlugin::CREATIONDATE_PROPERTYNAME; - public const DOWNLOADURL_PROPERTYNAME = FilesPlugin::DOWNLOADURL_PROPERTYNAME; - public const OWNER_ID_PROPERTYNAME = FilesPlugin::OWNER_ID_PROPERTYNAME; - public const OWNER_DISPLAY_NAME_PROPERTYNAME = FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; - public const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; - public const HAS_PREVIEW_PROPERTYNAME = FilesPlugin::HAS_PREVIEW_PROPERTYNAME; - /** - * @var \Sabre\DAV\Server | \PHPUnit\Framework\MockObject\MockObject - */ - private $server; - - /** - * @var \Sabre\DAV\Tree | \PHPUnit\Framework\MockObject\MockObject - */ - private $tree; - - /** - * @var FilesPlugin - */ - private $plugin; - - /** - * @var \OCP\IConfig | \PHPUnit\Framework\MockObject\MockObject - */ - private $config; - - /** - * @var \OCP\IRequest | \PHPUnit\Framework\MockObject\MockObject - */ - private $request; - - /** - * @var \OCP\IPreview | \PHPUnit\Framework\MockObject\MockObject - */ - private $previewManager; - - /** @var IUserSession|MockObject */ - private $userSession; + private Tree&MockObject $tree; + private Server&MockObject $server; + private IConfig&MockObject $config; + private IRequest&MockObject $request; + private IPreview&MockObject $previewManager; + private IUserSession&MockObject $userSession; + private IFilenameValidator&MockObject $filenameValidator; + private FilesPlugin $plugin; protected function setUp(): void { parent::setUp(); @@ -89,13 +56,15 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->previewManager = $this->createMock(IPreview::class); $this->userSession = $this->createMock(IUserSession::class); + $this->filenameValidator = $this->createMock(IFilenameValidator::class); $this->plugin = new FilesPlugin( $this->tree, $this->config, $this->request, $this->previewManager, - $this->userSession + $this->userSession, + $this->filenameValidator, ); $response = $this->getMockBuilder(ResponseInterface::class) @@ -160,16 +129,16 @@ public function testGetPropertiesForFile(): void { $propFind = new PropFind( '/dummyPath', [ - self::GETETAG_PROPERTYNAME, - self::FILEID_PROPERTYNAME, - self::INTERNAL_FILEID_PROPERTYNAME, - self::SIZE_PROPERTYNAME, - self::PERMISSIONS_PROPERTYNAME, - self::DOWNLOADURL_PROPERTYNAME, - self::OWNER_ID_PROPERTYNAME, - self::OWNER_DISPLAY_NAME_PROPERTYNAME, - self::DATA_FINGERPRINT_PROPERTYNAME, - self::CREATIONDATE_PROPERTYNAME, + FilesPlugin::GETETAG_PROPERTYNAME, + FilesPlugin::FILEID_PROPERTYNAME, + FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, + FilesPlugin::SIZE_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::OWNER_ID_PROPERTYNAME, + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::CREATIONDATE_PROPERTYNAME, ], 0 ); @@ -197,16 +166,16 @@ public function testGetPropertiesForFile(): void { $node ); - $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); - $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME)); - $this->assertEquals('123', $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME)); - $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(self::CREATIONDATE_PROPERTYNAME)); - $this->assertEquals(0, $propFind->get(self::SIZE_PROPERTYNAME)); - $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); - $this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals('foo', $propFind->get(self::OWNER_ID_PROPERTYNAME)); - $this->assertEquals('M. Foo', $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME)); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); + $this->assertEquals('123', $propFind->get(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME)); + $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(FilesPlugin::CREATIONDATE_PROPERTYNAME)); + $this->assertEquals(0, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals('http://example.com/', $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals('foo', $propFind->get(FilesPlugin::OWNER_ID_PROPERTYNAME)); + $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); $this->assertEquals([], $propFind->get404Properties()); } @@ -217,7 +186,7 @@ public function testGetPropertiesStorageNotAvailable(): void { $propFind = new PropFind( '/dummyPath', [ - self::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, ], 0 ); @@ -231,25 +200,29 @@ public function testGetPropertiesStorageNotAvailable(): void { $node ); - $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); } public function testGetPublicPermissions(): void { + /** @var IRequest&MockObject */ + $request = $this->getMockBuilder(IRequest::class) + ->disableOriginalConstructor() + ->getMock(); $this->plugin = new FilesPlugin( $this->tree, $this->config, - $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor() - ->getMock(), + $request, $this->previewManager, $this->userSession, - true); + $this->filenameValidator, + true, + ); $this->plugin->initialize($this->server); $propFind = new PropFind( '/dummyPath', [ - self::PERMISSIONS_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, ], 0 ); @@ -265,7 +238,7 @@ public function testGetPublicPermissions(): void { $node ); - $this->assertEquals('DWCKR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals('DWCKR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); } public function testGetPropertiesForDirectory(): void { @@ -275,12 +248,12 @@ public function testGetPropertiesForDirectory(): void { $propFind = new PropFind( '/dummyPath', [ - self::GETETAG_PROPERTYNAME, - self::FILEID_PROPERTYNAME, - self::SIZE_PROPERTYNAME, - self::PERMISSIONS_PROPERTYNAME, - self::DOWNLOADURL_PROPERTYNAME, - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::GETETAG_PROPERTYNAME, + FilesPlugin::FILEID_PROPERTYNAME, + FilesPlugin::SIZE_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -294,13 +267,13 @@ public function testGetPropertiesForDirectory(): void { $node ); - $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); - $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME)); - $this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME)); - $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); - $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); + $this->assertEquals(1025, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals([FilesPlugin::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); } public function testGetPropertiesForRootDirectory(): void { @@ -322,7 +295,7 @@ public function testGetPropertiesForRootDirectory(): void { $propFind = new PropFind( '/', [ - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -332,7 +305,7 @@ public function testGetPropertiesForRootDirectory(): void { $node ); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); } public function testGetPropertiesWhenNoPermission(): void { @@ -358,7 +331,7 @@ public function testGetPropertiesWhenNoPermission(): void { $propFind = new PropFind( '/test', [ - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -392,9 +365,9 @@ public function testUpdateProps(): void { // properties to set $propPatch = new PropPatch([ - self::GETETAG_PROPERTYNAME => 'newetag', - self::LASTMODIFIED_PROPERTYNAME => $testDate, - self::CREATIONDATE_PROPERTYNAME => $testCreationDate, + FilesPlugin::GETETAG_PROPERTYNAME => 'newetag', + FilesPlugin::LASTMODIFIED_PROPERTYNAME => $testDate, + FilesPlugin::CREATIONDATE_PROPERTYNAME => $testCreationDate, ]); @@ -408,19 +381,19 @@ public function testUpdateProps(): void { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertEquals(200, $result[self::LASTMODIFIED_PROPERTYNAME]); - $this->assertEquals(200, $result[self::GETETAG_PROPERTYNAME]); - $this->assertEquals(200, $result[self::CREATIONDATE_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::LASTMODIFIED_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::GETETAG_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::CREATIONDATE_PROPERTYNAME]); } public function testUpdatePropsForbidden(): void { $propPatch = new PropPatch([ - self::OWNER_ID_PROPERTYNAME => 'user2', - self::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', - self::FILEID_PROPERTYNAME => 12345, - self::PERMISSIONS_PROPERTYNAME => 'C', - self::SIZE_PROPERTYNAME => 123, - self::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', + FilesPlugin::OWNER_ID_PROPERTYNAME => 'user2', + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', + FilesPlugin::FILEID_PROPERTYNAME => 12345, + FilesPlugin::PERMISSIONS_PROPERTYNAME => 'C', + FilesPlugin::SIZE_PROPERTYNAME => 123, + FilesPlugin::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', ]); $this->plugin->handleUpdateProperties( @@ -433,16 +406,16 @@ public function testUpdatePropsForbidden(): void { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertEquals(403, $result[self::OWNER_ID_PROPERTYNAME]); - $this->assertEquals(403, $result[self::OWNER_DISPLAY_NAME_PROPERTYNAME]); - $this->assertEquals(403, $result[self::FILEID_PROPERTYNAME]); - $this->assertEquals(403, $result[self::PERMISSIONS_PROPERTYNAME]); - $this->assertEquals(403, $result[self::SIZE_PROPERTYNAME]); - $this->assertEquals(403, $result[self::DOWNLOADURL_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::OWNER_ID_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::FILEID_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::PERMISSIONS_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::SIZE_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::DOWNLOADURL_PROPERTYNAME]); } /** - * Testcase from https://github.com/owncloud/core/issues/5251 + * Test case from https://github.com/owncloud/core/issues/5251 * * |-FolderA * |-text.txt @@ -466,11 +439,12 @@ public function testMoveSrcNotDeletable(): void { $node = $this->getMockBuilder(Node::class) ->disableOriginalConstructor() ->getMock(); - $node->expects($this->once()) + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn($fileInfoFolderATestTXT); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); @@ -487,17 +461,17 @@ public function testMoveSrcDeletable(): void { $node = $this->getMockBuilder(Node::class) ->disableOriginalConstructor() ->getMock(); - $node->expects($this->once()) + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn($fileInfoFolderATestTXT); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } - public function testMoveSrcNotExist(): void { $this->expectException(\Sabre\DAV\Exception\NotFound::class); $this->expectExceptionMessage('FolderA/test.txt does not exist'); @@ -505,16 +479,81 @@ public function testMoveSrcNotExist(): void { $node = $this->getMockBuilder(Node::class) ->disableOriginalConstructor() ->getMock(); - $node->expects($this->once()) + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn(null); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } + public function testMoveDestinationInvalid(): void { + $this->expectException(InvalidPath::class); + $this->expectExceptionMessage('Mocked exception'); + + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); + $fileInfoFolderATestTXT->expects(self::any()) + ->method('isDeletable') + ->willReturn(true); + + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn($fileInfoFolderATestTXT); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->filenameValidator->expects(self::once()) + ->method('validateFilename') + ->with('invalid\\path.txt') + ->willThrowException(new InvalidPathException('Mocked exception')); + + $this->plugin->checkMove('FolderA/test.txt', 'invalid\\path.txt'); + } + + public function testCopySrcNotExist(): void { + $this->expectException(\Sabre\DAV\Exception\NotFound::class); + $this->expectExceptionMessage('FolderA/test.txt does not exist'); + + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn(null); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->plugin->checkCopy('FolderA/test.txt', 'test.txt'); + } + + public function testCopyDestinationInvalid(): void { + $this->expectException(InvalidPath::class); + $this->expectExceptionMessage('Mocked exception'); + + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn($fileInfoFolderATestTXT); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->filenameValidator->expects(self::once()) + ->method('validateFilename') + ->with('invalid\\path.txt') + ->willThrowException(new InvalidPathException('Mocked exception')); + + $this->plugin->checkCopy('FolderA/test.txt', 'invalid\\path.txt'); + } + public function downloadHeadersProvider() { return [ [ @@ -581,7 +620,7 @@ public function testHasPreview(): void { $propFind = new PropFind( '/dummyPath', [ - self::HAS_PREVIEW_PROPERTYNAME + FilesPlugin::HAS_PREVIEW_PROPERTYNAME ], 0 ); @@ -595,6 +634,6 @@ public function testHasPreview(): void { $node ); - $this->assertEquals('false', $propFind->get(self::HAS_PREVIEW_PROPERTYNAME)); + $this->assertEquals('false', $propFind->get(FilesPlugin::HAS_PREVIEW_PROPERTYNAME)); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php index 95fd79fa5f1f0..76a70a93e1313 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php @@ -14,6 +14,7 @@ use OCP\Files\File; use OCP\Files\FileInfo; use OCP\Files\Folder; +use OCP\Files\IFilenameValidator; use OCP\IConfig; use OCP\IGroupManager; use OCP\IPreview; @@ -32,43 +33,20 @@ use Sabre\HTTP\ResponseInterface; class FilesReportPluginTest extends \Test\TestCase { - /** @var \Sabre\DAV\Server|MockObject */ - private $server; - /** @var \Sabre\DAV\Tree|MockObject */ - private $tree; - - /** @var ISystemTagObjectMapper|MockObject */ - private $tagMapper; - - /** @var ISystemTagManager|MockObject */ - private $tagManager; - - /** @var ITags|MockObject */ - private $privateTags; - - private ITagManager|MockObject $privateTagManager; - - /** @var \OCP\IUserSession */ - private $userSession; - - /** @var FilesReportPluginImplementation */ - private $plugin; - - /** @var View|MockObject * */ - private $view; - - /** @var IGroupManager|MockObject * */ - private $groupManager; - - /** @var Folder|MockObject * */ - private $userFolder; - - /** @var IPreview|MockObject * */ - private $previewManager; - - /** @var IAppManager|MockObject * */ - private $appManager; + private \Sabre\DAV\Server&MockObject $server; + private Tree&MockObject $tree; + private ISystemTagObjectMapper&MockObject $tagMapper; + private ISystemTagManager&MockObject $tagManager; + private ITags&MockObject $privateTags; + private ITagManager&MockObject $privateTagManager; + private IUserSession&MockObject $userSession; + private FilesReportPluginImplementation $plugin; + private View&MockObject $view; + private IGroupManager&MockObject $groupManager; + private Folder&MockObject $userFolder; + private IPreview&MockObject $previewManager; + private IAppManager&MockObject $appManager; protected function setUp(): void { parent::setUp(); @@ -82,7 +60,7 @@ protected function setUp(): void { $this->server = $this->getMockBuilder('\Sabre\DAV\Server') ->setConstructorArgs([$this->tree]) - ->setMethods(['getRequestUri', 'getBaseUri']) + ->onlyMethods(['getRequestUri', 'getBaseUri']) ->getMock(); $this->server->expects($this->any()) @@ -311,7 +289,7 @@ public function testFindNodesByFileIdsRoot(): void { $filesNode2, ); - /** @var \OCA\DAV\Connector\Sabre\Directory|MockObject $reportTargetNode */ + /** @var \OCA\DAV\Connector\Sabre\Directory&MockObject $reportTargetNode */ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); $this->assertCount(2, $result); @@ -364,7 +342,7 @@ public function testFindNodesByFileIdsSubDir(): void { $filesNode2, ); - /** @var \OCA\DAV\Connector\Sabre\Directory|MockObject $reportTargetNode */ + /** @var \OCA\DAV\Connector\Sabre\Directory&MockObject $reportTargetNode */ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); $this->assertCount(2, $result); @@ -409,13 +387,16 @@ public function testPrepareResponses(): void { ->disableOriginalConstructor() ->getMock(); + $validator = $this->createMock(IFilenameValidator::class); + $this->server->addPlugin( new \OCA\DAV\Connector\Sabre\FilesPlugin( $this->tree, $config, $this->createMock(IRequest::class), $this->previewManager, - $this->createMock(IUserSession::class) + $this->createMock(IUserSession::class), + $validator, ) ); $this->plugin->initialize($this->server); From b4ca0816953bbd01ca1042cce6065fc35194b935 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 27 Aug 2024 01:05:28 +0200 Subject: [PATCH 09/12] 1 --- apps/dav/lib/Connector/Sabre/Node.php | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 973d5ca6f0ea9..4bdc436ac89a4 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -129,7 +129,6 @@ public function setName($name) { // verify path of the target $this->verifyPath($newPath); - if (!$this->fileView->rename($this->path, $newPath)) { throw new \Sabre\DAV\Exception('Failed to rename '. $this->path . ' to ' . $newPath); } From 1d1797eb315a8017adf56506d1996350df6abec1 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 27 Aug 2024 01:07:51 +0200 Subject: [PATCH 10/12] node --- lib/private/Files/Node/Node.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index 962d1bb6d4400..5dbdc4054bf33 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -463,7 +463,7 @@ public function move($targetPath) { $this->path = $targetPath; return $targetNode; } else { - throw new NotPermittedException('No permission to move to path ' . $targetPath . ($parent instanceof Folder ? 'true':'faöse'). ($this->isValidPath($targetPath) ? 'true':'false')); + throw new NotPermittedException('No permission to move to path ' . $targetPath); } } From a849aa171c27502aa8f8aa2ee47a7afc37db96d0 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 27 Aug 2024 01:45:15 +0200 Subject: [PATCH 11/12] plug --- apps/dav/lib/Connector/Sabre/FilesPlugin.php | 14 +++++++++++++- apps/dav/lib/Connector/Sabre/Node.php | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index adc3e892484bd..7fbf174a89a24 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -22,6 +22,7 @@ use OCP\IPreview; use OCP\IRequest; use OCP\IUserSession; +use OCP\L10N\IFactory; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\IFile; @@ -163,11 +164,22 @@ public function checkCopy($source, $destination) { } // Ensure the target name is valid try { - [, $targetName] = \Sabre\Uri\split($destination); + [$targetPath, $targetName] = \Sabre\Uri\split($destination); $this->validator->validateFilename($targetName); } catch (InvalidPathException $e) { throw new InvalidPath($e->getMessage(), false); } + // Ensure the target path is valid + try { + $segments = array_slice(explode('/', $targetPath), 2); + foreach ($segments as $segment) { + $this->validator->validateFilename($segment); + } + } catch (InvalidPathException) { + $l = \OCP\Server::get(IFactory::class)->get('dav'); + throw new InvalidPath($l->t('Invalid target path')); + } + } /** diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 4bdc436ac89a4..ab07f1e73b01f 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -117,8 +117,8 @@ public function getPath() { * @throws \Sabre\DAV\Exception\Forbidden */ public function setName($name) { - // rename is only allowed if the update privilege is granted - if (!($this->info->isUpdateable() || ($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === ''))) { + // rename is only allowed if the delete privilege is granted + if (!($this->info->isDeletable() || ($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === ''))) { throw new \Sabre\DAV\Exception\Forbidden(); } From f16e2d843dc0e6b7f0c9db3660fe82d435d7e020 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 27 Aug 2024 01:45:53 +0200 Subject: [PATCH 12/12] chore: Replace deprecated functions with new interface (IFilenameValidator) Signed-off-by: Ferdinand Thiessen --- lib/private/Files/Filesystem.php | 18 --- lib/private/Files/Storage/Common.php | 4 +- lib/private/Files/Storage/DAV.php | 4 +- lib/private/Files/Storage/Local.php | 8 +- lib/private/Files/View.php | 220 +++++++++++++-------------- tests/lib/Files/FilesystemTest.php | 22 --- 6 files changed, 122 insertions(+), 154 deletions(-) diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 48c069de0b95c..2cd37df67c796 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -29,9 +29,6 @@ class Filesystem { private static ?CappedMemoryCache $normalizedPathCache = null; - /** @var string[]|null */ - private static ?array $blacklist = null; - /** * classname which used for hooks handling * used as signalclass in OC_Hooks::emit() @@ -428,21 +425,6 @@ public static function isValidPath($path) { return true; } - /** - * @param string $filename - * @return bool - */ - public static function isFileBlacklisted($filename) { - $filename = self::normalizePath($filename); - - if (self::$blacklist === null) { - self::$blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', ['.htaccess']); - } - - $filename = strtolower(basename($filename)); - return in_array($filename, self::$blacklist); - } - /** * check if the directory should be ignored when scanning * NOTE: the special directories . and .. would cause never ending recursion diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 77e5b95fc20be..207495a6712d6 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -630,7 +630,9 @@ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $t * @inheritdoc */ public function getMetaData($path) { - if (Filesystem::isFileBlacklisted($path)) { + /** @var \OC\Files\FilenameValidator */ + $validator = $this->getFilenameValidator(); + if ($validator->isForbidden($path)) { throw new ForbiddenException('Invalid path: ' . $path, false); } diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 7abc0ccc18245..887dc50305eee 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -561,7 +561,9 @@ public function copy($source, $target) { } public function getMetaData($path) { - if (Filesystem::isFileBlacklisted($path)) { + /** @var \OC\Files\FilenameValidator */ + $validator = $this->getFilenameValidator(); + if ($validator->isForbidden($path)) { throw new ForbiddenException('Invalid path: ' . $path, false); } $response = $this->propfind($path); diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index a65d60bf278dd..2061866ad47e7 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -318,9 +318,11 @@ public function unlink($path) { private function checkTreeForForbiddenItems(string $path) { $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); + /** @var \OC\Files\FilenameValidator */ + $validator = $this->getFilenameValidator(); foreach ($iterator as $file) { /** @var \SplFileInfo $file */ - if (Filesystem::isFileBlacklisted($file->getBasename())) { + if ($validator->isForbidden($file->getBasename())) { throw new ForbiddenException('Invalid path: ' . $file->getPathname(), false); } } @@ -475,7 +477,9 @@ public function hasUpdated($path, $time) { * @throws ForbiddenException */ public function getSourcePath($path) { - if (Filesystem::isFileBlacklisted($path)) { + /** @var \OC\Files\FilenameValidator */ + $validator = $this->getFilenameValidator(); + if ($validator->isForbidden($path)) { throw new ForbiddenException('Invalid path: ' . $path, false); } diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index ca0320f55219d..f6dd33fb0d335 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -590,7 +590,7 @@ public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) - && !Filesystem::isFileBlacklisted($path) + && !$this->filenameValidator->isForbidden($path) ) { $path = $this->getRelativePath($absolutePath); if ($path === null) { @@ -702,133 +702,133 @@ public function rename($source, $target) { throw new ForbiddenException('Moving a folder into a child folder is forbidden', false); } - $targetParts = explode('/', $absolutePath2); - $targetUser = $targetParts[1] ?? null; - $result = false; - if ( - Filesystem::isValidPath($target) - && Filesystem::isValidPath($source) - && !Filesystem::isFileBlacklisted($target) + if (!Filesystem::isValidPath($target) + || !Filesystem::isValidPath($source) ) { - $source = $this->getRelativePath($absolutePath1); - $target = $this->getRelativePath($absolutePath2); - $exists = $this->file_exists($target); + return false; + } - if ($source == null || $target == null) { - return false; - } + $source = $this->getRelativePath($absolutePath1); + $target = $this->getRelativePath($absolutePath2); + $exists = $this->file_exists($target); - try { - $this->verifyPath(dirname($target), basename($target)); - } catch (InvalidPathException) { - return false; - } + if ($source == null || $target == null) { + return false; + } - $this->lockFile($source, ILockingProvider::LOCK_SHARED, true); - try { - $this->lockFile($target, ILockingProvider::LOCK_SHARED, true); + try { + $this->verifyPath(dirname($target), basename($target)); + } catch (InvalidPathException) { + return false; + } - $run = true; - if ($this->shouldEmitHooks($source) && (Cache\Scanner::isPartialFile($source) && !Cache\Scanner::isPartialFile($target))) { - // if it was a rename from a part file to a regular file it was a write and not a rename operation - $this->emit_file_hooks_pre($exists, $target, $run); - } elseif ($this->shouldEmitHooks($source)) { - $sourcePath = $this->getHookPath($source); - $targetPath = $this->getHookPath($target); - if ($sourcePath !== null && $targetPath !== null) { - \OC_Hook::emit( - Filesystem::CLASSNAME, Filesystem::signal_rename, - [ - Filesystem::signal_param_oldpath => $sourcePath, - Filesystem::signal_param_newpath => $targetPath, - Filesystem::signal_param_run => &$run - ] - ); - } - } - if ($run) { - $manager = Filesystem::getMountManager(); - $mount1 = $this->getMount($source); - $mount2 = $this->getMount($target); - $storage1 = $mount1->getStorage(); - $storage2 = $mount2->getStorage(); - $internalPath1 = $mount1->getInternalPath($absolutePath1); - $internalPath2 = $mount2->getInternalPath($absolutePath2); + $targetParts = explode('/', $absolutePath2); + $targetUser = $targetParts[1] ?? null; + $result = false; + $this->lockFile($source, ILockingProvider::LOCK_SHARED, true); + try { + $this->lockFile($target, ILockingProvider::LOCK_SHARED, true); - $this->changeLock($source, ILockingProvider::LOCK_EXCLUSIVE, true); - try { - $this->changeLock($target, ILockingProvider::LOCK_EXCLUSIVE, true); - - if ($internalPath1 === '') { - if ($mount1 instanceof MoveableMount) { - $sourceParentMount = $this->getMount(dirname($source)); - if ($sourceParentMount === $mount2 && $this->targetIsNotShared($targetUser, $absolutePath2)) { - /** - * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 - */ - $sourceMountPoint = $mount1->getMountPoint(); - $result = $mount1->moveMount($absolutePath2); - $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); - } else { - $result = false; - } - } else { - $result = false; - } - // moving a file/folder within the same mount point - } elseif ($storage1 === $storage2) { - if ($storage1) { - $result = $storage1->rename($internalPath1, $internalPath2); + $run = true; + if ($this->shouldEmitHooks($source) && (Cache\Scanner::isPartialFile($source) && !Cache\Scanner::isPartialFile($target))) { + // if it was a rename from a part file to a regular file it was a write and not a rename operation + $this->emit_file_hooks_pre($exists, $target, $run); + } elseif ($this->shouldEmitHooks($source)) { + $sourcePath = $this->getHookPath($source); + $targetPath = $this->getHookPath($target); + if ($sourcePath !== null && $targetPath !== null) { + \OC_Hook::emit( + Filesystem::CLASSNAME, Filesystem::signal_rename, + [ + Filesystem::signal_param_oldpath => $sourcePath, + Filesystem::signal_param_newpath => $targetPath, + Filesystem::signal_param_run => &$run + ] + ); + } + } + if ($run) { + $manager = Filesystem::getMountManager(); + $mount1 = $this->getMount($source); + $mount2 = $this->getMount($target); + $storage1 = $mount1->getStorage(); + $storage2 = $mount2->getStorage(); + $internalPath1 = $mount1->getInternalPath($absolutePath1); + $internalPath2 = $mount2->getInternalPath($absolutePath2); + + $this->changeLock($source, ILockingProvider::LOCK_EXCLUSIVE, true); + try { + $this->changeLock($target, ILockingProvider::LOCK_EXCLUSIVE, true); + + if ($internalPath1 === '') { + if ($mount1 instanceof MoveableMount) { + $sourceParentMount = $this->getMount(dirname($source)); + if ($sourceParentMount === $mount2 && $this->targetIsNotShared($targetUser, $absolutePath2)) { + /** + * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 + */ + $sourceMountPoint = $mount1->getMountPoint(); + $result = $mount1->moveMount($absolutePath2); + $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); } else { $result = false; } - // moving a file/folder between storages (from $storage1 to $storage2) } else { - $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); + $result = false; } - - if ((Cache\Scanner::isPartialFile($source) && !Cache\Scanner::isPartialFile($target)) && $result !== false) { - // if it was a rename from a part file to a regular file it was a write and not a rename operation - $this->writeUpdate($storage2, $internalPath2); - } elseif ($result) { - if ($internalPath1 !== '') { // don't do a cache update for moved mounts - $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); - } + // moving a file/folder within the same mount point + } elseif ($storage1 === $storage2) { + if ($storage1) { + $result = $storage1->rename($internalPath1, $internalPath2); + } else { + $result = false; } - } catch (\Exception $e) { - throw $e; - } finally { - $this->changeLock($source, ILockingProvider::LOCK_SHARED, true); - $this->changeLock($target, ILockingProvider::LOCK_SHARED, true); + // moving a file/folder between storages (from $storage1 to $storage2) + } else { + $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } if ((Cache\Scanner::isPartialFile($source) && !Cache\Scanner::isPartialFile($target)) && $result !== false) { - if ($this->shouldEmitHooks()) { - $this->emit_file_hooks_post($exists, $target); - } + // if it was a rename from a part file to a regular file it was a write and not a rename operation + $this->writeUpdate($storage2, $internalPath2); } elseif ($result) { - if ($this->shouldEmitHooks($source) && $this->shouldEmitHooks($target)) { - $sourcePath = $this->getHookPath($source); - $targetPath = $this->getHookPath($target); - if ($sourcePath !== null && $targetPath !== null) { - \OC_Hook::emit( - Filesystem::CLASSNAME, - Filesystem::signal_post_rename, - [ - Filesystem::signal_param_oldpath => $sourcePath, - Filesystem::signal_param_newpath => $targetPath, - ] - ); - } + if ($internalPath1 !== '') { // don't do a cache update for moved mounts + $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); + } + } + } catch (\Exception $e) { + throw $e; + } finally { + $this->changeLock($source, ILockingProvider::LOCK_SHARED, true); + $this->changeLock($target, ILockingProvider::LOCK_SHARED, true); + } + + if ((Cache\Scanner::isPartialFile($source) && !Cache\Scanner::isPartialFile($target)) && $result !== false) { + if ($this->shouldEmitHooks()) { + $this->emit_file_hooks_post($exists, $target); + } + } elseif ($result) { + if ($this->shouldEmitHooks($source) && $this->shouldEmitHooks($target)) { + $sourcePath = $this->getHookPath($source); + $targetPath = $this->getHookPath($target); + if ($sourcePath !== null && $targetPath !== null) { + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_rename, + [ + Filesystem::signal_param_oldpath => $sourcePath, + Filesystem::signal_param_newpath => $targetPath, + ] + ); } } } - } catch (\Exception $e) { - throw $e; - } finally { - $this->unlockFile($source, ILockingProvider::LOCK_SHARED, true); - $this->unlockFile($target, ILockingProvider::LOCK_SHARED, true); } + } catch (\Exception $e) { + throw $e; + } finally { + $this->unlockFile($source, ILockingProvider::LOCK_SHARED, true); + $this->unlockFile($target, ILockingProvider::LOCK_SHARED, true); } return $result; } @@ -849,7 +849,7 @@ public function copy($source, $target, $preserveMtime = false) { if ( Filesystem::isValidPath($target) && Filesystem::isValidPath($source) - && !Filesystem::isFileBlacklisted($target) + && !$this->filenameValidator->isForbidden($target) ) { $source = $this->getRelativePath($absolutePath1); $target = $this->getRelativePath($absolutePath2); @@ -1106,7 +1106,7 @@ private function basicOperation(string $operation, string $path, array $hooks = $postFix = (substr($path, -1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) - && !Filesystem::isFileBlacklisted($path) + && !$this->filenameValidator->isForbidden($path) ) { $path = $this->getRelativePath($absolutePath); if ($path == null) { diff --git a/tests/lib/Files/FilesystemTest.php b/tests/lib/Files/FilesystemTest.php index f7964feeae8ab..f2f5c35577308 100644 --- a/tests/lib/Files/FilesystemTest.php +++ b/tests/lib/Files/FilesystemTest.php @@ -258,28 +258,6 @@ public function testIsValidPath($path, $expected) { $this->assertSame($expected, \OC\Files\Filesystem::isValidPath($path)); } - public function isFileBlacklistedData() { - return [ - ['/etc/foo/bar/foo.txt', false], - ['\etc\foo/bar\foo.txt', false], - ['.htaccess', true], - ['.htaccess/', true], - ['.htaccess\\', true], - ['/etc/foo\bar/.htaccess\\', true], - ['/etc/foo\bar/.htaccess/', true], - ['/etc/foo\bar/.htaccess/foo', false], - ['//foo//bar/\.htaccess/', true], - ['\foo\bar\.HTAccess', true], - ]; - } - - /** - * @dataProvider isFileBlacklistedData - */ - public function testIsFileBlacklisted($path, $expected) { - $this->assertSame($expected, \OC\Files\Filesystem::isFileBlacklisted($path)); - } - public function testNormalizePathUTF8() { if (!class_exists('Patchwork\PHP\Shim\Normalizer')) { $this->markTestSkipped('UTF8 normalizer Patchwork was not found');