Path: blob/master/src/applications/auth/storage/PhabricatorAuthPassword.php
12256 views
<?php12final class PhabricatorAuthPassword3extends PhabricatorAuthDAO4implements5PhabricatorPolicyInterface,6PhabricatorDestructibleInterface,7PhabricatorApplicationTransactionInterface {89protected $objectPHID;10protected $passwordType;11protected $passwordHash;12protected $passwordSalt;13protected $isRevoked;14protected $legacyDigestFormat;1516private $object = self::ATTACHABLE;1718const PASSWORD_TYPE_ACCOUNT = 'account';19const PASSWORD_TYPE_VCS = 'vcs';20const PASSWORD_TYPE_TEST = 'test';2122public static function initializeNewPassword(23PhabricatorAuthPasswordHashInterface $object,24$type) {2526return id(new self())27->setObjectPHID($object->getPHID())28->attachObject($object)29->setPasswordType($type)30->setIsRevoked(0);31}3233protected function getConfiguration() {34return array(35self::CONFIG_AUX_PHID => true,36self::CONFIG_COLUMN_SCHEMA => array(37'passwordType' => 'text64',38'passwordHash' => 'text128',39'passwordSalt' => 'text64',40'isRevoked' => 'bool',41'legacyDigestFormat' => 'text32?',42),43self::CONFIG_KEY_SCHEMA => array(44'key_role' => array(45'columns' => array('objectPHID', 'passwordType'),46),47),48) + parent::getConfiguration();49}5051public function getPHIDType() {52return PhabricatorAuthPasswordPHIDType::TYPECONST;53}5455public function getObject() {56return $this->assertAttached($this->object);57}5859public function attachObject($object) {60$this->object = $object;61return $this;62}6364public function getHasher() {65$hash = $this->newPasswordEnvelope();66return PhabricatorPasswordHasher::getHasherForHash($hash);67}6869public function canUpgrade() {70// If this password uses a legacy digest format, we can upgrade it to the71// new digest format even if a better hasher isn't available.72if ($this->getLegacyDigestFormat() !== null) {73return true;74}7576$hash = $this->newPasswordEnvelope();77return PhabricatorPasswordHasher::canUpgradeHash($hash);78}7980public function upgradePasswordHasher(81PhutilOpaqueEnvelope $envelope,82PhabricatorAuthPasswordHashInterface $object) {8384// Before we make changes, double check that this is really the correct85// password. It could be really bad if we "upgraded" a password and changed86// the secret!8788if (!$this->comparePassword($envelope, $object)) {89throw new Exception(90pht(91'Attempting to upgrade password hasher, but the password for the '.92'upgrade is not the stored credential!'));93}9495return $this->setPassword($envelope, $object);96}9798public function setPassword(99PhutilOpaqueEnvelope $password,100PhabricatorAuthPasswordHashInterface $object) {101102$hasher = PhabricatorPasswordHasher::getBestHasher();103return $this->setPasswordWithHasher($password, $object, $hasher);104}105106public function setPasswordWithHasher(107PhutilOpaqueEnvelope $password,108PhabricatorAuthPasswordHashInterface $object,109PhabricatorPasswordHasher $hasher) {110111if (!strlen($password->openEnvelope())) {112throw new Exception(113pht('Attempting to set an empty password!'));114}115116// Generate (or regenerate) the salt first.117$new_salt = Filesystem::readRandomCharacters(64);118$this->setPasswordSalt($new_salt);119120// Clear any legacy digest format to force a modern digest.121$this->setLegacyDigestFormat(null);122123$digest = $this->digestPassword($password, $object);124$hash = $hasher->getPasswordHashForStorage($digest);125$raw_hash = $hash->openEnvelope();126127return $this->setPasswordHash($raw_hash);128}129130public function comparePassword(131PhutilOpaqueEnvelope $password,132PhabricatorAuthPasswordHashInterface $object) {133134$digest = $this->digestPassword($password, $object);135$hash = $this->newPasswordEnvelope();136137return PhabricatorPasswordHasher::comparePassword($digest, $hash);138}139140public function newPasswordEnvelope() {141return new PhutilOpaqueEnvelope($this->getPasswordHash());142}143144private function digestPassword(145PhutilOpaqueEnvelope $password,146PhabricatorAuthPasswordHashInterface $object) {147148$object_phid = $object->getPHID();149150if ($this->getObjectPHID() !== $object->getPHID()) {151throw new Exception(152pht(153'This password is associated with an object PHID ("%s") for '.154'a different object than the provided one ("%s").',155$this->getObjectPHID(),156$object->getPHID()));157}158159$digest = $object->newPasswordDigest($password, $this);160161if (!($digest instanceof PhutilOpaqueEnvelope)) {162throw new Exception(163pht(164'Failed to digest password: object ("%s") did not return an '.165'opaque envelope with a password digest.',166$object->getPHID()));167}168169return $digest;170}171172173174/* -( PhabricatorPolicyInterface )----------------------------------------- */175176177public function getCapabilities() {178return array(179PhabricatorPolicyCapability::CAN_VIEW,180PhabricatorPolicyCapability::CAN_EDIT,181);182}183184public function getPolicy($capability) {185return PhabricatorPolicies::getMostOpenPolicy();186}187188public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {189return false;190}191192193/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */194195196public function getExtendedPolicy($capability, PhabricatorUser $viewer) {197return array(198array($this->getObject(), $capability),199);200}201202203/* -( PhabricatorDestructibleInterface )----------------------------------- */204205206public function destroyObjectPermanently(207PhabricatorDestructionEngine $engine) {208$this->delete();209}210211212/* -( PhabricatorApplicationTransactionInterface )------------------------- */213214215public function getApplicationTransactionEditor() {216return new PhabricatorAuthPasswordEditor();217}218219public function getApplicationTransactionTemplate() {220return new PhabricatorAuthPasswordTransaction();221}222223}224225226