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' => 8,
39 'MinimumPasswordLengthToLogin' => 1,
40 'PasswordCannotMatchUsername' => true,
43 'MinimalPasswordLength' => 4,
44 'MinimumPasswordLengthToLogin' => 1,
45 'PasswordCannotMatchBlacklist' => true,
46 'MaximalPasswordLength' => 4096,
48 'PasswordCannotMatchUsername' => null,
53 'MinimalPasswordLength' => 'PasswordPolicyChecks::checkMinimalPasswordLength',
54 'MinimumPasswordLengthToLogin' => 'PasswordPolicyChecks::checkMinimumPasswordLengthToLogin',
55 'PasswordCannotMatchUsername' => 'PasswordPolicyChecks::checkPasswordCannotMatchUsername',
56 'PasswordCannotMatchBlacklist' => 'PasswordPolicyChecks::checkPasswordCannotMatchBlacklist',
57 'MaximalPasswordLength' => 'PasswordPolicyChecks::checkMaximalPasswordLength',
60 private function getUserPasswordPolicy() {
61 return new UserPasswordPolicy( $this->policies
, $this->checks
);
64 public function testGetPoliciesForUser() {
65 $upp = $this->getUserPasswordPolicy();
67 $user = $this->getTestUser( [ 'sysop' ] )->getUser();
68 $this->assertArrayEquals(
70 'MinimalPasswordLength' => 8,
71 'MinimumPasswordLengthToLogin' => 1,
72 'PasswordCannotMatchUsername' => true,
73 'PasswordCannotMatchBlacklist' => true,
74 'MaximalPasswordLength' => 4096,
76 $upp->getPoliciesForUser( $user )
79 $user = $this->getTestUser( [ 'sysop', 'checkuser' ] )->getUser();
80 $this->assertArrayEquals(
82 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
83 'MinimumPasswordLengthToLogin' => 6,
84 'PasswordCannotMatchUsername' => true,
85 'PasswordCannotMatchBlacklist' => true,
86 'MaximalPasswordLength' => 4096,
88 $upp->getPoliciesForUser( $user )
92 public function testGetPoliciesForGroups() {
93 $effective = UserPasswordPolicy
::getPoliciesForGroups(
95 [ 'user', 'checkuser' ],
96 $this->policies
['default']
99 $this->assertArrayEquals(
101 'MinimalPasswordLength' => [ 'value' => 10, 'forceChange' => true ],
102 'MinimumPasswordLengthToLogin' => 6,
103 'PasswordCannotMatchUsername' => true,
104 'PasswordCannotMatchBlacklist' => true,
105 'MaximalPasswordLength' => 4096,
112 * @dataProvider provideCheckUserPassword
114 public function testCheckUserPassword( $groups, $password, StatusValue
$expectedStatus ) {
115 $upp = $this->getUserPasswordPolicy();
116 $user = $this->getTestUser( $groups )->getUser();
118 $status = $upp->checkUserPassword( $user, $password );
119 $this->assertSame( $expectedStatus->isGood(), $status->isGood(), 'password valid' );
120 $this->assertSame( $expectedStatus->isOK(), $status->isOK(), 'can login' );
121 $this->assertSame( $expectedStatus->getValue(), $status->getValue(), 'flags' );
124 public function provideCheckUserPassword() {
125 $success = Status
::newGood( [] );
126 $warning = Status
::newGood( [] );
127 $forceChange = Status
::newGood( [ 'forceChange' => true ] );
128 $fatal = Status
::newGood( [] );
129 // the message does not matter, we only test for state and value
130 $warning->warning( 'invalid-password' );
131 $forceChange->warning( 'invalid-password' );
132 $warning->warning( 'invalid-password' );
133 $fatal->fatal( 'invalid-password' );
135 'No groups, default policy, password too short to login' => [
140 'Default policy, short password' => [
145 'Sysop with good password' => [
150 'Sysop with short password' => [
155 'Checkuser with short password' => [
156 [ 'sysop', 'checkuser' ],
160 'Checkuser with too short password to login' => [
161 [ 'sysop', 'checkuser' ],
168 public function testCheckUserPassword_blacklist() {
169 $upp = $this->getUserPasswordPolicy();
170 $user = User
::newFromName( 'Useruser' );
171 $user->addToDatabase();
173 $status = $upp->checkUserPassword( $user, 'Passpass' );
174 $this->assertFalse( $status->isGood(), 'password invalid' );
175 $this->assertTrue( $status->isOK(), 'can login' );
179 * @dataProvider provideMaxOfPolicies
181 public function testMaxOfPolicies( $p1, $p2, $max ) {
182 $this->assertArrayEquals(
184 UserPasswordPolicy
::maxOfPolicies( $p1, $p2 )
188 public function provideMaxOfPolicies() {
190 'Basic max in p1' => [
191 [ 'MinimalPasswordLength' => 8 ], // p1
192 [ 'MinimalPasswordLength' => 2 ], // p2
193 [ 'MinimalPasswordLength' => 8 ], // max
195 'Basic max in p2' => [
196 [ 'MinimalPasswordLength' => 2 ], // p1
197 [ 'MinimalPasswordLength' => 8 ], // p2
198 [ 'MinimalPasswordLength' => 8 ], // max
200 'Missing items in p1' => [
202 'MinimalPasswordLength' => 8,
205 'MinimalPasswordLength' => 2,
206 'PasswordCannotMatchUsername' => 1,
209 'MinimalPasswordLength' => 8,
210 'PasswordCannotMatchUsername' => 1,
213 'Missing items in p2' => [
215 'MinimalPasswordLength' => 8,
216 'PasswordCannotMatchUsername' => 1,
219 'MinimalPasswordLength' => 2,
222 'MinimalPasswordLength' => 8,
223 'PasswordCannotMatchUsername' => 1,
226 'complex value in p1' => [
228 'MinimalPasswordLength' => [
234 'MinimalPasswordLength' => 2,
237 'MinimalPasswordLength' => [
243 'complex value in p2' => [
245 'MinimalPasswordLength' => 8,
248 'MinimalPasswordLength' => [
254 'MinimalPasswordLength' => [
260 'complex value in both p1 and p2' => [
262 'MinimalPasswordLength' => [
269 'MinimalPasswordLength' => [
276 'MinimalPasswordLength' => [