Skip to content

Commit 83ca183

Browse files
danxuliubackportbot[bot]
authored andcommitted
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> [skip ci]
1 parent d707dd9 commit 83ca183

File tree

5 files changed

+204
-1
lines changed

5 files changed

+204
-1
lines changed

.github/workflows/integration-sqlite.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
- 'setup_features'
6969
- 'sharees_features'
7070
- 'sharing_features'
71+
- 'theming_features'
7172
- 'videoverification_features'
7273

7374
php-versions: ['8.1']

build/integration/features/bootstrap/BasicStructure.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ trait BasicStructure {
1919
use Avatar;
2020
use Download;
2121
use Mail;
22+
use Theming;
2223

2324
/** @var string */
2425
private $currentUser = '';
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
require __DIR__ . '/../../vendor/autoload.php';
8+
9+
trait Theming {
10+
11+
private bool $undoAllThemingChangesAfterScenario = false;
12+
13+
/**
14+
* @AfterScenario
15+
*/
16+
public function undoAllThemingChanges() {
17+
if (!$this->undoAllThemingChangesAfterScenario) {
18+
return;
19+
}
20+
21+
$this->loggingInUsingWebAs('admin');
22+
$this->sendingAToWithRequesttoken('POST', '/index.php/apps/theming/ajax/undoAllChanges');
23+
24+
$this->undoAllThemingChangesAfterScenario = false;
25+
}
26+
27+
/**
28+
* @When logged in admin uploads theming image for :key from file :source
29+
*
30+
* @param string $key
31+
* @param string $source
32+
*/
33+
public function loggedInAdminUploadsThemingImageForFromFile(string $key, string $source) {
34+
$this->undoAllThemingChangesAfterScenario = true;
35+
36+
$file = \GuzzleHttp\Psr7\Utils::streamFor(fopen($source, 'r'));
37+
38+
$this->sendingAToWithRequesttoken('POST', '/index.php/apps/theming/ajax/uploadImage?key=' . $key,
39+
[
40+
'multipart' => [
41+
[
42+
'name' => 'image',
43+
'contents' => $file
44+
]
45+
]
46+
]);
47+
$this->theHTTPStatusCodeShouldBe('200');
48+
}
49+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
Feature: theming
4+
5+
Background:
6+
Given user "user0" exists
7+
8+
Scenario: themed stylesheets are available for users
9+
Given As an "user0"
10+
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
11+
Then the HTTP status code should be "200"
12+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
13+
Then the HTTP status code should be "200"
14+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
15+
Then the HTTP status code should be "200"
16+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
17+
Then the HTTP status code should be "200"
18+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
19+
Then the HTTP status code should be "200"
20+
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
21+
Then the HTTP status code should be "200"
22+
23+
Scenario: themed stylesheets are available for guests
24+
Given As an "anonymous"
25+
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
26+
Then the HTTP status code should be "200"
27+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
28+
Then the HTTP status code should be "200"
29+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
30+
Then the HTTP status code should be "200"
31+
# Themes that can not be explicitly set by a guest could have been
32+
# globally set too through "enforce_theme".
33+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
34+
Then the HTTP status code should be "200"
35+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
36+
Then the HTTP status code should be "200"
37+
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
38+
Then the HTTP status code should be "200"
39+
40+
Scenario: themed stylesheets are available for disabled users
41+
Given As an "admin"
42+
And assure user "user0" is disabled
43+
And As an "user0"
44+
When sending "GET" with exact url to "/index.php/apps/theming/theme/default.css"
45+
Then the HTTP status code should be "200"
46+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light.css"
47+
Then the HTTP status code should be "200"
48+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark.css"
49+
Then the HTTP status code should be "200"
50+
When sending "GET" with exact url to "/index.php/apps/theming/theme/light-highcontrast.css"
51+
Then the HTTP status code should be "200"
52+
When sending "GET" with exact url to "/index.php/apps/theming/theme/dark-highcontrast.css"
53+
Then the HTTP status code should be "200"
54+
When sending "GET" with exact url to "/index.php/apps/theming/theme/opendyslexic.css"
55+
Then the HTTP status code should be "200"
56+
57+
Scenario: themed images are available for users
58+
Given Logging in using web as "admin"
59+
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
60+
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
61+
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
62+
And As an "user0"
63+
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
64+
Then the HTTP status code should be "200"
65+
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
66+
Then the HTTP status code should be "200"
67+
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
68+
Then the HTTP status code should be "200"
69+
70+
Scenario: themed images are available for guests
71+
Given Logging in using web as "admin"
72+
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
73+
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
74+
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
75+
And As an "anonymous"
76+
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
77+
Then the HTTP status code should be "200"
78+
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
79+
Then the HTTP status code should be "200"
80+
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
81+
Then the HTTP status code should be "200"
82+
83+
Scenario: themed images are available for disabled users
84+
Given Logging in using web as "admin"
85+
And logged in admin uploads theming image for "background" from file "data/clouds.jpg"
86+
And logged in admin uploads theming image for "logo" from file "data/coloured-pattern-non-square.png"
87+
And logged in admin uploads theming image for "logoheader" from file "data/coloured-pattern-non-square.png"
88+
And As an "admin"
89+
And assure user "user0" is disabled
90+
And As an "user0"
91+
When sending "GET" with exact url to "/index.php/apps/theming/image/background"
92+
Then the HTTP status code should be "200"
93+
When sending "GET" with exact url to "/index.php/apps/theming/image/logo"
94+
Then the HTTP status code should be "200"
95+
When sending "GET" with exact url to "/index.php/apps/theming/image/logoheader"
96+
Then the HTTP status code should be "200"
97+
98+
Scenario: themed icons are available for users
99+
Given As an "user0"
100+
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
101+
Then the HTTP status code should be "200"
102+
When sending "GET" with exact url to "/index.php/apps/theming/icon"
103+
Then the HTTP status code should be "200"
104+
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
105+
Then the HTTP status code should be "200"
106+
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
107+
Then the HTTP status code should be "200"
108+
109+
Scenario: themed icons are available for guests
110+
Given As an "anonymous"
111+
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
112+
Then the HTTP status code should be "200"
113+
When sending "GET" with exact url to "/index.php/apps/theming/icon"
114+
Then the HTTP status code should be "200"
115+
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
116+
Then the HTTP status code should be "200"
117+
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
118+
Then the HTTP status code should be "200"
119+
120+
Scenario: themed icons are available for disabled users
121+
Given As an "admin"
122+
And assure user "user0" is disabled
123+
And As an "user0"
124+
When sending "GET" with exact url to "/index.php/apps/theming/favicon"
125+
Then the HTTP status code should be "200"
126+
When sending "GET" with exact url to "/index.php/apps/theming/icon"
127+
Then the HTTP status code should be "200"
128+
When sending "GET" with exact url to "/index.php/apps/theming/favicon/dashboard"
129+
Then the HTTP status code should be "200"
130+
When sending "GET" with exact url to "/index.php/apps/theming/icon/dashboard"
131+
Then the HTTP status code should be "200"

lib/base.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use OC\Share20\Hooks;
1212
use OC\Share20\UserDeletedListener;
1313
use OC\Share20\UserRemovedListener;
14+
use OC\User\DisabledUserException;
1415
use OCP\EventDispatcher\IEventDispatcher;
1516
use OCP\Group\Events\GroupDeletedEvent;
1617
use OCP\Group\Events\UserRemovedEvent;
@@ -996,7 +997,27 @@ public static function handleRequest(): void {
996997
// OAuth needs to support basic auth too, so the login is not valid
997998
// inside Nextcloud and the Login exception would ruin it.
998999
if ($request->getRawPathInfo() !== '/apps/oauth2/api/v1/token') {
999-
self::handleLogin($request);
1000+
try {
1001+
self::handleLogin($request);
1002+
} catch (DisabledUserException $e) {
1003+
// Disabled users would not be seen as logged in and
1004+
// trying to log them in would fail, so the login
1005+
// exception is ignored for the themed stylesheets and
1006+
// images.
1007+
if ($request->getRawPathInfo() !== '/apps/theming/theme/default.css'
1008+
&& $request->getRawPathInfo() !== '/apps/theming/theme/light.css'
1009+
&& $request->getRawPathInfo() !== '/apps/theming/theme/dark.css'
1010+
&& $request->getRawPathInfo() !== '/apps/theming/theme/light-highcontrast.css'
1011+
&& $request->getRawPathInfo() !== '/apps/theming/theme/dark-highcontrast.css'
1012+
&& $request->getRawPathInfo() !== '/apps/theming/theme/opendyslexic.css'
1013+
&& $request->getRawPathInfo() !== '/apps/theming/image/background'
1014+
&& $request->getRawPathInfo() !== '/apps/theming/image/logo'
1015+
&& $request->getRawPathInfo() !== '/apps/theming/image/logoheader'
1016+
&& !str_starts_with($request->getRawPathInfo(), '/apps/theming/favicon')
1017+
&& !str_starts_with($request->getRawPathInfo(), '/apps/theming/icon')) {
1018+
throw $e;
1019+
}
1020+
}
10001021
}
10011022
}
10021023
}

0 commit comments

Comments
 (0)