Changeset View
Changeset View
Standalone View
Standalone View
src/applications/conduit/storage/PhabricatorConduitToken.php
| <?php | <?php | ||||
| final class PhabricatorConduitToken | final class PhabricatorConduitToken | ||||
| extends PhabricatorConduitDAO | extends PhabricatorConduitDAO | ||||
| implements PhabricatorPolicyInterface { | implements PhabricatorPolicyInterface { | ||||
| protected $objectPHID; | protected $objectPHID; | ||||
| protected $tokenType; | protected $tokenType; | ||||
| protected $token; | protected $token; | ||||
| protected $expires; | protected $expires; | ||||
| private $object = self::ATTACHABLE; | private $object = self::ATTACHABLE; | ||||
| const TYPE_STANDARD = 'api'; | const TYPE_STANDARD = 'api'; | ||||
| const TYPE_TEMPORARY = 'tmp'; | |||||
| const TYPE_COMMANDLINE = 'cli'; | const TYPE_COMMANDLINE = 'cli'; | ||||
| const TYPE_CLUSTER = 'clr'; | |||||
| public function getConfiguration() { | public function getConfiguration() { | ||||
| return array( | return array( | ||||
| self::CONFIG_COLUMN_SCHEMA => array( | self::CONFIG_COLUMN_SCHEMA => array( | ||||
| 'tokenType' => 'text32', | 'tokenType' => 'text32', | ||||
| 'token' => 'text32', | 'token' => 'text32', | ||||
| 'expires' => 'epoch?', | 'expires' => 'epoch?', | ||||
| ), | ), | ||||
| self::CONFIG_KEY_SCHEMA => array( | self::CONFIG_KEY_SCHEMA => array( | ||||
| 'key_object' => array( | 'key_object' => array( | ||||
| 'columns' => array('objectPHID', 'tokenType'), | 'columns' => array('objectPHID', 'tokenType'), | ||||
| ), | ), | ||||
| 'key_token' => array( | 'key_token' => array( | ||||
| 'columns' => array('token'), | 'columns' => array('token'), | ||||
| 'unique' => true, | 'unique' => true, | ||||
| ), | ), | ||||
| 'key_expires' => array( | 'key_expires' => array( | ||||
| 'columns' => array('expires'), | 'columns' => array('expires'), | ||||
| ), | ), | ||||
| ), | ), | ||||
| ) + parent::getConfiguration(); | ) + parent::getConfiguration(); | ||||
| } | } | ||||
| public static function loadClusterTokenForUser(PhabricatorUser $user) { | |||||
| if (!$user->isLoggedIn()) { | |||||
| return null; | |||||
| } | |||||
| $tokens = id(new PhabricatorConduitTokenQuery()) | |||||
| ->setViewer($user) | |||||
| ->withObjectPHIDs(array($user->getPHID())) | |||||
| ->withTokenTypes(array(self::TYPE_CLUSTER)) | |||||
| ->withExpired(false) | |||||
| ->execute(); | |||||
| // Only return a token if it has at least 5 minutes left before | |||||
| // expiration. Cluster tokens cycle regularly, so we don't want to use | |||||
| // one that's going to expire momentarily. | |||||
| $now = PhabricatorTime::getNow(); | |||||
| $must_expire_after = $now + phutil_units('5 minutes in seconds'); | |||||
| foreach ($tokens as $token) { | |||||
| if ($token->getExpires() > $must_expire_after) { | |||||
| return $token; | |||||
| } | |||||
| } | |||||
| // We didn't find any existing tokens (or the existing tokens are all about | |||||
| // to expire) so generate a new token. | |||||
| $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | |||||
| $token = PhabricatorConduitToken::initializeNewToken( | |||||
| $user->getPHID(), | |||||
| self::TYPE_CLUSTER); | |||||
| $token->save(); | |||||
| unset($unguarded); | |||||
| return $token; | |||||
| } | |||||
| public static function initializeNewToken($object_phid, $token_type) { | public static function initializeNewToken($object_phid, $token_type) { | ||||
| $token = new PhabricatorConduitToken(); | $token = new PhabricatorConduitToken(); | ||||
| $token->objectPHID = $object_phid; | $token->objectPHID = $object_phid; | ||||
| $token->tokenType = $token_type; | $token->tokenType = $token_type; | ||||
| $token->expires = $token->getTokenExpires($token_type); | $token->expires = $token->getTokenExpires($token_type); | ||||
| $secret = $token_type.'-'.Filesystem::readRandomCharacters(32); | $secret = $token_type.'-'.Filesystem::readRandomCharacters(32); | ||||
| $secret = substr($secret, 0, 32); | $secret = substr($secret, 0, 32); | ||||
| $token->token = $secret; | $token->token = $secret; | ||||
| return $token; | return $token; | ||||
| } | } | ||||
| public static function getTokenTypeName($type) { | public static function getTokenTypeName($type) { | ||||
| $map = array( | $map = array( | ||||
| self::TYPE_STANDARD => pht('Standard API Token'), | self::TYPE_STANDARD => pht('Standard API Token'), | ||||
| self::TYPE_TEMPORARY => pht('Temporary API Token'), | |||||
| self::TYPE_COMMANDLINE => pht('Command Line API Token'), | self::TYPE_COMMANDLINE => pht('Command Line API Token'), | ||||
| self::TYPE_CLUSTER => pht('Cluster API Token'), | |||||
| ); | ); | ||||
| return idx($map, $type, $type); | return idx($map, $type, $type); | ||||
| } | } | ||||
| public static function getAllTokenTypes() { | public static function getAllTokenTypes() { | ||||
| return array( | return array( | ||||
| self::TYPE_STANDARD, | self::TYPE_STANDARD, | ||||
| self::TYPE_TEMPORARY, | |||||
| self::TYPE_COMMANDLINE, | self::TYPE_COMMANDLINE, | ||||
| self::TYPE_CLUSTER, | |||||
| ); | ); | ||||
| } | } | ||||
| private function getTokenExpires($token_type) { | private function getTokenExpires($token_type) { | ||||
| $now = PhabricatorTime::getNow(); | |||||
| switch ($token_type) { | switch ($token_type) { | ||||
| case self::TYPE_STANDARD: | case self::TYPE_STANDARD: | ||||
| return null; | return null; | ||||
| case self::TYPE_TEMPORARY: | |||||
| return PhabricatorTime::getNow() + phutil_units('24 hours in seconds'); | |||||
| case self::TYPE_COMMANDLINE: | case self::TYPE_COMMANDLINE: | ||||
| return PhabricatorTime::getNow() + phutil_units('1 hour in seconds'); | return $now + phutil_units('1 hour in seconds'); | ||||
| case self::TYPE_CLUSTER: | |||||
| return $now + phutil_units('30 minutes in seconds'); | |||||
| default: | default: | ||||
| throw new Exception( | throw new Exception( | ||||
| pht('Unknown Conduit token type "%s"!', $token_type)); | pht('Unknown Conduit token type "%s"!', $token_type)); | ||||
| } | } | ||||
| } | } | ||||
| public function getPublicTokenName() { | |||||
| switch ($this->getTokenType()) { | |||||
| case self::TYPE_CLUSTER: | |||||
| return pht('Cluster API Token'); | |||||
| default: | |||||
| return substr($this->getToken(), 0, 8).'...'; | |||||
| } | |||||
| } | |||||
| public function getObject() { | public function getObject() { | ||||
| return $this->assertAttached($this->object); | return $this->assertAttached($this->object); | ||||
| } | } | ||||
| public function attachObject(PhabricatorUser $object) { | public function attachObject(PhabricatorUser $object) { | ||||
| $this->object = $object; | $this->object = $object; | ||||
| return $this; | return $this; | ||||
| } | } | ||||
| Show All 26 Lines | |||||