[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Editor class for creating and adjusting users. This class guarantees data 5 * integrity and writes logs when user information changes. 6 * 7 * @task config Configuration 8 * @task edit Creating and Editing Users 9 * @task role Editing Roles 10 * @task email Adding, Removing and Changing Email 11 * @task internal Internals 12 */ 13 final class PhabricatorUserEditor extends PhabricatorEditor { 14 15 private $logs = array(); 16 17 18 /* -( Creating and Editing Users )----------------------------------------- */ 19 20 21 /** 22 * @task edit 23 */ 24 public function createNewUser( 25 PhabricatorUser $user, 26 PhabricatorUserEmail $email) { 27 28 if ($user->getID()) { 29 throw new Exception('User has already been created!'); 30 } 31 32 if ($email->getID()) { 33 throw new Exception('Email has already been created!'); 34 } 35 36 if (!PhabricatorUser::validateUsername($user->getUsername())) { 37 $valid = PhabricatorUser::describeValidUsername(); 38 throw new Exception("Username is invalid! {$valid}"); 39 } 40 41 // Always set a new user's email address to primary. 42 $email->setIsPrimary(1); 43 44 // If the primary address is already verified, also set the verified flag 45 // on the user themselves. 46 if ($email->getIsVerified()) { 47 $user->setIsEmailVerified(1); 48 } 49 50 $this->willAddEmail($email); 51 52 $user->openTransaction(); 53 try { 54 $user->save(); 55 $email->setUserPHID($user->getPHID()); 56 $email->save(); 57 } catch (AphrontDuplicateKeyQueryException $ex) { 58 // We might have written the user but failed to write the email; if 59 // so, erase the IDs we attached. 60 $user->setID(null); 61 $user->setPHID(null); 62 63 $user->killTransaction(); 64 throw $ex; 65 } 66 67 $log = PhabricatorUserLog::initializeNewLog( 68 $this->requireActor(), 69 $user->getPHID(), 70 PhabricatorUserLog::ACTION_CREATE); 71 $log->setNewValue($email->getAddress()); 72 $log->save(); 73 74 $user->saveTransaction(); 75 76 return $this; 77 } 78 79 80 /** 81 * @task edit 82 */ 83 public function updateUser( 84 PhabricatorUser $user, 85 PhabricatorUserEmail $email = null) { 86 if (!$user->getID()) { 87 throw new Exception('User has not been created yet!'); 88 } 89 90 $user->openTransaction(); 91 $user->save(); 92 if ($email) { 93 $email->save(); 94 } 95 96 $log = PhabricatorUserLog::initializeNewLog( 97 $this->requireActor(), 98 $user->getPHID(), 99 PhabricatorUserLog::ACTION_EDIT); 100 $log->save(); 101 102 $user->saveTransaction(); 103 104 return $this; 105 } 106 107 108 /** 109 * @task edit 110 */ 111 public function changePassword( 112 PhabricatorUser $user, 113 PhutilOpaqueEnvelope $envelope) { 114 115 if (!$user->getID()) { 116 throw new Exception('User has not been created yet!'); 117 } 118 119 $user->openTransaction(); 120 $user->reload(); 121 122 $user->setPassword($envelope); 123 $user->save(); 124 125 $log = PhabricatorUserLog::initializeNewLog( 126 $this->requireActor(), 127 $user->getPHID(), 128 PhabricatorUserLog::ACTION_CHANGE_PASSWORD); 129 $log->save(); 130 131 $user->saveTransaction(); 132 } 133 134 135 /** 136 * @task edit 137 */ 138 public function changeUsername(PhabricatorUser $user, $username) { 139 $actor = $this->requireActor(); 140 141 if (!$user->getID()) { 142 throw new Exception('User has not been created yet!'); 143 } 144 145 if (!PhabricatorUser::validateUsername($username)) { 146 $valid = PhabricatorUser::describeValidUsername(); 147 throw new Exception("Username is invalid! {$valid}"); 148 } 149 150 $old_username = $user->getUsername(); 151 152 $user->openTransaction(); 153 $user->reload(); 154 $user->setUsername($username); 155 156 try { 157 $user->save(); 158 } catch (AphrontDuplicateKeyQueryException $ex) { 159 $user->setUsername($old_username); 160 $user->killTransaction(); 161 throw $ex; 162 } 163 164 $log = PhabricatorUserLog::initializeNewLog( 165 $actor, 166 $user->getPHID(), 167 PhabricatorUserLog::ACTION_CHANGE_USERNAME); 168 $log->setOldValue($old_username); 169 $log->setNewValue($username); 170 $log->save(); 171 172 $user->saveTransaction(); 173 174 $user->sendUsernameChangeEmail($actor, $old_username); 175 } 176 177 178 /* -( Editing Roles )------------------------------------------------------ */ 179 180 181 /** 182 * @task role 183 */ 184 public function makeAdminUser(PhabricatorUser $user, $admin) { 185 $actor = $this->requireActor(); 186 187 if (!$user->getID()) { 188 throw new Exception('User has not been created yet!'); 189 } 190 191 $user->openTransaction(); 192 $user->beginWriteLocking(); 193 194 $user->reload(); 195 if ($user->getIsAdmin() == $admin) { 196 $user->endWriteLocking(); 197 $user->killTransaction(); 198 return $this; 199 } 200 201 $log = PhabricatorUserLog::initializeNewLog( 202 $actor, 203 $user->getPHID(), 204 PhabricatorUserLog::ACTION_ADMIN); 205 $log->setOldValue($user->getIsAdmin()); 206 $log->setNewValue($admin); 207 208 $user->setIsAdmin((int)$admin); 209 $user->save(); 210 211 $log->save(); 212 213 $user->endWriteLocking(); 214 $user->saveTransaction(); 215 216 return $this; 217 } 218 219 /** 220 * @task role 221 */ 222 public function makeSystemAgentUser(PhabricatorUser $user, $system_agent) { 223 $actor = $this->requireActor(); 224 225 if (!$user->getID()) { 226 throw new Exception('User has not been created yet!'); 227 } 228 229 $user->openTransaction(); 230 $user->beginWriteLocking(); 231 232 $user->reload(); 233 if ($user->getIsSystemAgent() == $system_agent) { 234 $user->endWriteLocking(); 235 $user->killTransaction(); 236 return $this; 237 } 238 239 $log = PhabricatorUserLog::initializeNewLog( 240 $actor, 241 $user->getPHID(), 242 PhabricatorUserLog::ACTION_SYSTEM_AGENT); 243 $log->setOldValue($user->getIsSystemAgent()); 244 $log->setNewValue($system_agent); 245 246 $user->setIsSystemAgent((int)$system_agent); 247 $user->save(); 248 249 $log->save(); 250 251 $user->endWriteLocking(); 252 $user->saveTransaction(); 253 254 return $this; 255 } 256 257 258 /** 259 * @task role 260 */ 261 public function disableUser(PhabricatorUser $user, $disable) { 262 $actor = $this->requireActor(); 263 264 if (!$user->getID()) { 265 throw new Exception('User has not been created yet!'); 266 } 267 268 $user->openTransaction(); 269 $user->beginWriteLocking(); 270 271 $user->reload(); 272 if ($user->getIsDisabled() == $disable) { 273 $user->endWriteLocking(); 274 $user->killTransaction(); 275 return $this; 276 } 277 278 $log = PhabricatorUserLog::initializeNewLog( 279 $actor, 280 $user->getPHID(), 281 PhabricatorUserLog::ACTION_DISABLE); 282 $log->setOldValue($user->getIsDisabled()); 283 $log->setNewValue($disable); 284 285 $user->setIsDisabled((int)$disable); 286 $user->save(); 287 288 $log->save(); 289 290 $user->endWriteLocking(); 291 $user->saveTransaction(); 292 293 return $this; 294 } 295 296 297 /** 298 * @task role 299 */ 300 public function approveUser(PhabricatorUser $user, $approve) { 301 $actor = $this->requireActor(); 302 303 if (!$user->getID()) { 304 throw new Exception('User has not been created yet!'); 305 } 306 307 $user->openTransaction(); 308 $user->beginWriteLocking(); 309 310 $user->reload(); 311 if ($user->getIsApproved() == $approve) { 312 $user->endWriteLocking(); 313 $user->killTransaction(); 314 return $this; 315 } 316 317 $log = PhabricatorUserLog::initializeNewLog( 318 $actor, 319 $user->getPHID(), 320 PhabricatorUserLog::ACTION_APPROVE); 321 $log->setOldValue($user->getIsApproved()); 322 $log->setNewValue($approve); 323 324 $user->setIsApproved($approve); 325 $user->save(); 326 327 $log->save(); 328 329 $user->endWriteLocking(); 330 $user->saveTransaction(); 331 332 return $this; 333 } 334 335 336 /* -( Adding, Removing and Changing Email )-------------------------------- */ 337 338 339 /** 340 * @task email 341 */ 342 public function addEmail( 343 PhabricatorUser $user, 344 PhabricatorUserEmail $email) { 345 346 $actor = $this->requireActor(); 347 348 if (!$user->getID()) { 349 throw new Exception('User has not been created yet!'); 350 } 351 if ($email->getID()) { 352 throw new Exception('Email has already been created!'); 353 } 354 355 // Use changePrimaryEmail() to change primary email. 356 $email->setIsPrimary(0); 357 $email->setUserPHID($user->getPHID()); 358 359 $this->willAddEmail($email); 360 361 $user->openTransaction(); 362 $user->beginWriteLocking(); 363 364 $user->reload(); 365 366 try { 367 $email->save(); 368 } catch (AphrontDuplicateKeyQueryException $ex) { 369 $user->endWriteLocking(); 370 $user->killTransaction(); 371 372 throw $ex; 373 } 374 375 $log = PhabricatorUserLog::initializeNewLog( 376 $actor, 377 $user->getPHID(), 378 PhabricatorUserLog::ACTION_EMAIL_ADD); 379 $log->setNewValue($email->getAddress()); 380 $log->save(); 381 382 $user->endWriteLocking(); 383 $user->saveTransaction(); 384 385 return $this; 386 } 387 388 389 /** 390 * @task email 391 */ 392 public function removeEmail( 393 PhabricatorUser $user, 394 PhabricatorUserEmail $email) { 395 396 $actor = $this->requireActor(); 397 398 if (!$user->getID()) { 399 throw new Exception('User has not been created yet!'); 400 } 401 if (!$email->getID()) { 402 throw new Exception('Email has not been created yet!'); 403 } 404 405 $user->openTransaction(); 406 $user->beginWriteLocking(); 407 408 $user->reload(); 409 $email->reload(); 410 411 if ($email->getIsPrimary()) { 412 throw new Exception("Can't remove primary email!"); 413 } 414 if ($email->getUserPHID() != $user->getPHID()) { 415 throw new Exception('Email not owned by user!'); 416 } 417 418 $email->delete(); 419 420 $log = PhabricatorUserLog::initializeNewLog( 421 $actor, 422 $user->getPHID(), 423 PhabricatorUserLog::ACTION_EMAIL_REMOVE); 424 $log->setOldValue($email->getAddress()); 425 $log->save(); 426 427 $user->endWriteLocking(); 428 $user->saveTransaction(); 429 430 $this->revokePasswordResetLinks($user); 431 432 return $this; 433 } 434 435 436 /** 437 * @task email 438 */ 439 public function changePrimaryEmail( 440 PhabricatorUser $user, 441 PhabricatorUserEmail $email) { 442 $actor = $this->requireActor(); 443 444 if (!$user->getID()) { 445 throw new Exception('User has not been created yet!'); 446 } 447 if (!$email->getID()) { 448 throw new Exception('Email has not been created yet!'); 449 } 450 451 $user->openTransaction(); 452 $user->beginWriteLocking(); 453 454 $user->reload(); 455 $email->reload(); 456 457 if ($email->getUserPHID() != $user->getPHID()) { 458 throw new Exception('User does not own email!'); 459 } 460 461 if ($email->getIsPrimary()) { 462 throw new Exception('Email is already primary!'); 463 } 464 465 if (!$email->getIsVerified()) { 466 throw new Exception('Email is not verified!'); 467 } 468 469 $old_primary = $user->loadPrimaryEmail(); 470 if ($old_primary) { 471 $old_primary->setIsPrimary(0); 472 $old_primary->save(); 473 } 474 475 $email->setIsPrimary(1); 476 $email->save(); 477 478 $log = PhabricatorUserLog::initializeNewLog( 479 $actor, 480 $user->getPHID(), 481 PhabricatorUserLog::ACTION_EMAIL_PRIMARY); 482 $log->setOldValue($old_primary ? $old_primary->getAddress() : null); 483 $log->setNewValue($email->getAddress()); 484 485 $log->save(); 486 487 $user->endWriteLocking(); 488 $user->saveTransaction(); 489 490 if ($old_primary) { 491 $old_primary->sendOldPrimaryEmail($user, $email); 492 } 493 $email->sendNewPrimaryEmail($user); 494 495 496 $this->revokePasswordResetLinks($user); 497 498 return $this; 499 } 500 501 502 /** 503 * Verify a user's email address. 504 * 505 * This verifies an individual email address. If the address is the user's 506 * primary address and their account was not previously verified, their 507 * account is marked as email verified. 508 * 509 * @task email 510 */ 511 public function verifyEmail( 512 PhabricatorUser $user, 513 PhabricatorUserEmail $email) { 514 $actor = $this->requireActor(); 515 516 if (!$user->getID()) { 517 throw new Exception('User has not been created yet!'); 518 } 519 if (!$email->getID()) { 520 throw new Exception('Email has not been created yet!'); 521 } 522 523 $user->openTransaction(); 524 $user->beginWriteLocking(); 525 526 $user->reload(); 527 $email->reload(); 528 529 if ($email->getUserPHID() != $user->getPHID()) { 530 throw new Exception(pht('User does not own email!')); 531 } 532 533 if (!$email->getIsVerified()) { 534 $email->setIsVerified(1); 535 $email->save(); 536 537 $log = PhabricatorUserLog::initializeNewLog( 538 $actor, 539 $user->getPHID(), 540 PhabricatorUserLog::ACTION_EMAIL_VERIFY); 541 $log->setNewValue($email->getAddress()); 542 $log->save(); 543 } 544 545 if (!$user->getIsEmailVerified()) { 546 // If the user just verified their primary email address, mark their 547 // account as email verified. 548 $user_primary = $user->loadPrimaryEmail(); 549 if ($user_primary->getID() == $email->getID()) { 550 $user->setIsEmailVerified(1); 551 $user->save(); 552 } 553 } 554 555 $user->endWriteLocking(); 556 $user->saveTransaction(); 557 558 } 559 560 561 /* -( Internals )---------------------------------------------------------- */ 562 563 564 /** 565 * @task internal 566 */ 567 private function willAddEmail(PhabricatorUserEmail $email) { 568 569 // Hard check before write to prevent creation of disallowed email 570 // addresses. Normally, the application does checks and raises more 571 // user friendly errors for us, but we omit the courtesy checks on some 572 // pathways like administrative scripts for simplicity. 573 574 if (!PhabricatorUserEmail::isValidAddress($email->getAddress())) { 575 throw new Exception(PhabricatorUserEmail::describeValidAddresses()); 576 } 577 578 if (!PhabricatorUserEmail::isAllowedAddress($email->getAddress())) { 579 throw new Exception(PhabricatorUserEmail::describeAllowedAddresses()); 580 } 581 } 582 583 private function revokePasswordResetLinks(PhabricatorUser $user) { 584 // Revoke any outstanding password reset links. If an attacker compromises 585 // an account, changes the email address, and sends themselves a password 586 // reset link, it could otherwise remain live for a short period of time 587 // and allow them to compromise the account again later. 588 589 PhabricatorAuthTemporaryToken::revokeTokens( 590 $user, 591 array($user->getPHID()), 592 array( 593 PhabricatorAuthSessionEngine::ONETIME_TEMPORARY_TOKEN_TYPE, 594 PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE, 595 )); 596 } 597 598 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |