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

Make temporary token storage/schema more flexible

Summary:
Ref T10603. This makes minor updates to temporary tokens:

- Rename `objectPHID` (which is sometimes used to store some other kind of identifier instead of a PHID) to `tokenResource` (i.e., which resource does this token permit access to?).
- Add a `userPHID` column. For LFS tokens and some other types of tokens, I want to bind the token to both a resource (like a repository) and a user.
- Add a `properties` column. This makes tokens more flexible and supports custom behavior (like scoping LFS tokens even more tightly).

Test Plan:
- Ran `bin/storage upgrade -f`, got a clean upgrade.
- Viewed one-time tokens.
- Revoked one token.
- Revoked all tokens.
- Performed a one-time login.
- Performed a password reset.
- Added an MFA token.
- Removed an MFA token.
- Used a file token to view a file.
- Verified file token was removed after viewing file.
- Linked my account to an OAuth1 account (Twitter).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10603

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

+77 -47
+2
resources/sql/autopatches/20160316.lfs.01.token.resource.sql
··· 1 + ALTER TABLE {$NAMESPACE}_auth.auth_temporarytoken 2 + CHANGE objectPHID tokenResource VARBINARY(64) NOT NULL;
+2
resources/sql/autopatches/20160316.lfs.02.token.user.sql
··· 1 + ALTER TABLE {$NAMESPACE}_auth.auth_temporarytoken 2 + ADD userPHID VARBINARY(64);
+2
resources/sql/autopatches/20160316.lfs.03.token.properties.sql
··· 1 + ALTER TABLE {$NAMESPACE}_auth.auth_temporarytoken 2 + ADD properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20160316.lfs.04.token.default.sql
··· 1 + UPDATE {$NAMESPACE}_auth.auth_temporarytoken 2 + SET properties = '{}' WHERE properties = '';
+1 -1
src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php
··· 132 132 133 133 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 134 134 id(new PhabricatorAuthTemporaryToken()) 135 - ->setObjectPHID($target_user->getPHID()) 135 + ->setTokenResource($target_user->getPHID()) 136 136 ->setTokenType($password_type) 137 137 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 138 138 ->setTokenCode(PhabricatorHash::digest($key))
+1 -1
src/applications/auth/controller/PhabricatorAuthRevokeTokenController.php
··· 11 11 12 12 $query = id(new PhabricatorAuthTemporaryTokenQuery()) 13 13 ->setViewer($viewer) 14 - ->withObjectPHIDs(array($viewer->getPHID())); 14 + ->withTokenResources(array($viewer->getPHID())); 15 15 if (!$is_all) { 16 16 $query->withIDs(array($id)); 17 17 }
+2 -2
src/applications/auth/engine/PhabricatorAuthSessionEngine.php
··· 635 635 636 636 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 637 637 id(new PhabricatorAuthTemporaryToken()) 638 - ->setObjectPHID($user->getPHID()) 638 + ->setTokenResource($user->getPHID()) 639 639 ->setTokenType($onetime_type) 640 640 ->setTokenExpires(time() + phutil_units('1 day in seconds')) 641 641 ->setTokenCode($key_hash) ··· 679 679 680 680 return id(new PhabricatorAuthTemporaryTokenQuery()) 681 681 ->setViewer($user) 682 - ->withObjectPHIDs(array($user->getPHID())) 682 + ->withTokenResources(array($user->getPHID())) 683 683 ->withTokenTypes(array($onetime_type)) 684 684 ->withTokenCodes(array($key_hash)) 685 685 ->withExpired(false)
+2 -2
src/applications/auth/factor/PhabricatorTOTPAuthFactor.php
··· 36 36 37 37 $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) 38 38 ->setViewer($user) 39 - ->withObjectPHIDs(array($user->getPHID())) 39 + ->withTokenResources(array($user->getPHID())) 40 40 ->withTokenTypes(array(self::TEMPORARY_TOKEN_TYPE)) 41 41 ->withExpired(false) 42 42 ->withTokenCodes(array(PhabricatorHash::digest($key))) ··· 55 55 56 56 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 57 57 id(new PhabricatorAuthTemporaryToken()) 58 - ->setObjectPHID($user->getPHID()) 58 + ->setTokenResource($user->getPHID()) 59 59 ->setTokenType(self::TEMPORARY_TOKEN_TYPE) 60 60 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 61 61 ->setTokenCode(PhabricatorHash::digest($key))
+3 -3
src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php
··· 221 221 // Wipe out an existing token, if one exists. 222 222 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 223 223 ->setViewer(PhabricatorUser::getOmnipotentUser()) 224 - ->withObjectPHIDs(array($key)) 224 + ->withTokenResources(array($key)) 225 225 ->withTokenTypes(array($type)) 226 226 ->executeOne(); 227 227 if ($token) { ··· 230 230 231 231 // Save the new secret. 232 232 id(new PhabricatorAuthTemporaryToken()) 233 - ->setObjectPHID($key) 233 + ->setTokenResource($key) 234 234 ->setTokenType($type) 235 235 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 236 236 ->setTokenCode($secret) ··· 243 243 244 244 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 245 245 ->setViewer(PhabricatorUser::getOmnipotentUser()) 246 - ->withObjectPHIDs(array($key)) 246 + ->withTokenResources(array($key)) 247 247 ->withTokenTypes(array($type)) 248 248 ->withExpired(false) 249 249 ->executeOne();
+31 -27
src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php
··· 4 4 extends PhabricatorCursorPagedPolicyAwareQuery { 5 5 6 6 private $ids; 7 - private $objectPHIDs; 7 + private $tokenResources; 8 8 private $tokenTypes; 9 + private $userPHIDs; 9 10 private $expired; 10 11 private $tokenCodes; 11 12 ··· 14 15 return $this; 15 16 } 16 17 17 - public function withObjectPHIDs(array $object_phids) { 18 - $this->objectPHIDs = $object_phids; 18 + public function withTokenResources(array $resources) { 19 + $this->tokenResources = $resources; 19 20 return $this; 20 21 } 21 22 ··· 34 35 return $this; 35 36 } 36 37 37 - protected function loadPage() { 38 - $table = new PhabricatorAuthTemporaryToken(); 39 - $conn_r = $table->establishConnection('r'); 38 + public function withUserPHIDs(array $phids) { 39 + $this->userPHIDs = $phids; 40 + return $this; 41 + } 40 42 41 - $data = queryfx_all( 42 - $conn_r, 43 - 'SELECT * FROM %T %Q %Q %Q', 44 - $table->getTableName(), 45 - $this->buildWhereClause($conn_r), 46 - $this->buildOrderClause($conn_r), 47 - $this->buildLimitClause($conn_r)); 43 + public function newResultObject() { 44 + return new PhabricatorAuthTemporaryToken(); 45 + } 48 46 49 - return $table->loadAllFromArray($data); 47 + protected function loadPage() { 48 + return $this->loadStandardPage($this->newResultObject()); 50 49 } 51 50 52 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 53 - $where = array(); 51 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 52 + $where = parent::buildWhereClauseParts($conn); 54 53 55 54 if ($this->ids !== null) { 56 55 $where[] = qsprintf( 57 - $conn_r, 56 + $conn, 58 57 'id IN (%Ld)', 59 58 $this->ids); 60 59 } 61 60 62 - if ($this->objectPHIDs !== null) { 61 + if ($this->tokenResources !== null) { 63 62 $where[] = qsprintf( 64 - $conn_r, 65 - 'objectPHID IN (%Ls)', 66 - $this->objectPHIDs); 63 + $conn, 64 + 'tokenResource IN (%Ls)', 65 + $this->tokenResources); 67 66 } 68 67 69 68 if ($this->tokenTypes !== null) { 70 69 $where[] = qsprintf( 71 - $conn_r, 70 + $conn, 72 71 'tokenType IN (%Ls)', 73 72 $this->tokenTypes); 74 73 } ··· 76 75 if ($this->expired !== null) { 77 76 if ($this->expired) { 78 77 $where[] = qsprintf( 79 - $conn_r, 78 + $conn, 80 79 'tokenExpires <= %d', 81 80 time()); 82 81 } else { 83 82 $where[] = qsprintf( 84 - $conn_r, 83 + $conn, 85 84 'tokenExpires > %d', 86 85 time()); 87 86 } ··· 89 88 90 89 if ($this->tokenCodes !== null) { 91 90 $where[] = qsprintf( 92 - $conn_r, 91 + $conn, 93 92 'tokenCode IN (%Ls)', 94 93 $this->tokenCodes); 95 94 } 96 95 97 - $where[] = $this->buildPagingClause($conn_r); 96 + if ($this->userPHIDs !== null) { 97 + $where[] = qsprintf( 98 + $conn, 99 + 'userPHID IN (%Ls)', 100 + $this->userPHIDs); 101 + } 98 102 99 - return $this->formatWhereClause($where); 103 + return $where; 100 104 } 101 105 102 106 public function getQueryApplicationClass() {
+25 -7
src/applications/auth/storage/PhabricatorAuthTemporaryToken.php
··· 3 3 final class PhabricatorAuthTemporaryToken extends PhabricatorAuthDAO 4 4 implements PhabricatorPolicyInterface { 5 5 6 - // TODO: OAuth1 stores a client identifier here, which is not a real PHID. 7 - // At some point, we should rename this column to be a little more generic. 8 - protected $objectPHID; 9 - 6 + // NOTE: This is usually a PHID, but may be some other kind of resource 7 + // identifier for some token types. 8 + protected $tokenResource; 10 9 protected $tokenType; 11 10 protected $tokenExpires; 12 11 protected $tokenCode; 12 + protected $userPHID; 13 + protected $properties; 13 14 14 15 protected function getConfiguration() { 15 16 return array( 16 17 self::CONFIG_TIMESTAMPS => false, 18 + self::CONFIG_SERIALIZATION => array( 19 + 'properties' => self::SERIALIZATION_JSON, 20 + ), 17 21 self::CONFIG_COLUMN_SCHEMA => array( 22 + 'tokenResource' => 'phid', 18 23 'tokenType' => 'text64', 19 24 'tokenExpires' => 'epoch', 20 25 'tokenCode' => 'text64', 26 + 'userPHID' => 'phid?', 21 27 ), 22 28 self::CONFIG_KEY_SCHEMA => array( 23 29 'key_token' => array( 24 - 'columns' => array('objectPHID', 'tokenType', 'tokenCode'), 30 + 'columns' => array('tokenResource', 'tokenType', 'tokenCode'), 25 31 'unique' => true, 26 32 ), 27 33 'key_expires' => array( 28 34 'columns' => array('tokenExpires'), 29 35 ), 36 + 'key_user' => array( 37 + 'columns' => array('userPHID'), 38 + ), 30 39 ), 31 40 ) + parent::getConfiguration(); 32 41 } ··· 73 82 74 83 public static function revokeTokens( 75 84 PhabricatorUser $viewer, 76 - array $object_phids, 85 + array $token_resources, 77 86 array $token_types) { 78 87 79 88 $tokens = id(new PhabricatorAuthTemporaryTokenQuery()) 80 89 ->setViewer($viewer) 81 - ->withObjectPHIDs($object_phids) 90 + ->withTokenResources($token_resources) 82 91 ->withTokenTypes($token_types) 83 92 ->withExpired(false) 84 93 ->execute(); ··· 86 95 foreach ($tokens as $token) { 87 96 $token->revokeToken(); 88 97 } 98 + } 99 + 100 + public function getTemporaryTokenProperty($key, $default = null) { 101 + return idx($this->properties, $key, $default); 102 + } 103 + 104 + public function setTemporaryTokenProperty($key, $value) { 105 + $this->properties[$key] = $value; 106 + return $this; 89 107 } 90 108 91 109 /* -( PhabricatorPolicyInterface )----------------------------------------- */
+2 -2
src/applications/files/storage/PhabricatorFile.php
··· 1123 1123 // Save the new secret. 1124 1124 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 1125 1125 $token = id(new PhabricatorAuthTemporaryToken()) 1126 - ->setObjectPHID($this->getPHID()) 1126 + ->setTokenResource($this->getPHID()) 1127 1127 ->setTokenType(self::ONETIME_TEMPORARY_TOKEN_TYPE) 1128 1128 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 1129 1129 ->setTokenCode(PhabricatorHash::digest($key)) ··· 1136 1136 public function validateOneTimeToken($token_code) { 1137 1137 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 1138 1138 ->setViewer(PhabricatorUser::getOmnipotentUser()) 1139 - ->withObjectPHIDs(array($this->getPHID())) 1139 + ->withTokenResources(array($this->getPHID())) 1140 1140 ->withTokenTypes(array(self::ONETIME_TEMPORARY_TOKEN_TYPE)) 1141 1141 ->withExpired(false) 1142 1142 ->withTokenCodes(array(PhabricatorHash::digest($token_code)))
+1 -1
src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php
··· 46 46 if ($key) { 47 47 $token = id(new PhabricatorAuthTemporaryTokenQuery()) 48 48 ->setViewer($user) 49 - ->withObjectPHIDs(array($user->getPHID())) 49 + ->withTokenResources(array($user->getPHID())) 50 50 ->withTokenTypes(array($password_type)) 51 51 ->withTokenCodes(array(PhabricatorHash::digest($key))) 52 52 ->withExpired(false)
+1 -1
src/applications/settings/panel/PhabricatorTokensSettingsPanel.php
··· 23 23 24 24 $tokens = id(new PhabricatorAuthTemporaryTokenQuery()) 25 25 ->setViewer($viewer) 26 - ->withObjectPHIDs(array($viewer->getPHID())) 26 + ->withTokenResources(array($viewer->getPHID())) 27 27 ->execute(); 28 28 29 29 $rows = array();