@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 284 lines 9.6 kB view raw
1<?php 2 3final class PhabricatorAuthPasswordTestCase extends PhabricatorTestCase { 4 5 protected function getPhabricatorTestCaseConfiguration() { 6 return array( 7 self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, 8 ); 9 } 10 11 public function testCompare() { 12 $password1 = new PhutilOpaqueEnvelope('hunter2'); 13 $password2 = new PhutilOpaqueEnvelope('hunter3'); 14 15 $user = $this->generateNewTestUser(); 16 $type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST; 17 18 $pass = PhabricatorAuthPassword::initializeNewPassword($user, $type) 19 ->setPassword($password1, $user) 20 ->save(); 21 22 $this->assertTrue( 23 $pass->comparePassword($password1, $user), 24 pht('Good password should match.')); 25 26 $this->assertFalse( 27 $pass->comparePassword($password2, $user), 28 pht('Bad password should not match.')); 29 } 30 31 public function testPasswordEngine() { 32 $password1 = new PhutilOpaqueEnvelope('the quick'); 33 $password2 = new PhutilOpaqueEnvelope('brown fox'); 34 35 $user = $this->generateNewTestUser(); 36 $test_type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST; 37 $account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT; 38 $content_source = $this->newContentSource(); 39 40 $engine = id(new PhabricatorAuthPasswordEngine()) 41 ->setViewer($user) 42 ->setContentSource($content_source) 43 ->setPasswordType($test_type) 44 ->setObject($user); 45 46 $account_engine = id(new PhabricatorAuthPasswordEngine()) 47 ->setViewer($user) 48 ->setContentSource($content_source) 49 ->setPasswordType($account_type) 50 ->setObject($user); 51 52 // We haven't set any passwords yet, so both passwords should be 53 // invalid. 54 $this->assertFalse($engine->isValidPassword($password1)); 55 $this->assertFalse($engine->isValidPassword($password2)); 56 57 $pass = PhabricatorAuthPassword::initializeNewPassword($user, $test_type) 58 ->setPassword($password1, $user) 59 ->save(); 60 61 // The password should now be valid. 62 $this->assertTrue($engine->isValidPassword($password1)); 63 $this->assertFalse($engine->isValidPassword($password2)); 64 65 // But, since the password is a "test" password, it should not be a valid 66 // "account" password. 67 $this->assertFalse($account_engine->isValidPassword($password1)); 68 $this->assertFalse($account_engine->isValidPassword($password2)); 69 70 // Both passwords are unique for the "test" engine, since an active 71 // password of a given type doesn't collide with itself. 72 $this->assertTrue($engine->isUniquePassword($password1)); 73 $this->assertTrue($engine->isUniquePassword($password2)); 74 75 // The "test" password is no longer unique for the "account" engine. 76 $this->assertFalse($account_engine->isUniquePassword($password1)); 77 $this->assertTrue($account_engine->isUniquePassword($password2)); 78 79 $this->revokePassword($user, $pass); 80 81 // Now that we've revoked the password, it should no longer be valid. 82 $this->assertFalse($engine->isValidPassword($password1)); 83 $this->assertFalse($engine->isValidPassword($password2)); 84 85 // But it should be a revoked password. 86 $this->assertTrue($engine->isRevokedPassword($password1)); 87 $this->assertFalse($engine->isRevokedPassword($password2)); 88 89 // It should be revoked for both roles: revoking a "test" password also 90 // prevents you from choosing it as a new "account" password. 91 $this->assertTrue($account_engine->isRevokedPassword($password1)); 92 $this->assertFalse($account_engine->isValidPassword($password2)); 93 94 // The revoked password makes this password non-unique for all account 95 // types. 96 $this->assertFalse($engine->isUniquePassword($password1)); 97 $this->assertTrue($engine->isUniquePassword($password2)); 98 $this->assertFalse($account_engine->isUniquePassword($password1)); 99 $this->assertTrue($account_engine->isUniquePassword($password2)); 100 } 101 102 public function testPasswordBlocklisting() { 103 $user = $this->generateNewTestUser(); 104 105 $user 106 ->setUsername('iasimov') 107 ->setRealName('Isaac Asimov') 108 ->save(); 109 110 $test_type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST; 111 $content_source = $this->newContentSource(); 112 113 $engine = id(new PhabricatorAuthPasswordEngine()) 114 ->setViewer($user) 115 ->setContentSource($content_source) 116 ->setPasswordType($test_type) 117 ->setObject($user); 118 119 $env = PhabricatorEnv::beginScopedEnv(); 120 $env->overrideEnvConfig('account.minimum-password-length', 4); 121 122 $passwords = array( 123 'a23li432m9mdf' => true, 124 125 // Empty. 126 '' => false, 127 128 // Password length tests. 129 'xh3' => false, 130 'xh32' => true, 131 132 // In common password blocklist. 133 'password1' => false, 134 135 // Tests for the account identifier blocklist. 136 'isaac' => false, 137 'iasimov' => false, 138 'iasimov1' => false, 139 'asimov' => false, 140 'iSaAc' => false, 141 '32IASIMOV' => false, 142 'i-am-iasimov-this-is-my-long-strong-password' => false, 143 'iasimo' => false, 144 145 // These are okay: although they're visually similar, they aren't mutual 146 // substrings of any identifier. 147 'iasimo1' => true, 148 'isa1mov' => true, 149 ); 150 151 foreach ($passwords as $password => $expect) { 152 $this->assertBlocklistedPassword($engine, $password, $expect); 153 } 154 } 155 156 private function assertBlocklistedPassword( 157 PhabricatorAuthPasswordEngine $engine, 158 $raw_password, 159 $expect_valid) { 160 161 $envelope_1 = new PhutilOpaqueEnvelope($raw_password); 162 $envelope_2 = new PhutilOpaqueEnvelope($raw_password); 163 164 $caught = null; 165 try { 166 $engine->checkNewPassword($envelope_1, $envelope_2); 167 } catch (PhabricatorAuthPasswordException $exception) { 168 $caught = $exception; 169 } 170 171 $this->assertEqual( 172 $expect_valid, 173 !($caught instanceof PhabricatorAuthPasswordException), 174 pht('Validity of password "%s".', $raw_password)); 175 } 176 177 178 public function testPasswordUpgrade() { 179 $weak_hasher = new PhabricatorIteratedMD5PasswordHasher(); 180 181 // Make sure we have two different hashers, and that the second one is 182 // stronger than iterated MD5. The most common reason this would fail is 183 // if an install does not have bcrypt available. 184 $strong_hasher = PhabricatorPasswordHasher::getBestHasher(); 185 if ($strong_hasher->getStrength() <= $weak_hasher->getStrength()) { 186 $this->assertSkipped( 187 pht( 188 'Multiple password hashers of different strengths are not '. 189 'available, so hash upgrading can not be tested.')); 190 } 191 192 $envelope = new PhutilOpaqueEnvelope('lunar1997'); 193 194 $user = $this->generateNewTestUser(); 195 $type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST; 196 $content_source = $this->newContentSource(); 197 198 $engine = id(new PhabricatorAuthPasswordEngine()) 199 ->setViewer($user) 200 ->setContentSource($content_source) 201 ->setPasswordType($type) 202 ->setObject($user); 203 204 $password = PhabricatorAuthPassword::initializeNewPassword($user, $type) 205 ->setPasswordWithHasher($envelope, $user, $weak_hasher) 206 ->save(); 207 208 $weak_name = $weak_hasher->getHashName(); 209 $strong_name = $strong_hasher->getHashName(); 210 211 // Since we explicitly used the weak hasher, the password should have 212 // been hashed with it. 213 $actual_hasher = $password->getHasher(); 214 $this->assertEqual($weak_name, $actual_hasher->getHashName()); 215 216 $is_valid = $engine 217 ->setUpgradeHashers(false) 218 ->isValidPassword($envelope, $user); 219 $password->reload(); 220 221 // Since we disabled hasher upgrading, the password should not have been 222 // rehashed. 223 $this->assertTrue($is_valid); 224 $actual_hasher = $password->getHasher(); 225 $this->assertEqual($weak_name, $actual_hasher->getHashName()); 226 227 $is_valid = $engine 228 ->setUpgradeHashers(true) 229 ->isValidPassword($envelope, $user); 230 $password->reload(); 231 232 // Now that we enabled hasher upgrading, the password should have been 233 // automatically rehashed into the stronger format. 234 $this->assertTrue($is_valid); 235 $actual_hasher = $password->getHasher(); 236 $this->assertEqual($strong_name, $actual_hasher->getHashName()); 237 238 // We should also have an "upgrade" transaction in the transaction record 239 // now which records the two hasher names. 240 $xactions = id(new PhabricatorAuthPasswordTransactionQuery()) 241 ->setViewer($user) 242 ->withObjectPHIDs(array($password->getPHID())) 243 ->withTransactionTypes( 244 array( 245 PhabricatorAuthPasswordUpgradeTransaction::TRANSACTIONTYPE, 246 )) 247 ->execute(); 248 249 $this->assertEqual(1, count($xactions)); 250 $xaction = head($xactions); 251 252 $this->assertEqual($weak_name, $xaction->getOldValue()); 253 $this->assertEqual($strong_name, $xaction->getNewValue()); 254 255 $is_valid = $engine 256 ->isValidPassword($envelope, $user); 257 258 // Finally, the password should still be valid after all the dust has 259 // settled. 260 $this->assertTrue($is_valid); 261 } 262 263 private function revokePassword( 264 PhabricatorUser $actor, 265 PhabricatorAuthPassword $password) { 266 267 $content_source = $this->newContentSource(); 268 $revoke_type = PhabricatorAuthPasswordRevokeTransaction::TRANSACTIONTYPE; 269 270 $xactions = array(); 271 272 $xactions[] = $password->getApplicationTransactionTemplate() 273 ->setTransactionType($revoke_type) 274 ->setNewValue(true); 275 276 $editor = $password->getApplicationTransactionEditor() 277 ->setActor($actor) 278 ->setContinueOnNoEffect(true) 279 ->setContinueOnMissingFields(true) 280 ->setContentSource($content_source) 281 ->applyTransactions($password, $xactions); 282 } 283 284}