Merge pull request #53501 from nextcloud/fix-theming-for-disabled-accounts

fix: Fix theming for disabled accounts
This commit is contained in:
Daniel Calviño Sánchez 2025-07-10 20:47:48 +02:00 committed by GitHub
commit d4e9a8ac33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 229 additions and 4 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;
}
}
}
}
}

View file

@ -2124,6 +2124,7 @@ return array(
'OC\\User\\Backend' => $baseDir . '/lib/private/User/Backend.php',
'OC\\User\\BackgroundJobs\\CleanupDeletedUsers' => $baseDir . '/lib/private/User/BackgroundJobs/CleanupDeletedUsers.php',
'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php',
'OC\\User\\DisabledUserException' => $baseDir . '/lib/private/User/DisabledUserException.php',
'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php',
'OC\\User\\LazyUser' => $baseDir . '/lib/private/User/LazyUser.php',
'OC\\User\\Listeners\\BeforeUserDeletedListener' => $baseDir . '/lib/private/User/Listeners/BeforeUserDeletedListener.php',

View file

@ -2165,6 +2165,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\User\\Backend' => __DIR__ . '/../../..' . '/lib/private/User/Backend.php',
'OC\\User\\BackgroundJobs\\CleanupDeletedUsers' => __DIR__ . '/../../..' . '/lib/private/User/BackgroundJobs/CleanupDeletedUsers.php',
'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php',
'OC\\User\\DisabledUserException' => __DIR__ . '/../../..' . '/lib/private/User/DisabledUserException.php',
'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php',
'OC\\User\\LazyUser' => __DIR__ . '/../../..' . '/lib/private/User/LazyUser.php',
'OC\\User\\Listeners\\BeforeUserDeletedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/BeforeUserDeletedListener.php',

View file

@ -0,0 +1,10 @@
<?php
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\User;
class DisabledUserException extends LoginException {
}

View file

@ -320,7 +320,7 @@ class Session implements IUserSession, Emitter {
// disabled users can not log in
// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
$message = \OCP\Util::getL10N('lib')->t('Account disabled');
throw new LoginException($message);
throw new DisabledUserException($message);
}
if ($regenerateSessionId) {

View file

@ -6,7 +6,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
use OC\Authentication\Token\IProvider;
use OC\User\LoginException;
use OC\User\DisabledUserException;
use OCP\Authentication\Exceptions\InvalidTokenException;
use OCP\Authentication\Exceptions\WipeTokenException;
use OCP\Authentication\Token\IToken;
@ -151,7 +151,7 @@ class OC_User {
if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) {
$message = \OC::$server->getL10N('lib')->t('Account disabled');
throw new LoginException($message);
throw new DisabledUserException($message);
}
$userSession->setLoginName($uid);
$request = OC::$server->getRequest();