@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.

Add a more modern object for storing password hashes

Summary:
Ref T13043. Currently:

- Passwords are stored separately in the "VCS Passwords" and "User" tables and don't share as much code as they could.
- Because User objects are all over the place in the code, password hashes are all over the place too (i.e., often somewhere in process memory). This is a very low-severity, theoretical sort of issue, but it could make leaving a stray `var_dump()` in the code somewhere a lot more dangerous than it otherwise is. Even if we never do this, third-party developers might. So it "feels nice" to imagine separating this data into a different table that we rarely load.
- Passwords can not be //revoked//. They can be //deleted//, but users can set the same password again. If you believe or suspect that a password may have been compromised, you might reasonably prefer to revoke it and force the user to select a //different// password.

This change prepares to remedy these issues by adding a new, more modern dedicated password storage table which supports storing multiple password types (account vs VCS), gives passwords real PHIDs and transactions, supports DestructionEngine, supports revocation, and supports `bin/auth revoke`.

It doesn't actually make anything use this new table yet. Future changes will migrate VCS passwords and account passwords to this table.

(This also gives third party applications a reasonable place to store password hashes in a consistent way if they have some need for it.)

Test Plan: Added some basic unit tests to cover general behavior. This is just skeleton code for now and will get more thorough testing when applications move.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13043

Differential Revision: https://secure.phabricator.com/D18894

+563 -1
+10
resources/sql/autopatches/20180120.auth.01.password.sql
··· 1 + CREATE TABLE {$NAMESPACE}_auth.auth_password ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + objectPHID VARBINARY(64) NOT NULL, 5 + passwordType VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, 6 + passwordHash VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT}, 7 + isRevoked BOOL NOT NULL, 8 + dateCreated INT UNSIGNED NOT NULL, 9 + dateModified INT UNSIGNED NOT NULL 10 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+19
resources/sql/autopatches/20180120.auth.02.passwordxaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_auth.auth_passwordtransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+25
src/__phutil_library_map__.php
··· 2089 2089 'PhabricatorAuthOldOAuthRedirectController' => 'applications/auth/controller/PhabricatorAuthOldOAuthRedirectController.php', 2090 2090 'PhabricatorAuthOneTimeLoginController' => 'applications/auth/controller/PhabricatorAuthOneTimeLoginController.php', 2091 2091 'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthOneTimeLoginTemporaryTokenType.php', 2092 + 'PhabricatorAuthPassword' => 'applications/auth/storage/PhabricatorAuthPassword.php', 2093 + 'PhabricatorAuthPasswordEditor' => 'applications/auth/editor/PhabricatorAuthPasswordEditor.php', 2094 + 'PhabricatorAuthPasswordPHIDType' => 'applications/auth/phid/PhabricatorAuthPasswordPHIDType.php', 2095 + 'PhabricatorAuthPasswordQuery' => 'applications/auth/query/PhabricatorAuthPasswordQuery.php', 2092 2096 'PhabricatorAuthPasswordResetTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthPasswordResetTemporaryTokenType.php', 2097 + 'PhabricatorAuthPasswordRevokeTransaction' => 'applications/auth/xaction/PhabricatorAuthPasswordRevokeTransaction.php', 2098 + 'PhabricatorAuthPasswordRevoker' => 'applications/auth/revoker/PhabricatorAuthPasswordRevoker.php', 2099 + 'PhabricatorAuthPasswordTestCase' => 'applications/auth/__tests__/PhabricatorAuthPasswordTestCase.php', 2100 + 'PhabricatorAuthPasswordTransaction' => 'applications/auth/storage/PhabricatorAuthPasswordTransaction.php', 2101 + 'PhabricatorAuthPasswordTransactionType' => 'applications/auth/xaction/PhabricatorAuthPasswordTransactionType.php', 2093 2102 'PhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorAuthProvider.php', 2094 2103 'PhabricatorAuthProviderConfig' => 'applications/auth/storage/PhabricatorAuthProviderConfig.php', 2095 2104 'PhabricatorAuthProviderConfigController' => 'applications/auth/controller/config/PhabricatorAuthProviderConfigController.php', ··· 3481 3490 'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php', 3482 3491 'PhabricatorPassphraseApplication' => 'applications/passphrase/application/PhabricatorPassphraseApplication.php', 3483 3492 'PhabricatorPasswordAuthProvider' => 'applications/auth/provider/PhabricatorPasswordAuthProvider.php', 3493 + 'PhabricatorPasswordDestructionEngineExtension' => 'applications/auth/extension/PhabricatorPasswordDestructionEngineExtension.php', 3484 3494 'PhabricatorPasswordHasher' => 'infrastructure/util/password/PhabricatorPasswordHasher.php', 3485 3495 'PhabricatorPasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorPasswordHasherTestCase.php', 3486 3496 'PhabricatorPasswordHasherUnavailableException' => 'infrastructure/util/password/PhabricatorPasswordHasherUnavailableException.php', ··· 7366 7376 'PhabricatorAuthOldOAuthRedirectController' => 'PhabricatorAuthController', 7367 7377 'PhabricatorAuthOneTimeLoginController' => 'PhabricatorAuthController', 7368 7378 'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType', 7379 + 'PhabricatorAuthPassword' => array( 7380 + 'PhabricatorAuthDAO', 7381 + 'PhabricatorPolicyInterface', 7382 + 'PhabricatorDestructibleInterface', 7383 + 'PhabricatorApplicationTransactionInterface', 7384 + ), 7385 + 'PhabricatorAuthPasswordEditor' => 'PhabricatorApplicationTransactionEditor', 7386 + 'PhabricatorAuthPasswordPHIDType' => 'PhabricatorPHIDType', 7387 + 'PhabricatorAuthPasswordQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7369 7388 'PhabricatorAuthPasswordResetTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType', 7389 + 'PhabricatorAuthPasswordRevokeTransaction' => 'PhabricatorAuthPasswordTransactionType', 7390 + 'PhabricatorAuthPasswordRevoker' => 'PhabricatorAuthRevoker', 7391 + 'PhabricatorAuthPasswordTestCase' => 'PhabricatorTestCase', 7392 + 'PhabricatorAuthPasswordTransaction' => 'PhabricatorApplicationTransaction', 7393 + 'PhabricatorAuthPasswordTransactionType' => 'PhabricatorModularTransactionType', 7370 7394 'PhabricatorAuthProvider' => 'Phobject', 7371 7395 'PhabricatorAuthProviderConfig' => array( 7372 7396 'PhabricatorAuthDAO', ··· 8984 9008 'PhabricatorPagerUIExample' => 'PhabricatorUIExample', 8985 9009 'PhabricatorPassphraseApplication' => 'PhabricatorApplication', 8986 9010 'PhabricatorPasswordAuthProvider' => 'PhabricatorAuthProvider', 9011 + 'PhabricatorPasswordDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', 8987 9012 'PhabricatorPasswordHasher' => 'Phobject', 8988 9013 'PhabricatorPasswordHasherTestCase' => 'PhabricatorTestCase', 8989 9014 'PhabricatorPasswordHasherUnavailableException' => 'Exception',
+31
src/applications/auth/__tests__/PhabricatorAuthPasswordTestCase.php
··· 1 + <?php 2 + 3 + final 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 + }
+22
src/applications/auth/editor/PhabricatorAuthPasswordEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorAuthApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Passwords'); 12 + } 13 + 14 + public function getCreateObjectTitle($author, $object) { 15 + return pht('%s created this password.', $author); 16 + } 17 + 18 + public function getCreateObjectTitleForFeed($author, $object) { 19 + return pht('%s created %s.', $author, $object); 20 + } 21 + 22 + }
+29
src/applications/auth/extension/PhabricatorPasswordDestructionEngineExtension.php
··· 1 + <?php 2 + 3 + final class PhabricatorPasswordDestructionEngineExtension 4 + extends PhabricatorDestructionEngineExtension { 5 + 6 + const EXTENSIONKEY = 'passwords'; 7 + 8 + public function getExtensionName() { 9 + return pht('Passwords'); 10 + } 11 + 12 + public function destroyObject( 13 + PhabricatorDestructionEngine $engine, 14 + $object) { 15 + 16 + $viewer = $engine->getViewer(); 17 + $object_phid = $object->getPHID(); 18 + 19 + $passwords = id(new PhabricatorAuthPasswordQuery()) 20 + ->setViewer($viewer) 21 + ->withObjectPHIDs(array($object_phid)) 22 + ->execute(); 23 + 24 + foreach ($passwords as $password) { 25 + $engine->destroyObject($password); 26 + } 27 + } 28 + 29 + }
+36
src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordPHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'APAS'; 6 + 7 + public function getTypeName() { 8 + return pht('Auth Password'); 9 + } 10 + 11 + public function newObject() { 12 + return new PhabricatorAuthPassword(); 13 + } 14 + 15 + public function getPHIDTypeApplicationClass() { 16 + return 'PhabricatorAuthApplication'; 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + return id(new PhabricatorAuthPasswordQuery()) 23 + ->withPHIDs($phids); 24 + } 25 + 26 + public function loadHandles( 27 + PhabricatorHandleQuery $query, 28 + array $handles, 29 + array $objects) { 30 + 31 + foreach ($handles as $phid => $handle) { 32 + $password = $objects[$phid]; 33 + } 34 + } 35 + 36 + }
+114
src/applications/auth/query/PhabricatorAuthPasswordQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $objectPHIDs; 9 + private $passwordTypes; 10 + private $isRevoked; 11 + 12 + public function withIDs(array $ids) { 13 + $this->ids = $ids; 14 + return $this; 15 + } 16 + 17 + public function withPHIDs(array $phids) { 18 + $this->phids = $phids; 19 + return $this; 20 + } 21 + 22 + public function withObjectPHIDs(array $object_phids) { 23 + $this->objectPHIDs = $object_phids; 24 + return $this; 25 + } 26 + 27 + public function withPasswordTypes(array $types) { 28 + $this->passwordTypes = $types; 29 + return $this; 30 + } 31 + 32 + public function withIsRevoked($is_revoked) { 33 + $this->isRevoked = $is_revoked; 34 + return $this; 35 + } 36 + 37 + public function newResultObject() { 38 + return new PhabricatorAuthPassword(); 39 + } 40 + 41 + protected function loadPage() { 42 + return $this->loadStandardPage($this->newResultObject()); 43 + } 44 + 45 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 46 + $where = parent::buildWhereClauseParts($conn); 47 + 48 + if ($this->ids !== null) { 49 + $where[] = qsprintf( 50 + $conn, 51 + 'id IN (%Ld)', 52 + $this->ids); 53 + } 54 + 55 + if ($this->phids !== null) { 56 + $where[] = qsprintf( 57 + $conn, 58 + 'phid IN (%Ls)', 59 + $this->phids); 60 + } 61 + 62 + if ($this->objectPHIDs !== null) { 63 + $where[] = qsprintf( 64 + $conn, 65 + 'objectPHID IN (%Ls)', 66 + $this->objectPHIDs); 67 + } 68 + 69 + if ($this->passwordTypes !== null) { 70 + $where[] = qsprintf( 71 + $conn, 72 + 'passwordType IN (%Ls)', 73 + $this->passwordTypes); 74 + } 75 + 76 + if ($this->isRevoked !== null) { 77 + $where[] = qsprintf( 78 + $conn, 79 + 'isRevoked = %d', 80 + (int)$this->isRevoked); 81 + } 82 + 83 + return $where; 84 + } 85 + 86 + protected function willFilterPage(array $passwords) { 87 + $object_phids = mpull($passwords, 'getObjectPHID'); 88 + 89 + $objects = id(new PhabricatorObjectQuery()) 90 + ->setViewer($this->getViewer()) 91 + ->setParentQuery($this) 92 + ->withPHIDs($object_phids) 93 + ->execute(); 94 + $objects = mpull($objects, null, 'getPHID'); 95 + 96 + foreach ($passwords as $key => $password) { 97 + $object = idx($objects, $password->getObjectPHID()); 98 + if (!$object) { 99 + unset($passwords[$key]); 100 + $this->didRejectResult($password); 101 + continue; 102 + } 103 + 104 + $password->attachObject($object); 105 + } 106 + 107 + return $passwords; 108 + } 109 + 110 + public function getQueryApplicationClass() { 111 + return 'PhabricatorAuthApplication'; 112 + } 113 + 114 + }
+52
src/applications/auth/revoker/PhabricatorAuthPasswordRevoker.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordRevoker 4 + extends PhabricatorAuthRevoker { 5 + 6 + const REVOKERKEY = 'password'; 7 + 8 + public function revokeAllCredentials() { 9 + $query = new PhabricatorAuthPasswordQuery(); 10 + return $this->revokeWithQuery($query); 11 + } 12 + 13 + public function revokeCredentialsFrom($object) { 14 + $query = id(new PhabricatorAuthPasswordQuery()) 15 + ->withObjectPHIDs(array($object->getPHID())); 16 + return $this->revokeWithQuery($query); 17 + } 18 + 19 + private function revokeWithQuery(PhabricatorAuthPasswordQuery $query) { 20 + $viewer = $this->getViewer(); 21 + 22 + $passwords = $query 23 + ->setViewer($viewer) 24 + ->withIsRevoked(false) 25 + ->execute(); 26 + 27 + $content_source = PhabricatorContentSource::newForSource( 28 + PhabricatorDaemonContentSource::SOURCECONST); 29 + 30 + $revoke_type = PhabricatorAuthPasswordRevokeTransaction::TRANSACTIONTYPE; 31 + 32 + $auth_phid = id(new PhabricatorAuthApplication())->getPHID(); 33 + foreach ($passwords as $password) { 34 + $xactions = array(); 35 + 36 + $xactions[] = $password->getApplicationTransactionTemplate() 37 + ->setTransactionType($revoke_type) 38 + ->setNewValue(true); 39 + 40 + $editor = $password->getApplicationTransactionEditor() 41 + ->setActor($viewer) 42 + ->setActingAsPHID($auth_phid) 43 + ->setContinueOnNoEffect(true) 44 + ->setContinueOnMissingFields(true) 45 + ->setContentSource($content_source) 46 + ->applyTransactions($password, $xactions); 47 + } 48 + 49 + return count($passwords); 50 + } 51 + 52 + }
+1 -1
src/applications/auth/revoker/PhabricatorAuthSSHRevoker.php
··· 37 37 ->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE) 38 38 ->setNewValue(1); 39 39 40 - $editor = id(new PhabricatorAuthSSHKeyEditor()) 40 + $editor = $ssh_key->getApplicationTransactionEditor() 41 41 ->setActor($viewer) 42 42 ->setActingAsPHID($auth_phid) 43 43 ->setContinueOnNoEffect(true)
+167
src/applications/auth/storage/PhabricatorAuthPassword.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPassword 4 + extends PhabricatorAuthDAO 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorDestructibleInterface, 8 + PhabricatorApplicationTransactionInterface { 9 + 10 + protected $objectPHID; 11 + protected $passwordType; 12 + protected $passwordHash; 13 + protected $isRevoked; 14 + 15 + private $object = self::ATTACHABLE; 16 + 17 + const PASSWORD_TYPE_ACCOUNT = 'account'; 18 + const PASSWORD_TYPE_VCS = 'vcs'; 19 + const PASSWORD_TYPE_TEST = 'test'; 20 + 21 + public static function initializeNewPassword( 22 + PhabricatorUser $object, 23 + $type) { 24 + 25 + return id(new self()) 26 + ->setObjectPHID($object->getPHID()) 27 + ->attachObject($object) 28 + ->setPasswordType($type) 29 + ->setIsRevoked(0); 30 + } 31 + 32 + protected function getConfiguration() { 33 + return array( 34 + self::CONFIG_AUX_PHID => true, 35 + self::CONFIG_COLUMN_SCHEMA => array( 36 + 'passwordType' => 'text64', 37 + 'passwordHash' => 'text128', 38 + 'isRevoked' => 'bool', 39 + ), 40 + self::CONFIG_KEY_SCHEMA => array( 41 + 'key_role' => array( 42 + 'columns' => array('objectPHID', 'passwordType'), 43 + ), 44 + ), 45 + ) + parent::getConfiguration(); 46 + } 47 + 48 + public function getPHIDType() { 49 + return PhabricatorAuthPasswordPHIDType::TYPECONST; 50 + } 51 + 52 + public function getObject() { 53 + return $this->assertAttached($this->object); 54 + } 55 + 56 + public function attachObject($object) { 57 + $this->object = $object; 58 + return $this; 59 + } 60 + 61 + public function setPassword( 62 + PhutilOpaqueEnvelope $password, 63 + PhabricatorUser $object) { 64 + 65 + $hasher = PhabricatorPasswordHasher::getBestHasher(); 66 + 67 + $digest = $this->digestPassword($password, $object); 68 + $hash = $hasher->getPasswordHashForStorage($digest); 69 + $raw_hash = $hash->openEnvelope(); 70 + 71 + return $this->setPasswordHash($raw_hash); 72 + } 73 + 74 + public function comparePassword( 75 + PhutilOpaqueEnvelope $password, 76 + PhabricatorUser $object) { 77 + 78 + $digest = $this->digestPassword($password, $object); 79 + $raw_hash = $this->getPasswordHash(); 80 + $hash = new PhutilOpaqueEnvelope($raw_hash); 81 + 82 + return PhabricatorPasswordHasher::comparePassword($digest, $hash); 83 + } 84 + 85 + private function digestPassword( 86 + PhutilOpaqueEnvelope $password, 87 + PhabricatorUser $object) { 88 + 89 + $object_phid = $object->getPHID(); 90 + 91 + if ($this->getObjectPHID() !== $object->getPHID()) { 92 + throw new Exception( 93 + pht( 94 + 'This password is associated with an object PHID ("%s") for '. 95 + 'a different object than the provided one ("%s").', 96 + $this->getObjectPHID(), 97 + $object->getPHID())); 98 + } 99 + 100 + $raw_input = PhabricatorHash::digestPassword($password, $object_phid); 101 + 102 + return new PhutilOpaqueEnvelope($raw_input); 103 + } 104 + 105 + 106 + 107 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 108 + 109 + 110 + public function getCapabilities() { 111 + return array( 112 + PhabricatorPolicyCapability::CAN_VIEW, 113 + ); 114 + } 115 + 116 + public function getPolicy($capability) { 117 + return PhabricatorPolicies::getMostOpenPolicy(); 118 + } 119 + 120 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 121 + return false; 122 + } 123 + 124 + 125 + /* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ 126 + 127 + 128 + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { 129 + return array( 130 + array($this->getObject(), PhabricatorPolicyCapability::CAN_VIEW), 131 + ); 132 + } 133 + 134 + 135 + /* -( PhabricatorDestructibleInterface )----------------------------------- */ 136 + 137 + 138 + public function destroyObjectPermanently( 139 + PhabricatorDestructionEngine $engine) { 140 + $this->delete(); 141 + } 142 + 143 + 144 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 145 + 146 + 147 + public function getApplicationTransactionEditor() { 148 + return new PhabricatorAuthPasswordEditor(); 149 + } 150 + 151 + public function getApplicationTransactionObject() { 152 + return $this; 153 + } 154 + 155 + public function getApplicationTransactionTemplate() { 156 + return new PhabricatorAuthPasswordTransaction(); 157 + } 158 + 159 + public function willRenderTimeline( 160 + PhabricatorApplicationTransactionView $timeline, 161 + AphrontRequest $request) { 162 + 163 + return $timeline; 164 + } 165 + 166 + 167 + }
+21
src/applications/auth/storage/PhabricatorAuthPasswordTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + public function getApplicationName() { 7 + return 'auth'; 8 + } 9 + 10 + public function getApplicationTransactionType() { 11 + return PhabricatorAuthPasswordPHIDType::TYPECONST; 12 + } 13 + 14 + public function getApplicationTransactionCommentObject() { 15 + return null; 16 + } 17 + 18 + public function getBaseTransactionClass() { 19 + return 'PhabricatorAuthPasswordTransactionType'; 20 + } 21 + }
+32
src/applications/auth/xaction/PhabricatorAuthPasswordRevokeTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthPasswordRevokeTransaction 4 + extends PhabricatorAuthPasswordTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'password.revoke'; 7 + 8 + public function generateOldValue($object) { 9 + return (bool)$object->getIsRevoked(); 10 + } 11 + 12 + public function generateNewValue($object, $value) { 13 + return (bool)$value; 14 + } 15 + 16 + public function applyInternalEffects($object, $value) { 17 + $object->setIsRevoked((int)$value); 18 + } 19 + 20 + public function getTitle() { 21 + if ($this->getNewValue()) { 22 + return pht( 23 + '%s revoked this password.', 24 + $this->renderAuthor()); 25 + } else { 26 + return pht( 27 + '%s removed this password from the revocation list.', 28 + $this->renderAuthor()); 29 + } 30 + } 31 + 32 + }
+4
src/applications/auth/xaction/PhabricatorAuthPasswordTransactionType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorAuthPasswordTransactionType 4 + extends PhabricatorModularTransactionType {}