3 * Testing for password-policy enforcement, based on a user's groups.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
25 * @covers UserPasswordPolicy
27 class UserPasswordPolicyTest
extends MediaWikiTestCase
{
29 protected $tablesUsed = [ 'user', 'user_groups' ];
31 protected $policies = [
33 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
34 'MinimumPasswordLengthToLogin' => 6,
35 'PasswordCannotMatchUsername' => true,
38 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true ],
39 'MinimumPasswordLengthToLogin' => 1,
40 'PasswordCannotMatchUsername' => true,
43 'MinimalPasswordLength' => [
45 'suggestChangeOnLogin' => false,
46 'forceChange' => true,
48 'PasswordCannotMatchUsername' => true,
51 'MinimalPasswordLength' => 4,
52 'MinimumPasswordLengthToLogin' => 1,
53 'PasswordCannotMatchBlacklist' => true,
54 'MaximalPasswordLength' => 4096,
56 'PasswordCannotMatchUsername' => null,
61 'MinimalPasswordLength' => 'PasswordPolicyChecks::checkMinimalPasswordLength',
62 'MinimumPasswordLengthToLogin' => 'PasswordPolicyChecks::checkMinimumPasswordLengthToLogin',
63 'PasswordCannotMatchUsername' => 'PasswordPolicyChecks::checkPasswordCannotMatchUsername',
64 'PasswordCannotMatchBlacklist' => 'PasswordPolicyChecks::checkPasswordCannotMatchBlacklist',
65 'MaximalPasswordLength' => 'PasswordPolicyChecks::checkMaximalPasswordLength',
68 private function getUserPasswordPolicy() {
69 return new UserPasswordPolicy( $this->policies
, $this->checks
);
72 public function testGetPoliciesForUser() {
73 $upp = $this->getUserPasswordPolicy();
75 $user = $this->getTestUser( [ 'sysop' ] )->getUser();
76 $this->assertArrayEquals(
78 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true ],
79 'MinimumPasswordLengthToLogin' => 1,
80 'PasswordCannotMatchUsername' => true,
81 'PasswordCannotMatchBlacklist' => true,
82 'MaximalPasswordLength' => 4096,
84 $upp->getPoliciesForUser( $user )
87 $user = $this->getTestUser( [ 'sysop', 'checkuser' ] )->getUser();
88 $this->assertArrayEquals(
90 'MinimalPasswordLength' => [
92 'forceChange' => true,
93 'suggestChangeOnLogin' => true
95 'MinimumPasswordLengthToLogin' => 6,
96 'PasswordCannotMatchUsername' => true,
97 'PasswordCannotMatchBlacklist' => true,
98 'MaximalPasswordLength' => 4096,
100 $upp->getPoliciesForUser( $user )
104 public function testGetPoliciesForGroups() {
105 $effective = UserPasswordPolicy
::getPoliciesForGroups(
107 [ 'user', 'checkuser', 'sysop' ],
108 $this->policies
['default']
111 $this->assertArrayEquals(
113 'MinimalPasswordLength' => [
115 'forceChange' => true,
116 'suggestChangeOnLogin' => true
118 'MinimumPasswordLengthToLogin' => 6,
119 'PasswordCannotMatchUsername' => true,
120 'PasswordCannotMatchBlacklist' => true,
121 'MaximalPasswordLength' => 4096,
128 * @dataProvider provideCheckUserPassword
130 public function testCheckUserPassword( $groups, $password, StatusValue
$expectedStatus ) {
131 $upp = $this->getUserPasswordPolicy();
132 $user = $this->getTestUser( $groups )->getUser();
134 $status = $upp->checkUserPassword( $user, $password );
135 $this->assertSame( $expectedStatus->isGood(), $status->isGood(), 'password valid' );
136 $this->assertSame( $expectedStatus->isOK(), $status->isOK(), 'can login' );
137 $this->assertSame( $expectedStatus->getValue(), $status->getValue(), 'flags' );
140 public function provideCheckUserPassword() {
141 $success = Status
::newGood( [] );
142 $warning = Status
::newGood( [] );
143 $forceChange = Status
::newGood( [ 'forceChange' => true ] );
144 $suggestChangeOnLogin = Status
::newGood( [ 'suggestChangeOnLogin' => true ] );
145 $fatal = Status
::newGood( [] );
147 // the message does not matter, we only test for state and value
148 $warning->warning( 'invalid-password' );
149 $forceChange->warning( 'invalid-password' );
150 $suggestChangeOnLogin->warning( 'invalid-password' );
151 $warning->warning( 'invalid-password' );
152 $fatal->fatal( 'invalid-password' );
155 'No groups, default policy, password too short to login' => [
160 'Default policy, short password' => [
165 'Sysop with good password' => [
170 'Sysop with short password and suggestChangeOnLogin set to true' => [
173 $suggestChangeOnLogin,
175 'Checkuser with short password' => [
180 'Bureaucrat bad password with forceChange true, suggestChangeOnLogin false' => [
185 'Checkuser with too short password to login' => [
186 [ 'sysop', 'checkuser' ],
193 public function testCheckUserPassword_blacklist() {
194 $upp = $this->getUserPasswordPolicy();
195 $user = User
::newFromName( 'Useruser' );
196 $user->addToDatabase();
198 $status = $upp->checkUserPassword( $user, 'Passpass' );
199 $this->assertFalse( $status->isGood(), 'password invalid' );
200 $this->assertTrue( $status->isOK(), 'can login' );
204 * @dataProvider provideMaxOfPolicies
206 public function testMaxOfPolicies( $p1, $p2, $max ) {
207 $this->assertArrayEquals(
209 UserPasswordPolicy
::maxOfPolicies( $p1, $p2 )
213 public function provideMaxOfPolicies() {
215 'Basic max in p1' => [
216 [ 'MinimalPasswordLength' => 8 ], // p1
217 [ 'MinimalPasswordLength' => 2 ], // p2
218 [ 'MinimalPasswordLength' => 8 ], // max
220 'Basic max in p2' => [
221 [ 'MinimalPasswordLength' => 2 ], // p1
222 [ 'MinimalPasswordLength' => 8 ], // p2
223 [ 'MinimalPasswordLength' => 8 ], // max
225 'Missing items in p1' => [
227 'MinimalPasswordLength' => 8,
230 'MinimalPasswordLength' => 2,
231 'PasswordCannotMatchUsername' => 1,
234 'MinimalPasswordLength' => 8,
235 'PasswordCannotMatchUsername' => 1,
238 'Missing items in p2' => [
240 'MinimalPasswordLength' => 8,
241 'PasswordCannotMatchUsername' => 1,
244 'MinimalPasswordLength' => 2,
247 'MinimalPasswordLength' => 8,
248 'PasswordCannotMatchUsername' => 1,
251 'complex value in p1' => [
253 'MinimalPasswordLength' => [
259 'MinimalPasswordLength' => 2,
262 'MinimalPasswordLength' => [
268 'complex value in p2' => [
270 'MinimalPasswordLength' => 8,
273 'MinimalPasswordLength' => [
279 'MinimalPasswordLength' => [
285 'complex value in both p1 and p2' => [
287 'MinimalPasswordLength' => [
294 'MinimalPasswordLength' => [
301 'MinimalPasswordLength' => [
309 'complex value in both p1 and p2 #2' => [
311 'MinimalPasswordLength' => [
318 'MinimalPasswordLength' => [
324 'MinimalPasswordLength' => [