Skip to content

Commit 33e3743

Browse files
committed
fix: ExApp upgrade 401 on set_init_status
Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com>
1 parent f221313 commit 33e3743

3 files changed

Lines changed: 398 additions & 2 deletions

File tree

.github/workflows/phpunit.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
2+
# SPDX-License-Identifier: MIT
3+
name: PHPUnit
4+
5+
on:
6+
pull_request:
7+
push:
8+
branches: [main]
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
concurrency:
15+
group: phpunit-${{ github.head_ref || github.run_id }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
phpunit:
20+
runs-on: ubuntu-22.04
21+
name: PHPUnit • PHP ${{ matrix.php-version }} • ${{ matrix.server-version }}
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
php-version: ['8.2', '8.3']
26+
server-version: ['master']
27+
28+
steps:
29+
- name: Set app env
30+
run: echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
31+
32+
- name: Checkout server
33+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
34+
with:
35+
persist-credentials: false
36+
submodules: true
37+
repository: nextcloud/server
38+
ref: ${{ matrix.server-version }}
39+
40+
- name: Checkout AppAPI
41+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
42+
with:
43+
persist-credentials: false
44+
path: apps/${{ env.APP_NAME }}
45+
46+
- name: Set up PHP ${{ matrix.php-version }}
47+
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2
48+
with:
49+
php-version: ${{ matrix.php-version }}
50+
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite3, pdo_sqlite
51+
coverage: none
52+
ini-file: development
53+
env:
54+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
55+
56+
- name: Install app dependencies
57+
working-directory: apps/${{ env.APP_NAME }}
58+
run: composer i
59+
60+
- name: Set up Nextcloud
61+
run: |
62+
mkdir data
63+
./occ maintenance:install --verbose --database=sqlite --admin-user admin --admin-pass admin
64+
./occ app:enable --force ${{ env.APP_NAME }}
65+
66+
- name: Run PHPUnit
67+
working-directory: apps/${{ env.APP_NAME }}
68+
run: composer run test:unit
69+
70+
phpunit-summary:
71+
permissions:
72+
contents: none
73+
runs-on: ubuntu-22.04
74+
needs: [phpunit]
75+
name: PHPUnit-OK
76+
steps:
77+
- run: echo "PHPUnit tests passed successfully"

lib/Service/AppAPIService.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false)
309309
$this->logger->error(sprintf('Error getting path info. Error: %s', $e->getMessage()), ['exception' => $e]);
310310
return false;
311311
}
312-
if (($this->sanitizeOcsRoute($path) !== '/apps/app_api/ex-app/state') && !$exApp->getEnabled()) {
312+
if (!$exApp->getEnabled() && !$this->isExemptFromEnabledCheck($path, $exApp)) {
313313
$this->logger->error(sprintf('ExApp with appId %s is disabled (%s)', $request->getHeader('EX-APP-ID'), $request->getRequestUri()));
314314
return false;
315315
}
@@ -368,6 +368,28 @@ private function sanitizeOcsRoute(string $route): string {
368368
return $route;
369369
}
370370

371+
/**
372+
* Check if the given path is exempt from the ExApp enabled check.
373+
* /ex-app/state is always exempt (query enabled state).
374+
* Init status endpoints are only exempt while the ExApp is actively being
375+
* installed or updated (status type set server-side by Register/Update commands),
376+
* preventing a disabled ExApp from re-enabling itself via set_init_status(100).
377+
*/
378+
private function isExemptFromEnabledCheck(string $path, ExApp $exApp): bool {
379+
$sanitizedPath = $this->sanitizeOcsRoute($path);
380+
if ($sanitizedPath === '/apps/app_api/ex-app/state') {
381+
return true;
382+
}
383+
$status = $exApp->getStatus();
384+
$isInitializing = in_array($status['type'] ?? '', ['install', 'update'], true);
385+
if ($isInitializing) {
386+
if ($sanitizedPath === '/apps/app_api/ex-app/status') {
387+
return true;
388+
}
389+
}
390+
return false;
391+
}
392+
371393
private function getCustomLogger(string $name): LoggerInterface {
372394
$path = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $name;
373395
return $this->logFactory->getCustomPsrLogger($path);
@@ -411,7 +433,11 @@ public function dispatchExAppInitInternal(ExApp $exApp): void {
411433
}
412434

413435
$this->setAppInitProgress($exApp, 0);
414-
$this->exAppService->enableExAppInternal($exApp);
436+
if (!$this->exAppService->enableExAppInternal($exApp)) {
437+
$this->logger->error(sprintf('Failed to enable ExApp %s before init dispatch', $exApp->getAppid()));
438+
$this->setAppInitProgress($exApp, 0, 'Failed to enable ExApp before init');
439+
return;
440+
}
415441
try {
416442
$this->client->post($initUrl, $options);
417443
} catch (\Exception $e) {

0 commit comments

Comments
 (0)