[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/people/editor/ -> PhabricatorUserEditor.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1