Skip to content

Commit 9f3ab6c

Browse files
authored
Merge pull request #63757 from nextcloud/backport/63752/stable33
[stable33] fix(core): accept deprecated IANA timezone aliases
2 parents 705e647 + c24a7d6 commit 9f3ab6c

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
@@ -1132,7 +1132,8 @@ public function editUser(string $userId, string $key, string $value): DataRespon
11321132
$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
11331133
break;
11341134
case self::USER_FIELD_TIMEZONE:
1135-
if (!in_array($value, \DateTimeZone::listIdentifiers())) {
1135+
// Older browsers still report deprecated aliases like Europe/Kiev.
1136+
if (!in_array($value, \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC))) {
11361137
throw new OCSException($this->l10n->t('Invalid timezone'), 101);
11371138
}
11381139
$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
@@ -2618,6 +2618,108 @@ public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
26182618
$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
26192619
}
26202620

2621+
/**
2622+
* Debian and Ubuntu ship the tz database's backward links in a separate
2623+
* tzdata-legacy package, so pick an alias this platform actually knows
2624+
* instead of hardcoding one.
2625+
*/
2626+
private static function findBackwardCompatibleTimezone(): ?string {
2627+
$aliases = array_diff(
2628+
\DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC),
2629+
\DateTimeZone::listIdentifiers(),
2630+
);
2631+
return $aliases === [] ? null : reset($aliases);
2632+
}
2633+
2634+
public static function dataEditUserSelfEditChangeTimezone(): array {
2635+
return [
2636+
'primary identifier' => ['Europe/Vienna'],
2637+
'backward compatible alias' => [self::findBackwardCompatibleTimezone()],
2638+
];
2639+
}
2640+
2641+
#[\PHPUnit\Framework\Attributes\DataProvider('dataEditUserSelfEditChangeTimezone')]
2642+
public function testEditUserSelfEditChangeTimezone(?string $timezone): void {
2643+
if ($timezone === null) {
2644+
$this->markTestSkipped('No backward compatible timezone aliases in this platform\'s tz database');
2645+
}
2646+
2647+
$loggedInUser = $this->createMock(IUser::class);
2648+
$loggedInUser
2649+
->expects($this->any())
2650+
->method('getUID')
2651+
->willReturn('UserToEdit');
2652+
$targetUser = $this->createMock(IUser::class);
2653+
$this->config->expects($this->once())
2654+
->method('setUserValue')
2655+
->with('UserToEdit', 'core', 'timezone', $timezone);
2656+
$this->userSession
2657+
->expects($this->once())
2658+
->method('getUser')
2659+
->willReturn($loggedInUser);
2660+
$this->userManager
2661+
->expects($this->once())
2662+
->method('get')
2663+
->with('UserToEdit')
2664+
->willReturn($targetUser);
2665+
$this->groupManager
2666+
->expects($this->atLeastOnce())
2667+
->method('isAdmin')
2668+
->with('UserToEdit')
2669+
->willReturn(false);
2670+
$targetUser
2671+
->expects($this->any())
2672+
->method('getUID')
2673+
->willReturn('UserToEdit');
2674+
2675+
$backend = $this->createMock(UserInterface::class);
2676+
$targetUser
2677+
->expects($this->any())
2678+
->method('getBackend')
2679+
->willReturn($backend);
2680+
2681+
$this->assertEquals([], $this->api->editUser('UserToEdit', 'timezone', $timezone)->getData());
2682+
}
2683+
2684+
public function testEditUserSelfEditChangeTimezoneInvalid(): void {
2685+
$this->expectException(OCSException::class);
2686+
2687+
$loggedInUser = $this->createMock(IUser::class);
2688+
$loggedInUser
2689+
->expects($this->any())
2690+
->method('getUID')
2691+
->willReturn('UserToEdit');
2692+
$targetUser = $this->createMock(IUser::class);
2693+
$this->config->expects($this->never())
2694+
->method('setUserValue');
2695+
$this->userSession
2696+
->expects($this->once())
2697+
->method('getUser')
2698+
->willReturn($loggedInUser);
2699+
$this->userManager
2700+
->expects($this->once())
2701+
->method('get')
2702+
->with('UserToEdit')
2703+
->willReturn($targetUser);
2704+
$this->groupManager
2705+
->expects($this->atLeastOnce())
2706+
->method('isAdmin')
2707+
->with('UserToEdit')
2708+
->willReturn(false);
2709+
$targetUser
2710+
->expects($this->any())
2711+
->method('getUID')
2712+
->willReturn('UserToEdit');
2713+
2714+
$backend = $this->createMock(UserInterface::class);
2715+
$targetUser
2716+
->expects($this->any())
2717+
->method('getBackend')
2718+
->willReturn($backend);
2719+
2720+
$this->api->editUser('UserToEdit', 'timezone', 'Mars/Olympus_Mons');
2721+
}
2722+
26212723
public function testEditUserSubadminUserAccessible(): void {
26222724
$this->config
26232725
->expects($this->once())

‎lib/private/Authentication/Login/SetUserTimezoneCommand.php‎

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

4444
private function isValidTimezone(?string $value): bool {
45-
return $value && in_array($value, \DateTimeZone::listIdentifiers());
45+
// Older browsers still report deprecated aliases like Europe/Kiev.
46+
return $value && in_array($value, \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC));
4647
}
4748
}

‎tests/lib/Authentication/Login/ALoginTestCommand.php‎

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

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

‎tests/lib/Authentication/Login/SetUserTimezoneCommandTest.php‎

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

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

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

0 commit comments

Comments
 (0)