Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
21949ac
feat: add database migration creating the office_wopi token table
moodyjmz May 22, 2026
c003c89
feat: add ExpiredTokenException for WOPI token validation
moodyjmz May 22, 2026
7215949
feat: add UnknownTokenException for WOPI token lookup failures
moodyjmz May 22, 2026
e9ca33f
feat: add Wopi entity mapping the office_wopi table
moodyjmz May 22, 2026
8fad2c9
feat: add WopiMapper for token persistence and lookup
moodyjmz May 22, 2026
877e2f1
feat: add DiscoveryService to fetch and cache WOPI discovery XML
moodyjmz May 22, 2026
0337a41
feat: add TokenManager to generate WOPI access tokens for files
moodyjmz May 22, 2026
4eec74a
feat: add WopiController implementing CheckFileInfo, GetFile and PutFile
moodyjmz May 22, 2026
65b0ae9
✨ feat: register TokenManager in DI container with session user injec…
moodyjmz May 22, 2026
16a60e9
✨ feat: add Admin settings class registering the office settings section
moodyjmz May 22, 2026
e11b7ec
✨ feat: register Admin settings in Application bootstrap
moodyjmz May 22, 2026
6d67bef
✨ feat: add SettingsController with admin get/set endpoints
moodyjmz May 22, 2026
90f0cff
✨ feat: add admin settings PHP template
moodyjmz May 22, 2026
4d3d35c
✨ feat: add AdminSettings Vue component
moodyjmz May 22, 2026
a3290f7
✨ feat: add settings-admin entry point mounting AdminSettings
moodyjmz May 22, 2026
c624a6e
✨ feat: add EditorController generating WOPI token and editor URL
moodyjmz May 22, 2026
c392e2c
✨ feat: add editor PHP template passing editor URL to Vue
moodyjmz May 22, 2026
6786249
✨ feat: add Editor Vue component rendering the WOPI iframe with origi…
moodyjmz May 22, 2026
9fb1254
✨ feat: add editor entry point mounting Editor component
moodyjmz May 22, 2026
e05fcbb
🔧 chore: add editor and settings-admin entry points to vite config
moodyjmz May 22, 2026
560d10e
🐛 fix: correct query-string separator in buildEditorUrl
moodyjmz May 22, 2026
5fdcb1e
🔒 fix(security): prevent XPath injection via file extension in getUrlSrc
moodyjmz May 22, 2026
0633cd8
🔒 fix(security): validate wopi_url scheme in setAdmin to limit SSRF s…
moodyjmz May 22, 2026
8c6bb7d
🔒 fix(security): fix range-request handling and close file handles in…
moodyjmz May 22, 2026
d915c32
✨ feat: add hourly CleanupJob to purge expired WOPI tokens
moodyjmz May 22, 2026
edf48da
✨ feat: add office_wopi_locks migration for WOPI lock storage
moodyjmz May 23, 2026
6452bcf
✨ feat: add WopiLock entity and WopiLockMapper
moodyjmz May 23, 2026
82e136e
✨ feat: add WOPI lock operations (Lock, Unlock, RefreshLock, GetLock)
moodyjmz May 23, 2026
5d5ad41
🔒 fix(security): enforce WOPI lock and version checks on PutFile
moodyjmz May 23, 2026
113e6a6
♻️ refactor: extend CleanupJob to also purge expired WOPI locks
moodyjmz May 23, 2026
fa2c5b2
🔧 chore: move background-jobs and settings registration to info.xml
moodyjmz May 23, 2026
f00c0df
🐛 fix: guard fclose() on php://input after putContent()
moodyjmz May 23, 2026
f7b607e
🐛 fix: set Content-Type: application/octet-stream on WOPI GetFile res…
moodyjmz May 23, 2026
4a401bd
🔒 fix(security): reject WOPI lock operations on read-only tokens
moodyjmz May 23, 2026
0dc6078
✨ feat: add hide_download support to WOPI token row
moodyjmz May 23, 2026
7a6dfa3
✨ feat: add public ShareController for WOPI share-link access
moodyjmz May 23, 2026
0b2dd6d
✨ feat: propagate hideDownload flags in CheckFileInfo response
moodyjmz May 23, 2026
f3cc0ae
🔒 fix(security): sanitize guestName and document P3 known gaps
moodyjmz May 23, 2026
fc09a4d
✨ feat: add DiscoveryService::getSupportedMimeTypes()
moodyjmz May 23, 2026
5f6c2c0
✨ feat: issue user token for authenticated visitors on share links
moodyjmz May 23, 2026
3fbe1d4
✨ feat: inject file-actions script and MIME types on Files app pages
moodyjmz May 23, 2026
d44e17e
✨ feat: register Files app file action to open documents in Office
moodyjmz May 23, 2026
d8242c3
🐛 fix: use history.back() on UI_Close instead of window.close()
moodyjmz May 23, 2026
f39ea44
📝 docs: replace scaffold README with WOPI backend developer guide
moodyjmz May 24, 2026
00cd446
🐛 fix: resolve genuine Psalm errors across WOPI backend
moodyjmz May 24, 2026
464d0af
🔧 chore: upgrade Psalm to v6 and add baseline for false positives
moodyjmz May 24, 2026
5484887
🎨 style: apply PHP CS Fixer (import ordering, docblock alignment)
moodyjmz May 24, 2026
618c358
✨ feat(wopi): implement RenameFile and fix CheckFileInfo capability f…
moodyjmz May 24, 2026
ae59dd3
📝 docs: update README — WOPI spec compliance table, generic setup
moodyjmz May 24, 2026
26ffa9f
🐛 fix: add missing Application import in LoadAdditionalScriptsListener
moodyjmz May 25, 2026
2baf917
🔒 fix(wopi): close C-1 path traversal, H-3 unlocked writes, H-2 lock …
moodyjmz May 25, 2026
5934af7
🔧 fix(psalm): remove stale PossiblyUnusedMethod baseline entry for To…
moodyjmz May 25, 2026
13bfbf9
chore(psalm): bump phpVersion to 8.3 and regenerate baseline
moodyjmz Jul 1, 2026
20672f9
chore(deps): register @nextcloud/sharing as a root dependency
moodyjmz Jul 1, 2026
f7db7fe
style: fix eslint violations in AdminSettings and Editor
moodyjmz Jul 1, 2026
994583f
fix(settings): register admin settings routes as FrontpageRoute
moodyjmz Jul 1, 2026
da6e8f3
feat(wopi): add public_wopi_url and callback_url for split-URL deploy…
moodyjmz Jul 1, 2026
7120976
test(bootstrap): split standalone unit tests from in-server integrati…
moodyjmz Jul 1, 2026
0b8a53c
test(discovery): add DiscoveryService unit tests and discovery fixtures
moodyjmz Jul 1, 2026
4d016da
test(spec): add WOPI coverage matrix and spec-coverage report script
moodyjmz Jul 1, 2026
717ad99
test(wopi): add WopiController lock and rename-file unit tests
moodyjmz Jul 2, 2026
b0c7ef8
test(token): add TokenManager unit tests
moodyjmz Jul 2, 2026
0df0fc1
fix(tests): make integration bootstrap independent of mount layout
moodyjmz Jul 2, 2026
9b97321
test(integration): add WOPI integration suite running in a server tree
moodyjmz Jul 2, 2026
f6b69b2
fix(psalm): drop stale baseline entry for DiscoveryService::resetCache
moodyjmz Jul 2, 2026
9bfac81
fix(share): treat null and empty password as unprotected in openShare
moodyjmz Jul 2, 2026
b0335ee
fix(wopi): reject backslash-based names in RenameFile's traversal guard
moodyjmz Jul 2, 2026
9af16a7
style: fix import ordering in SettingsController
moodyjmz Jul 2, 2026
207b201
chore(psalm): prune baseline/config entries left stale by the main re…
moodyjmz Aug 28, 2026
b74e981
fix(wopi): submit editor iframe via hidden POST form, not GET src
moodyjmz Sep 7, 2026
6f5fc3d
fix(tests): repoint OCP stub autoload at the vendor-bin namespace
moodyjmz Sep 7, 2026
e7356db
fix: 🐛 allow the editor origin in the editor page CSP
moodyjmz Oct 23, 2026
13dbe76
fix: 🐛 always advertise WOPI lock support in CheckFileInfo
moodyjmz Oct 26, 2026
4d4d9d0
fix: 🐛 detect out-of-band changes from the editor's WOPI timestamp
moodyjmz Oct 26, 2026
4bc5a47
fix: 🐛 import t in the admin settings page so it renders
moodyjmz Oct 26, 2026
0d56a24
fix: 🐛 use variant for the admin save button style
moodyjmz Oct 26, 2026
59724b9
fix: 🐛 save admin settings through the router-generated URL
moodyjmz Oct 26, 2026
518dcab
fix: 🐛 pass the WOPI token expiry to the editor as access_token_ttl
moodyjmz Oct 26, 2026
a5c7999
fix: 🐛 stop discovery failures from stalling every Files page load
moodyjmz Oct 26, 2026
379658a
fix: 🐛 let logged-in users open link shares they can't otherwise reach
moodyjmz Oct 26, 2026
da23a84
docs: 📝 correct the README on share tokens and logged-in visitors
moodyjmz Oct 26, 2026
d0d9b82
fix: 🐛 report an unreachable editor server as 503, not unsupported type
moodyjmz Oct 26, 2026
a037b17
test: ✅ cover logged-in link visitors and pin every pre-password refusal
moodyjmz Oct 26, 2026
d59bb30
docs: 📝 describe share-link sessions and the discovery failure cache …
moodyjmz Oct 26, 2026
d36bf76
chore: 🔧 bump app version to 2.0.0-dev.1
moodyjmz Oct 26, 2026
e1de8c1
chore: 🔧 suppress psalm's unused-code false positives for wired code
moodyjmz Oct 26, 2026
6322313
fix: 🔒 re-check share-link WOPI tokens against the live share
moodyjmz Oct 26, 2026
5a33c1a
docs: 📝 describe share-bound WOPI tokens in the README
moodyjmz Oct 26, 2026
13770fe
fix: 🔒 rate-limit and brute-force protect the public share-open route
moodyjmz Oct 26, 2026
8d0b1a6
fix: 🐛 let the token cleanup keep up with share-link traffic
moodyjmz Oct 26, 2026
f09cf97
build: ⬆️ bundle phpseclib3 for WOPI proof-key verification
moodyjmz Oct 26, 2026
c124459
refactor: ♻️ extract the editor-facing URL rewrite from buildEditorUrl
moodyjmz Oct 26, 2026
d035a75
fix: 🔒 require the editor's WOPI proof signature when it publishes keys
moodyjmz Oct 26, 2026
b018006
feat: 🔒 optional allowlist of editor addresses for the WOPI endpoints
moodyjmz Oct 26, 2026
02ab6f7
feat: 🔒 warn admins when nothing checks who calls the WOPI endpoints
moodyjmz Oct 26, 2026
6134159
docs: 📝 document the WOPI caller checks and the bundled PHP dependency
moodyjmz Oct 26, 2026
4582256
fix: 🔒 honour "download disabled" on shares users open in the editor
moodyjmz Oct 26, 2026
18538a6
fix: 🐛 offer "Open in Office" on public share pages
moodyjmz Oct 26, 2026
dc0e90d
fix: 🐛 don't empty a document when PutFile can't read the request body
moodyjmz Oct 26, 2026
319868b
fix: 🔒 make the office settings routes plainly admin-only
moodyjmz Oct 26, 2026
b2a07bd
fix: 🐛 handle the null and untyped values psalm had baselined
moodyjmz Oct 26, 2026
b0c2994
chore: 🔧 resolve the last psalm findings and drop the baseline
moodyjmz Oct 26, 2026
bb5930e
fix: 🐛 keep the OCP test stubs out of the app's runtime autoloader
moodyjmz Oct 26, 2026
e983c8e
fix: 🐛 use the phpseclib 3 Nextcloud ships instead of bundling a copy
moodyjmz Oct 27, 2026
24c9fdd
test: ✅ update the integration tests for the changed mapper signatures
moodyjmz Oct 27, 2026
69d2f76
test: ✅ run the integration tests against a stub editor that signs re…
moodyjmz Oct 27, 2026
b61ea79
test: ✅ let the integration tests run against any Nextcloud install
moodyjmz Oct 27, 2026
64a993f
ci: 🔧 run the PHPUnit unit and integration tests on pull requests
moodyjmz Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Based on the organization's phpunit-*.yml templates
# (https://github.com/nextcloud/.github), merged into one workflow: the
# integration suite needs OFFICE_TEST_BASE_URL and runs once per database.
#
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: PHPUnit

on: pull_request

permissions:
contents: read

concurrency:
group: phpunit-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
changes-and-matrix:
runs-on: ubuntu-latest-low
permissions:
contents: read
pull-requests: read

outputs:
src: ${{ steps.changes.outputs.src }}
php-versions: ${{ steps.versions.outputs.php-available-list }}
php-max: ${{ steps.versions.outputs.php-max }}
server-max: ${{ steps.versions.outputs.branches-max }}

steps:
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: changes
continue-on-error: true
with:
filters: |
src:
- '.github/workflows/**'
- 'appinfo/**'
- 'lib/**'
- 'templates/**'
- 'tests/**'
- 'vendor-bin/**'
- 'composer.json'
- 'composer.lock'

- name: Checkout app
if: steps.changes.outputs.src != 'false'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Get version matrix
if: steps.changes.outputs.src != 'false'
id: versions
uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3

unit:
runs-on: ubuntu-latest

needs: changes-and-matrix
if: needs.changes-and-matrix.outputs.src != 'false'

strategy:
fail-fast: false
matrix:
php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-versions) }}

name: Unit PHP ${{ matrix.php-versions }}

steps:
- name: Checkout app
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: ${{ matrix.php-versions }}
extensions: ctype, json, mbstring, openssl, simplexml
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Install composer dependencies
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0

- name: PHPUnit
run: composer run test:unit

integration:
runs-on: ubuntu-latest

needs: changes-and-matrix
if: needs.changes-and-matrix.outputs.src != 'false'

strategy:
fail-fast: false
matrix:
database: ['sqlite', 'mysql', 'pgsql']

name: Integration ${{ matrix.database }} PHP ${{ needs.changes-and-matrix.outputs.php-max }} Nextcloud ${{ needs.changes-and-matrix.outputs.server-max }}

# Both start in every job; only the matrix database is used.
services:
mysql:
image: ghcr.io/nextcloud/continuous-integration-mysql-8.4:latest # zizmor: ignore[unpinned-images]
ports:
- 4444:3306/tcp
env:
MYSQL_ROOT_PASSWORD: rootpassword
options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10
postgres:
image: ghcr.io/nextcloud/continuous-integration-postgres-16:latest # zizmor: ignore[unpinned-images]
ports:
- 4445:5432/tcp
env:
POSTGRES_USER: root
POSTGRES_PASSWORD: rootpassword
POSTGRES_DB: nextcloud
options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5

steps:
- name: Checkout server
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: true
repository: nextcloud/server
ref: ${{ needs.changes-and-matrix.outputs.server-max }}

- name: Checkout app
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
path: apps/office

- name: Set up php ${{ needs.changes-and-matrix.outputs.php-max }}
uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: ${{ needs.changes-and-matrix.outputs.php-max }}
# 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, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, mysql, pdo_mysql, pgsql, pdo_pgsql
coverage: none
ini-file: development
# Temporary workaround for missing pcntl_* in PHP 8.3
ini-values: disable_functions=
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Install composer dependencies
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
with:
working-directory: apps/office

- name: Set up Nextcloud
env:
DATABASE: ${{ matrix.database }}
run: |
case "$DATABASE" in
mysql) db_args=(--database-host=127.0.0.1 --database-port=4444) ;;
pgsql) db_args=(--database-host=127.0.0.1 --database-port=4445) ;;
*) db_args=() ;;
esac
mkdir data
./occ maintenance:install --verbose --database="$DATABASE" --database-name=nextcloud --database-user=root --database-pass=rootpassword "${db_args[@]}" --admin-user admin --admin-pass admin
./occ app:enable --force office

- name: Run Nextcloud
env:
PHP_CLI_SERVER_WORKERS: 4
run: php -S localhost:8080 &

- name: PHPUnit integration
working-directory: apps/office
env:
NEXTCLOUD_ROOT: ${{ github.workspace }}
OFFICE_TEST_BASE_URL: http://localhost:8080
# composer stops scripts after 300 s by default
COMPOSER_PROCESS_TIMEOUT: 0
run: composer run test:integration

- name: Print logs
if: always()
run: cat data/nextcloud.log

summary:
permissions:
contents: none
runs-on: ubuntu-latest-low
needs: [changes-and-matrix, unit, integration]

if: always()

name: phpunit-summary

steps:
- name: Summary status
run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && (needs.unit.result != 'success' || needs.integration.result != 'success') }}; then exit 1; fi
Loading
Loading