1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace Tests\Controllers;
7
8use App\Models\Country;
9use App\Models\LoginAttempt;
10use App\Models\User;
11use Tests\TestCase;
12
13class SessionsControllerTest extends TestCase
14{
15 public function testLogin()
16 {
17 $password = 'password1';
18 $user = User::factory()->create(compact('password'));
19
20 $this->expectCountChange(fn () => LoginAttempt::count(), 1);
21 $this->post(route('login'), [
22 'username' => $user->username,
23 'password' => $password,
24 ])->assertSuccessful();
25
26 $this->assertAuthenticated();
27 }
28
29 public function testLoginInactiveUser()
30 {
31 $password = 'password1';
32 $countryAcronym = (Country::first() ?? Country::factory()->create())->getKey();
33 $user = User::factory()->create(['password' => $password, 'country_acronym' => $countryAcronym]);
34 $user->update(['user_lastvisit' => time() - $GLOBALS['cfg']['osu']['user']['inactive_seconds_verification'] - 1]);
35
36 $this->post(route('login'), [
37 'username' => $user->username,
38 'password' => $password,
39 ], [
40 'CF_IPCOUNTRY' => $countryAcronym,
41 ])->assertSuccessful();
42
43 $this->assertAuthenticated();
44
45 $this->get(route('home'))->assertStatus(401);
46 }
47
48 public function testLoginInactiveUserForceReset(): void
49 {
50 config_set('osu.user.inactive_force_password_reset', true);
51
52 $password = 'password1';
53 $countryAcronym = (Country::first() ?? Country::factory()->create())->getKey();
54 $user = User::factory()->create(['password' => $password, 'country_acronym' => $countryAcronym]);
55 $user->update(['user_lastvisit' => time() - $GLOBALS['cfg']['osu']['user']['inactive_seconds_verification'] - 1]);
56
57 $this->post(route('login'), [
58 'username' => $user->username,
59 'password' => $password,
60 ], [
61 'CF_IPCOUNTRY' => $countryAcronym,
62 ])->assertStatus(302);
63
64 $this->assertGuest();
65 }
66
67 public function testLoginInactiveUserDifferentCountry()
68 {
69 $password = 'password1';
70 $user = User::factory()->create(compact('password'));
71 $user->update(['user_lastvisit' => time() - $GLOBALS['cfg']['osu']['user']['inactive_seconds_verification'] - 1]);
72
73 $this->assertNotSame('', $user->fresh()->user_password);
74
75 $this->post(route('login'), [
76 'username' => $user->username,
77 'password' => $password,
78 ], [
79 'CF_IPCOUNTRY' => '__',
80 ])->assertStatus(302);
81
82 $this->assertGuest();
83 $this->assertSame('', $user->fresh()->user_password);
84 }
85
86 public function testLoginMissingParameters()
87 {
88 $password = 'password1';
89 $user = User::factory()->create(compact('password'));
90
91 $this->post(route('login'))->assertStatus(422);
92 $this->assertGuest();
93
94 $this->post(route('login'), ['username' => $user->username])->assertStatus(422);
95 $this->assertGuest();
96
97 $this->post(route('login'), compact('password'))->assertStatus(422);
98 $this->assertGuest();
99 }
100
101 public function testLoginWrongPassword()
102 {
103 $password = 'password1';
104 $user = User::factory()->create(compact('password'));
105
106 $this->post(route('login'), [
107 'username' => $user->username,
108 'password' => "{$password}1",
109 ])->assertStatus(403);
110
111 $this->assertGuest();
112
113 $record = LoginAttempt::find('127.0.0.1');
114 $this->assertTrue($record->containsUser($user, 'fail:'));
115 $this->assertSame(1, $record->unique_ids);
116 $this->assertSame(1, $record->failed_attempts);
117 $this->assertSame(1, $record->total_attempts);
118 }
119
120 public function testLoginWrongPasswordTwiceDifferent()
121 {
122 $password = 'password1';
123 $user = User::factory()->create(compact('password'));
124
125 $this->post(route('login'), [
126 'username' => $user->username,
127 'password' => 'password2',
128 ])->assertStatus(403);
129
130 $this->post(route('login'), [
131 'username' => $user->username,
132 'password' => 'password3',
133 ])->assertStatus(403);
134
135 $this->assertGuest();
136
137 $record = LoginAttempt::find('127.0.0.1');
138 $this->assertTrue($record->containsUser($user, 'fail:'));
139 $this->assertSame(1, $record->unique_ids);
140 $this->assertSame(2, $record->failed_attempts);
141 $this->assertSame(2, $record->total_attempts);
142 }
143
144 public function testLoginWrongPasswordTwiceSame()
145 {
146 $password = 'password1';
147 $wrongPassword = 'password2';
148 $user = User::factory()->create(compact('password'));
149
150 $this->post(route('login'), [
151 'username' => $user->username,
152 'password' => $wrongPassword,
153 ])->assertStatus(403);
154
155 $this->post(route('login'), [
156 'username' => $user->username,
157 'password' => $wrongPassword,
158 ])->assertStatus(403);
159
160 $this->assertGuest();
161
162 $record = LoginAttempt::find('127.0.0.1');
163 $this->assertTrue($record->containsUser($user, 'fail:'));
164 $this->assertSame(1, $record->unique_ids);
165 $this->assertSame(1, $record->failed_attempts);
166 $this->assertSame(1, $record->total_attempts);
167 }
168
169 public function testLoginWrongPasswordExtraFailedOnAnotherUser()
170 {
171 $password = 'password1';
172 $ip = '127.0.0.1';
173 $firstUser = User::factory()->create(compact('password'));
174 LoginAttempt::logAttempt($ip, $firstUser, 'fail', 'password2');
175
176 $record = LoginAttempt::find('127.0.0.1');
177 $this->assertTrue($record->containsUser($firstUser, 'fail:'));
178 $this->assertSame(1, $record->unique_ids);
179 $this->assertSame(1, $record->failed_attempts);
180 $this->assertSame(1, $record->total_attempts);
181
182 $secondUser = User::factory()->create(compact('password'));
183
184 $this->post(route('login'), [
185 'username' => $secondUser->username,
186 'password' => "{$password}1",
187 ])->assertStatus(403);
188
189 $this->assertGuest();
190
191 $record = LoginAttempt::find('127.0.0.1');
192 $this->assertTrue($record->containsUser($secondUser, 'fail:'));
193 $this->assertSame(2, $record->unique_ids);
194 $this->assertSame(3, $record->failed_attempts);
195 $this->assertSame(2, $record->total_attempts);
196 }
197}