Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
50.56% |
90 / 178 |
|
63.16% |
12 / 19 |
CRAP | |
0.00% |
0 / 1 |
| phpbb_passwords_manager_test | |
50.56% |
90 / 178 |
|
63.16% |
12 / 19 |
107.68 | |
0.00% |
0 / 1 |
| setUp | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
1 | |||
| hash_password_data | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
| test_hash_password | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
| check_password_data | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
| test_check_password | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
| check_hash_exceptions_data | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
2 | |||
| test_check_hash_exceptions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| data_hash_password_length | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
| test_hash_password_length | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| test_hash_password_8bit_bcrypt | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| combined_hash_data | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
2 | |||
| test_combined_hash_password | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
| test_unique_id | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
| test_check_hash_with_large_input | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| test_hash_password_with_large_input | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| data_test_string_compare | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
| test_string_compare | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| data_driver_interface_driver | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
| test_driver_interface_driver | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * |
| 4 | * This file is part of the phpBB Forum Software package. |
| 5 | * |
| 6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
| 7 | * @license GNU General Public License, version 2 (GPL-2.0) |
| 8 | * |
| 9 | * For full copyright and license information, please see |
| 10 | * the docs/CREDITS.txt file. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | class phpbb_passwords_manager_test extends \phpbb_test_case |
| 15 | { |
| 16 | /** @var \phpbb\passwords\driver\helper */ |
| 17 | protected $driver_helper; |
| 18 | |
| 19 | /** @var \phpbb\passwords\helper */ |
| 20 | protected $helper; |
| 21 | |
| 22 | /** @var \phpbb\passwords\manager */ |
| 23 | protected $manager; |
| 24 | |
| 25 | protected $passwords_drivers; |
| 26 | |
| 27 | protected $pw_characters = '0123456789abcdefghijklmnopqrstuvwyzABCDEFGHIJKLMNOPQRSTUVXYZ.,_!?/\\'; |
| 28 | |
| 29 | protected $default_pw = 'foobar'; |
| 30 | |
| 31 | protected function setUp(): void |
| 32 | { |
| 33 | // Prepare dependencies for manager and driver |
| 34 | $config = new \phpbb\config\config(array()); |
| 35 | $this->driver_helper = new \phpbb\passwords\driver\helper($config); |
| 36 | $request = new phpbb_mock_request(array(), array(), array(), array(), array('password' => 'töst')); |
| 37 | $phpbb_root_path = __DIR__ . '/../../phpBB/'; |
| 38 | $php_ext = 'php'; |
| 39 | |
| 40 | $this->passwords_drivers = array( |
| 41 | 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $this->driver_helper, 10), |
| 42 | 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $this->driver_helper, 10), |
| 43 | 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $this->driver_helper), |
| 44 | 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $this->driver_helper), |
| 45 | 'passwords.driver.convert_password' => new \phpbb\passwords\driver\convert_password($config, $this->driver_helper), |
| 46 | 'passwords.driver.sha1_smf' => new \phpbb\passwords\driver\sha1_smf($config, $this->driver_helper), |
| 47 | 'passwords.driver.sha1' => new \phpbb\passwords\driver\sha1($config, $this->driver_helper), |
| 48 | 'passwords.driver.sha1_wcf1' => new \phpbb\passwords\driver\sha1_wcf1($config, $this->driver_helper), |
| 49 | 'passwords.driver.md5_mybb' => new \phpbb\passwords\driver\md5_mybb($config, $this->driver_helper), |
| 50 | 'passwords.driver.md5_vb' => new \phpbb\passwords\driver\md5_vb($config, $this->driver_helper), |
| 51 | 'passwords.driver.sha_xf1' => new \phpbb\passwords\driver\sha_xf1($config, $this->driver_helper), |
| 52 | ); |
| 53 | $this->passwords_drivers['passwords.driver.md5_phpbb2'] = new \phpbb\passwords\driver\md5_phpbb2($request, $this->passwords_drivers['passwords.driver.salted_md5'], $this->driver_helper, $phpbb_root_path, $php_ext); |
| 54 | $this->passwords_drivers['passwords.driver.bcrypt_wcf2'] = new \phpbb\passwords\driver\bcrypt_wcf2($this->passwords_drivers['passwords.driver.bcrypt'], $this->driver_helper); |
| 55 | |
| 56 | $this->helper = new \phpbb\passwords\helper; |
| 57 | // Set up passwords manager |
| 58 | $this->manager = new \phpbb\passwords\manager($config, $this->passwords_drivers, $this->helper, array_keys($this->passwords_drivers)); |
| 59 | } |
| 60 | |
| 61 | public static function hash_password_data() |
| 62 | { |
| 63 | return array( |
| 64 | array('', '2y', 60), |
| 65 | array('passwords.driver.bcrypt_2y', '2y', 60), |
| 66 | array('passwords.driver.bcrypt', '2a', 60), |
| 67 | array('passwords.driver.salted_md5', 'H', 34), |
| 68 | array('passwords.driver.foobar', '', false), |
| 69 | ); |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @dataProvider hash_password_data |
| 74 | */ |
| 75 | public function test_hash_password($type, $prefix, $length) |
| 76 | { |
| 77 | $password = $this->default_pw; |
| 78 | |
| 79 | if (!$length) |
| 80 | { |
| 81 | $this->assertEquals(false, $hash = $this->manager->hash($password, $type)); |
| 82 | return; |
| 83 | } |
| 84 | $time = microtime(true); |
| 85 | |
| 86 | // Limit each test to 1 second |
| 87 | while ((microtime(true) - $time) < 1) |
| 88 | { |
| 89 | $hash = $this->manager->hash($password, $type); |
| 90 | preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match); |
| 91 | $this->assertEquals($prefix, $match[1]); |
| 92 | $this->assertEquals($length, strlen($hash)); |
| 93 | $password .= $this->pw_characters[mt_rand(0, 66)]; |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | public static function check_password_data() |
| 98 | { |
| 99 | return array( |
| 100 | array('passwords.driver.bcrypt_2y'), |
| 101 | array('passwords.driver.bcrypt'), |
| 102 | array('passwords.driver.salted_md5'), |
| 103 | array('passwords.driver.phpass'), |
| 104 | ); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * @dataProvider check_password_data |
| 109 | */ |
| 110 | public function test_check_password($hash_type) |
| 111 | { |
| 112 | $password = $this->default_pw; |
| 113 | $time = microtime(true); |
| 114 | // Limit each test to 1 second |
| 115 | while ((microtime(true) - $time) < 1) |
| 116 | { |
| 117 | $hash = $this->manager->hash($password, $hash_type); |
| 118 | $this->assertEquals(true, $this->manager->check($password, $hash)); |
| 119 | $password .= $this->pw_characters[mt_rand(0, 66)]; |
| 120 | $this->assertEquals(false, $this->manager->check($password, $hash)); |
| 121 | } |
| 122 | |
| 123 | // Check if convert_flag is correctly set |
| 124 | $default_type = 'passwords.driver.bcrypt_2y'; |
| 125 | $this->assertEquals(($hash_type !== $default_type), $this->manager->convert_flag); |
| 126 | } |
| 127 | |
| 128 | |
| 129 | public static function check_hash_exceptions_data() |
| 130 | { |
| 131 | return array( |
| 132 | array('3858f62230ac3c915f300c664312c63f', true), |
| 133 | array('$CP$3858f62230ac3c915f300c664312c63f', true), // md5_phpbb2 |
| 134 | array('$CP$3858f62230ac3c915f300c', false), |
| 135 | array('$S$b57a939fa4f2c04413a4eea9734a0903647b7adb93181295', false), |
| 136 | array('$2a\S$kkkkaakdkdiej39023903204j2k3490234jk234j02349', false), |
| 137 | array('$H$kklk938d023k//k3023', false), |
| 138 | array('$H$3PtYMgXb39lrIWkgoxYLWtRkZtY3AY/', false), |
| 139 | array('$2a$kwiweorurlaeirw', false), |
| 140 | array('6f9e2a1899e1f15708fd2e554103480eb53e8b57', false), |
| 141 | array('6f9e2a1899e1f15708fd2e554103480eb53e8b57', false, 'foobar', array('login_name' => 'test')), |
| 142 | array('$CP$6f9e2a1899e1f15708fd2e554103480eb53e8b57', true, 'foobar', array('login_name' => 'test')), // sha1_smf |
| 143 | array('6f9e2a1899', false, 'foobar', array('login_name' => 'test')), |
| 144 | array('ae2fc75e20ee25d4520766788fbc96ae', false, 'fööbar'), |
| 145 | array('$CP$ae2fc75e20ee25d4520766788fbc96ae', false, 'fööbar'), |
| 146 | array('$CP$ae2fc75e20ee25d4520766788fbc96ae', true, utf8_decode('fööbar')), // md5_phpbb2 |
| 147 | array('b86ee7e24008bfd2890dcfab1ed31333', false, 'foobar', array('user_passwd_salt' => 'yeOtfFO6')), |
| 148 | array('$CP$b86ee7e24008bfd2890dcfab1ed31333', true, 'foobar', array('user_passwd_salt' => 'yeOtfFO6')), // md5_mybb |
| 149 | array('$CP$b452c54c44c588fc095d2d000935c470', true, 'foobar', array('user_passwd_salt' => '9^F')), // md5_vb |
| 150 | array('$CP$f23a8241bd115d270c703213e3ef7f52', true, 'foobar', array('user_passwd_salt' => 'iaU*U%`CBl;/e~>D%do2m@Xf/,KZB0')), // md5_vb |
| 151 | array('$CP$fc46b9d9386167ce365ea3b891bf5dc31ddcd3ff', true, 'foobar', array('user_passwd_salt' => '1a783e478d63f6422783a868db667aed3a857840')), // sha_wcf1 |
| 152 | array('$2a$08$p8h14U0jsEiVb1Luy.s8oOTXSQ0hVWUXpcNGBoCezeYNXrQyCKHfi', false), |
| 153 | array('$CP$$2a$08$p8h14U0jsEiVb1Luy.s8oOTXSQ0hVWUXpcNGBoCezeYNXrQyCKHfi', true), // bcrypt_wcf2 |
| 154 | array('$CP$7f65d2fa8a826d232f8134772252f8b1aaef8594b1edcabd9ab65e5b0f236ff0', true, 'foobar', array('user_passwd_salt' => '15b6c02cedbd727f563dcca607a89b085287b448966f19c0cc78cae263b1e38c')), // sha_xf1 |
| 155 | array('$CP$69962ae2079420573a3948cc4dedbabd35680051', true, 'foobar', array('user_passwd_salt' => '15b6c02cedbd727f563dcca607a89b085287b448966f19c0cc78cae263b1e38c')), // sha_xf1 |
| 156 | ); |
| 157 | } |
| 158 | |
| 159 | /** |
| 160 | * @dataProvider check_hash_exceptions_data |
| 161 | */ |
| 162 | public function test_check_hash_exceptions($hash, $expected, $password = 'foobar', $user_row = array()) |
| 163 | { |
| 164 | $this->assertEquals($expected, $this->manager->check($password, $hash, $user_row)); |
| 165 | } |
| 166 | |
| 167 | public static function data_hash_password_length() |
| 168 | { |
| 169 | return array( |
| 170 | array('passwords.driver.bcrypt', false), |
| 171 | array('passwords.driver.bcrypt_2y', false), |
| 172 | array('passwords.driver.salted_md5', '3858f62230ac3c915f300c664312c63f'), |
| 173 | array('passwords.driver.phpass', '3858f62230ac3c915f300c664312c63f'), |
| 174 | ); |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * @dataProvider data_hash_password_length |
| 179 | */ |
| 180 | public function test_hash_password_length($driver, $expected) |
| 181 | { |
| 182 | $this->assertEquals($expected, $this->passwords_drivers[$driver]->hash('foobar', 'foobar')); |
| 183 | } |
| 184 | |
| 185 | public function test_hash_password_8bit_bcrypt() |
| 186 | { |
| 187 | $this->assertEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt')); |
| 188 | $this->assertNotEquals(false, $this->manager->hash('foobar𝄞', 'passwords.driver.bcrypt_2y')); |
| 189 | } |
| 190 | |
| 191 | public static function combined_hash_data() |
| 192 | { |
| 193 | return array( |
| 194 | array( |
| 195 | 'passwords.driver.salted_md5', |
| 196 | array('passwords.driver.bcrypt_2y'), |
| 197 | ), |
| 198 | array( |
| 199 | 'passwords.driver.salted_md5', |
| 200 | array('passwords.driver.bcrypt'), |
| 201 | ), |
| 202 | array( |
| 203 | 'passwords.driver.phpass', |
| 204 | array('passwords.driver.salted_md5'), |
| 205 | ), |
| 206 | array( |
| 207 | 'passwords.driver.salted_md5', |
| 208 | array('passwords.driver.bcrypt_2y', 'passwords.driver.bcrypt'), |
| 209 | ), |
| 210 | array( |
| 211 | 'passwords.driver.salted_md5', |
| 212 | array('passwords.driver.salted_md5'), |
| 213 | false, |
| 214 | ), |
| 215 | array( |
| 216 | 'passwords.driver.bcrypt_2y', |
| 217 | array('passwords.driver.salted_md4'), |
| 218 | false, |
| 219 | ), |
| 220 | array( |
| 221 | '$H$', |
| 222 | array('$2y$'), |
| 223 | ), |
| 224 | ); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * @dataProvider combined_hash_data |
| 229 | */ |
| 230 | public function test_combined_hash_password($first_type, $second_type, $expected = true) |
| 231 | { |
| 232 | $password = $this->default_pw; |
| 233 | $time = microtime(true); |
| 234 | // Limit each test to 1 second |
| 235 | while ((microtime(true) - $time) < 1) |
| 236 | { |
| 237 | $hash = $this->manager->hash($password, $first_type); |
| 238 | $combined_hash = $this->manager->hash($hash, $second_type); |
| 239 | $this->assertEquals($expected, $this->manager->check($password, $combined_hash)); |
| 240 | $password .= $this->pw_characters[mt_rand(0, 66)]; |
| 241 | $this->assertEquals(false, $this->manager->check($password, $combined_hash)); |
| 242 | |
| 243 | // If we are expecting the check to fail then there is |
| 244 | // no need to run this more than once |
| 245 | if (!$expected) |
| 246 | { |
| 247 | break; |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | public function test_unique_id() |
| 253 | { |
| 254 | $time = microtime(true); |
| 255 | $first_id = $this->driver_helper->unique_id(); |
| 256 | // Limit test to 1 second |
| 257 | while ((microtime(true) - $time) < 1) |
| 258 | { |
| 259 | $this->assertNotSame($first_id, $this->driver_helper->unique_id()); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | public function test_check_hash_with_large_input() |
| 264 | { |
| 265 | // 16 MB password, should be rejected quite fast |
| 266 | $start_time = time(); |
| 267 | $this->assertFalse($this->manager->check(str_repeat('a', 1024 * 1024 * 16), '$H$9isfrtKXWqrz8PvztXlL3.daw4U0zI1')); |
| 268 | $this->assertLessThanOrEqual(5, time() - $start_time); |
| 269 | } |
| 270 | |
| 271 | public function test_hash_password_with_large_input() |
| 272 | { |
| 273 | // 16 MB password, should be rejected quite fast |
| 274 | $start_time = time(); |
| 275 | $this->assertFalse($this->manager->hash(str_repeat('a', 1024 * 1024 * 16))); |
| 276 | $this->assertLessThanOrEqual(5, time() - $start_time); |
| 277 | } |
| 278 | |
| 279 | public static function data_test_string_compare() |
| 280 | { |
| 281 | return array( |
| 282 | array('foo', 'bar', false), |
| 283 | array(1, '1', false), |
| 284 | array('one', 'one', true), |
| 285 | array('foobar', 'foobaf', false), |
| 286 | ); |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * @dataProvider data_test_string_compare |
| 291 | */ |
| 292 | public function test_string_compare($a, $b, $expected) |
| 293 | { |
| 294 | $this->assertSame($expected, $this->driver_helper->string_compare($a, $b)); |
| 295 | } |
| 296 | |
| 297 | public static function data_driver_interface_driver() |
| 298 | { |
| 299 | return array( |
| 300 | array(false, false, false), |
| 301 | array(true, false, false), |
| 302 | array(true, true, true), |
| 303 | ); |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * @dataProvider data_driver_interface_driver |
| 308 | */ |
| 309 | public function test_driver_interface_driver($use_new_interface, $needs_rehash, $expected) |
| 310 | { |
| 311 | if ($use_new_interface) |
| 312 | { |
| 313 | $test_driver = $this->createMock('\phpbb\passwords\driver\rehashable_driver_interface'); |
| 314 | $test_driver->method('needs_rehash') |
| 315 | ->willReturn($needs_rehash); |
| 316 | } |
| 317 | else |
| 318 | { |
| 319 | $test_driver = $this->createMock('\phpbb\passwords\driver\driver_interface'); |
| 320 | } |
| 321 | $config = new \phpbb\config\config(array()); |
| 322 | |
| 323 | $test_driver->method('is_supported') |
| 324 | ->willReturn(true); |
| 325 | $test_driver->method('get_prefix') |
| 326 | ->willReturn('$test$'); |
| 327 | $test_driver->method('check') |
| 328 | ->with($this->anything()) |
| 329 | ->willReturn(true); |
| 330 | $passwords_drivers = array( |
| 331 | 'passwords.driver.foobar' => $test_driver, |
| 332 | 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $this->driver_helper, 10), |
| 333 | ); |
| 334 | // Set up another manager |
| 335 | $foobar_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $this->helper, array('passwords.driver.foobar')); |
| 336 | |
| 337 | $this->assertTrue($foobar_manager->check('foobar', '$test$somerandomstuff')); |
| 338 | $this->assertEquals($expected, $foobar_manager->convert_flag); |
| 339 | |
| 340 | // Should always return true in case a different driver is default |
| 341 | $foobar_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $this->helper, array('passwords.driver.bcrypt_2y', 'passwords.driver.foobar')); |
| 342 | |
| 343 | $this->assertTrue($foobar_manager->check('foobar', '$test$somerandomstuff')); |
| 344 | $this->assertTrue($foobar_manager->convert_flag); |
| 345 | } |
| 346 | } |