Skip to content

Commit 099ca58

Browse files
Merge pull request #63759 from nextcloud/backport/63752/stable35
[stable35] fix(core): accept deprecated IANA timezone aliases
2 parents a6e45a5 + 9ebaf4d commit 099ca58

5 files changed

Lines changed: 148 additions & 7 deletions

File tree

apps/provisioning_api/lib/Controller/UsersController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,8 @@ public function editUser(string $userId, string $key, string $value): DataRespon
13581358
$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
13591359
break;
13601360
case self::USER_FIELD_TIMEZONE:
1361-
if (!in_array($value, \DateTimeZone::listIdentifiers())) {
1361+
// Older browsers still report deprecated aliases like Europe/Kiev.
1362+
if (!in_array($value, \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC))) {
13621363
throw new OCSException($this->l10n->t('Invalid timezone'), 101);
13631364
}
13641365
$this->config->setUserValue($targetUser->getUID(), 'core', 'timezone', $value);

apps/provisioning_api/tests/Controller/UsersControllerTest.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,6 +2591,108 @@ public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
25912591
$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
25922592
}
25932593

2594+
/**
2595+
* Debian and Ubuntu ship the tz database's backward links in a separate
2596+
* tzdata-legacy package, so pick an alias this platform actually knows
2597+
* instead of hardcoding one.
2598+
*/
2599+
private static function findBackwardCompatibleTimezone(): ?string {
2600+
$aliases = array_diff(
2601+
\DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC),
2602+
\DateTimeZone::listIdentifiers(),
2603+
);
2604+
return $aliases === [] ? null : reset($aliases);
2605+
}
2606+
2607+
public static function dataEditUserSelfEditChangeTimezone(): array {
2608+
return [
2609+
'primary identifier' => ['Europe/Vienna'],
2610+
'backward compatible alias' => [self::findBackwardCompatibleTimezone()],
2611+
];
2612+
}
2613+
2614+
#[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeTimezone')]
2615+
public function testEditUserSelfEditChangeTimezone(?string $timezone): void {
2616+
if ($timezone === null) {
2617+
$this->markTestSkipped('No backward compatible timezone aliases in this platform\'s tz database');
2618+
}
2619+
2620+
$loggedInUser = $this->createMock(IUser::class);
2621+
$loggedInUser
2622+
->expects($this->any())
2623+
->method('getUID')
2624+
->willReturn('UserToEdit');
2625+
$targetUser = $this->createMock(IUser::class);
2626+
$this->config->expects($this->once())
2627+
->method('setUserValue')
2628+
->with('UserToEdit', 'core', 'timezone', $timezone);
2629+
$this->userSession
2630+
->expects($this->once())
2631+
->method('getUser')
2632+
->willReturn($loggedInUser);
2633+
$this->userManager
2634+
->expects($this->once())
2635+
->method('get')
2636+
->with('UserToEdit')
2637+
->willReturn($targetUser);
2638+
$this->groupManager
2639+
->expects($this->atLeastOnce())
2640+
->method('isAdmin')
2641+
->with('UserToEdit')
2642+
->willReturn(false);
2643+
$targetUser
2644+
->expects($this->any())
2645+
->method('getUID')
2646+
->willReturn('UserToEdit');
2647+
2648+
$backend = $this->createMock(UserInterface::class);
2649+
$targetUser
2650+
->expects($this->any())
2651+
->method('getBackend')
2652+
->willReturn($backend);
2653+
2654+
$this->assertEquals([], $this->api->editUser('UserToEdit', 'timezone', $timezone)->getData());
2655+
}
2656+
2657+
public function testEditUserSelfEditChangeTimezoneInvalid(): void {
2658+
$this->expectException(OCSException::class);
2659+
2660+
$loggedInUser = $this->createMock(IUser::class);
2661+
$loggedInUser
2662+
->expects($this->any())
2663+
->method('getUID')
2664+
->willReturn('UserToEdit');
2665+
$targetUser = $this->createMock(IUser::class);
2666+
$this->config->expects($this->never())
2667+
->method('setUserValue');
2668+
$this->userSession
2669+
->expects($this->once())
2670+
->method('getUser')
2671+
->willReturn($loggedInUser);
2672+
$this->userManager
2673+
->expects($this->once())
2674+
->method('get')
2675+
->with('UserToEdit')
2676+
->willReturn($targetUser);
2677+
$this->groupManager
2678+
->expects($this->atLeastOnce())
2679+
->method('isAdmin')
2680+
->with('UserToEdit')
2681+
->willReturn(false);
2682+
$targetUser
2683+
->expects($this->any())
2684+
->method('getUID')
2685+
->willReturn('UserToEdit');
2686+
2687+
$backend = $this->createMock(UserInterface::class);
2688+
$targetUser
2689+
->expects($this->any())
2690+
->method('getBackend')
2691+
->willReturn($backend);
2692+
2693+
$this->api->editUser('UserToEdit', 'timezone', 'Mars/Olympus_Mons');
2694+
}
2695+
25942696
public function testEditUserSubadminUserAccessible(): void {
25952697
$this->appConfig
25962698
->expects($this->once())

lib/private/Authentication/Login/SetUserTimezoneCommand.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function process(LoginData $loginData): LoginResult {
3838
}
3939

4040
private function isValidTimezone(?string $value): bool {
41-
return $value && in_array($value, \DateTimeZone::listIdentifiers());
41+
// Older browsers still report deprecated aliases like Europe/Kiev.
42+
return $value && in_array($value, \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC));
4243
}
4344
}

tests/lib/Authentication/Login/ALoginTestCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ protected function getLoggedInLoginDataWithRedirectUrl(): LoginData {
9191
return $data;
9292
}
9393

94-
protected function getLoggedInLoginDataWithTimezone(): LoginData {
94+
protected function getLoggedInLoginDataWithTimezone(?string $timezone = null): LoginData {
9595
$data = new LoginData(
9696
$this->request,
9797
$this->username,
9898
$this->password,
9999
true,
100100
null,
101-
$this->timezone,
101+
$timezone ?? $this->timezone,
102102
$this->timeZoneOffset
103103
);
104104
$data->setUser($this->user);

tests/lib/Authentication/Login/SetUserTimezoneCommandTest.php

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,33 @@ public function testProcessNoTimezoneSet(): void {
4545
$this->assertTrue($result->isSuccess());
4646
}
4747

48-
public function testProcess(): void {
49-
$data = $this->getLoggedInLoginDataWithTimezone();
48+
/**
49+
* Debian and Ubuntu ship the tz database's backward links in a separate
50+
* tzdata-legacy package, so pick an alias this platform actually knows
51+
* instead of hardcoding one.
52+
*/
53+
private static function findBackwardCompatibleTimezone(): ?string {
54+
$aliases = array_diff(
55+
\DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC),
56+
\DateTimeZone::listIdentifiers(),
57+
);
58+
return $aliases === [] ? null : reset($aliases);
59+
}
60+
61+
public static function dataAcceptedTimezone(): array {
62+
return [
63+
'primary identifier' => ['Europe/Vienna'],
64+
'backward compatible alias' => [self::findBackwardCompatibleTimezone()],
65+
];
66+
}
67+
68+
#[\PHPUnit\Framework\Attributes\DataProvider('dataAcceptedTimezone')]
69+
public function testProcess(?string $timezone): void {
70+
if ($timezone === null) {
71+
$this->markTestSkipped('No backward compatible timezone aliases in this platform\'s tz database');
72+
}
73+
74+
$data = $this->getLoggedInLoginDataWithTimezone($timezone);
5075
$this->user->expects($this->once())
5176
->method('getUID')
5277
->willReturn($this->username);
@@ -65,7 +90,7 @@ public function testProcess(): void {
6590
$this->username,
6691
'core',
6792
'timezone',
68-
$this->timezone
93+
$timezone
6994
);
7095
$this->session->expects($this->once())
7196
->method('set')
@@ -79,6 +104,18 @@ public function testProcess(): void {
79104
$this->assertTrue($result->isSuccess());
80105
}
81106

107+
public function testProcessUnknownTimezone(): void {
108+
$data = $this->getLoggedInLoginDataWithTimezone('Mars/Olympus_Mons');
109+
$this->config->expects($this->never())
110+
->method('setUserValue');
111+
$this->session->expects($this->never())
112+
->method('set');
113+
114+
$result = $this->cmd->process($data);
115+
116+
$this->assertTrue($result->isSuccess());
117+
}
118+
82119
public function testProcessAlreadySet(): void {
83120
$data = $this->getLoggedInLoginDataWithTimezone();
84121
$this->user->expects($this->once())

0 commit comments

Comments
 (0)