fix: Fix theming for disabled accounts

The Theming app injects the stylesheets for the different themes in the
"<header>" element of the page, and those stylesheets are then loaded by
the browser from a "Controller" (a plain "Controller", not an
"OCSController"). The stylesheets, in turn, may also get some images
(like the background) also from the "Controller".

When handling a request to "index.php" it is checked whether the user is
logged in and, if not, a login is tried. A disabled user is explicitly
seen as not logged in, so a login is always tried in that case, but
disabled users are also explicitly prevented to log in, so the login
also fails. Due to that trying to get any of the themed stylesheets or
images with a disabled account (to be able to show the "Account
disabled" error page) fails with an HTTP status 401.

To solve that, and to avoid touching this basic logic as much as
possible, the login exception is now ignored (if the user is disabled)
for some specific requests to the Theming app.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez
2025-06-05 04:25:34 +02:00
parent 4fcadd630b
commit 01766b18f8
6 changed files with 214 additions and 1 deletions

View File

@ -70,6 +70,7 @@ jobs:
- 'setup_features'
- 'sharees_features'
- 'sharing_features'
- 'theming_features'
- 'videoverification_features'
php-versions: ['8.1']

View File

@ -266,3 +266,13 @@ default:
- admin
- admin
regular_user_password: 123456
theming:
paths:
- "%paths.base%/../theming_features"
contexts:
- FeatureContext:
baseUrl: http://localhost:8080
admin:
- admin
- admin
regular_user_password: 123456

View File

@ -20,6 +20,7 @@ trait BasicStructure {
use Avatar;
use Download;
use Mail;
use Theming;
/** @var string */
private $currentUser = '';

View File

@ -0,0 +1,49 @@
<?php
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
require __DIR__ . '/../../vendor/autoload.php';
trait Theming {
private bool $undoAllThemingChangesAfterScenario = false;
/**
* @AfterScenario
*/
public function undoAllThemingChanges() {
if (!$this->undoAllThemingChangesAfterScenario) {
return;
}
$this->loggingInUsingWebAs('admin');
$this->sendingAToWithRequesttoken('POST', '/index.php/apps/theming/ajax/undoAllChanges');
$this->undoAllThemingChangesAfterScenario = false;
}
/**
* @When logged in admin uploads theming image for :key from file :source
*
* @param string $key
* @param string $source
*/
public function loggedInAdminUploadsThemingImageForFromFile(string $key, string $source) {
$this->undoAllThemingChangesAfterScenario = true;
$file = \GuzzleHttp\Psr7\Utils::streamFor(fopen($source, 'r'));
$this->sendingAToWithRequesttoken('POST', '/index.php/apps/theming/ajax/uploadImage?key=' . $key,
[
'multipart' => [
[
'name' => 'image',
'contents' => $file
]
]
]);
$this->theHTTPStatusCodeShouldBe('200');
}
}

View File

@ -0,0 +1,131 @@
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
Feature: theming
Background:
Given user "user0" exists
Scenario: themed stylesheets are available for users
Given As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
Then the HTTP status code should be "200"
Scenario: themed stylesheets are available for guests
Given As an "anonymous"
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
Then the HTTP status code should be "200"
# Themes that can not be explicitly set by a guest could have been
# globally set too through "enforce_theme".
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
Then the HTTP status code should be "200"
Scenario: themed stylesheets are available for disabled users
Given As an "admin"
And assure user "user0" is disabled
And As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
Then the HTTP status code should be "200"
Scenario: themed images are available for users
Given Logging in using web as "admin"
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
And As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
Then the HTTP status code should be "200"
Scenario: themed images are available for guests
Given Logging in using web as "admin"
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
And As an "anonymous"
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
Then the HTTP status code should be "200"
Scenario: themed images are available for disabled users
Given Logging in using web as "admin"
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
And As an "admin"
And assure user "user0" is disabled
And As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
Then the HTTP status code should be "200"
Scenario: themed icons are available for users
Given As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
Then the HTTP status code should be "200"
Scenario: themed icons are available for guests
Given As an "anonymous"
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
Then the HTTP status code should be "200"
Scenario: themed icons are available for disabled users
Given As an "admin"
And assure user "user0" is disabled
And As an "user0"
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
Then the HTTP status code should be "200"
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
Then the HTTP status code should be "200"

View File

@ -12,6 +12,7 @@ use OC\Share20\GroupDeletedListener;
use OC\Share20\Hooks;
use OC\Share20\UserDeletedListener;
use OC\Share20\UserRemovedListener;
use OC\User\DisabledUserException;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\BeforeFileSystemSetupEvent;
use OCP\Group\Events\GroupDeletedEvent;
@ -1026,7 +1027,27 @@ class OC {
// OAuth needs to support basic auth too, so the login is not valid
// inside Nextcloud and the Login exception would ruin it.
if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
self::handleLogin($request);
try {
self::handleLogin($request);
} catch (DisabledUserException $e) {
// Disabled users would not be seen as logged in and
// trying to log them in would fail, so the login
// exception is ignored for the themed stylesheets and
// images.
if ($request->getRawPathInfo() !== '/apps/theming/theme/default.css'
&& $request->getRawPathInfo() !== '/apps/theming/theme/light.css'
&& $request->getRawPathInfo() !== '/apps/theming/theme/dark.css'
&& $request->getRawPathInfo() !== '/apps/theming/theme/light-highcontrast.css'
&& $request->getRawPathInfo() !== '/apps/theming/theme/dark-highcontrast.css'
&& $request->getRawPathInfo() !== '/apps/theming/theme/opendyslexic.css'
&& $request->getRawPathInfo() !== '/apps/theming/image/background'
&& $request->getRawPathInfo() !== '/apps/theming/image/logo'
&& $request->getRawPathInfo() !== '/apps/theming/image/logoheader'
&& !str_starts_with($request->getRawPathInfo(), '/apps/theming/favicon')
&& !str_starts_with($request->getRawPathInfo(), '/apps/theming/icon')) {
throw $e;
}
}
}
}
}