Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894060
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
7 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/auth/storage/PhabricatorAuthChallenge.php b/src/applications/auth/storage/PhabricatorAuthChallenge.php
index 0b740e5fa7..05d00c3e6e 100644
--- a/src/applications/auth/storage/PhabricatorAuthChallenge.php
+++ b/src/applications/auth/storage/PhabricatorAuthChallenge.php
@@ -1,272 +1,272 @@
<?php
final class PhabricatorAuthChallenge
extends PhabricatorAuthDAO
implements PhabricatorPolicyInterface {
protected $userPHID;
protected $factorPHID;
protected $sessionPHID;
protected $workflowKey;
protected $challengeKey;
protected $challengeTTL;
protected $responseDigest;
protected $responseTTL;
protected $isCompleted;
protected $properties = array();
private $responseToken;
private $isNewChallenge;
const HTTPKEY = '__hisec.challenges__';
const TOKEN_DIGEST_KEY = 'auth.challenge.token';
public static function initializeNewChallenge() {
return id(new self())
->setIsCompleted(0);
}
public static function newHTTPParametersFromChallenges(array $challenges) {
assert_instances_of($challenges, __CLASS__);
$token_list = array();
foreach ($challenges as $challenge) {
$token = $challenge->getResponseToken();
if ($token) {
$token_list[] = sprintf(
'%s:%s',
$challenge->getPHID(),
$token->openEnvelope());
}
}
if (!$token_list) {
return array();
}
$token_list = implode(' ', $token_list);
return array(
self::HTTPKEY => $token_list,
);
}
public static function newChallengeResponsesFromRequest(
array $challenges,
AphrontRequest $request) {
assert_instances_of($challenges, __CLASS__);
- $token_list = $request->getStr(self::HTTPKEY);
+ $token_list = $request->getStr(self::HTTPKEY, '');
$token_list = explode(' ', $token_list);
$token_map = array();
foreach ($token_list as $token_element) {
$token_element = trim($token_element, ' ');
if (!strlen($token_element)) {
continue;
}
// NOTE: This error message is intentionally not printing the token to
// avoid disclosing it. As a result, it isn't terribly useful, but no
// normal user should ever end up here.
if (!preg_match('/^[^:]+:/', $token_element)) {
throw new Exception(
pht(
'This request included an improperly formatted MFA challenge '.
'token and can not be processed.'));
}
list($phid, $token) = explode(':', $token_element, 2);
if (isset($token_map[$phid])) {
throw new Exception(
pht(
'This request improperly specifies an MFA challenge token ("%s") '.
'multiple times and can not be processed.',
$phid));
}
$token_map[$phid] = new PhutilOpaqueEnvelope($token);
}
$challenges = mpull($challenges, null, 'getPHID');
$now = PhabricatorTime::getNow();
foreach ($challenges as $challenge_phid => $challenge) {
// If the response window has expired, don't attach the token.
if ($challenge->getResponseTTL() < $now) {
continue;
}
$token = idx($token_map, $challenge_phid);
if (!$token) {
continue;
}
$challenge->setResponseToken($token);
}
}
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'properties' => self::SERIALIZATION_JSON,
),
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'challengeKey' => 'text255',
'challengeTTL' => 'epoch',
'workflowKey' => 'text255',
'responseDigest' => 'text255?',
'responseTTL' => 'epoch?',
'isCompleted' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_issued' => array(
'columns' => array('userPHID', 'challengeTTL'),
),
'key_collection' => array(
'columns' => array('challengeTTL'),
),
),
) + parent::getConfiguration();
}
public function getPHIDType() {
return PhabricatorAuthChallengePHIDType::TYPECONST;
}
public function getIsReusedChallenge() {
if ($this->getIsCompleted()) {
return true;
}
if (!$this->getIsAnsweredChallenge()) {
return false;
}
// If the challenge has been answered but the client has provided a token
// proving that they answered it, this is still a valid response.
if ($this->getResponseToken()) {
return false;
}
return true;
}
public function getIsAnsweredChallenge() {
return (bool)$this->getResponseDigest();
}
public function markChallengeAsAnswered($ttl) {
$token = Filesystem::readRandomCharacters(32);
$token = new PhutilOpaqueEnvelope($token);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$this
->setResponseToken($token)
->setResponseTTL($ttl)
->save();
unset($unguarded);
return $this;
}
public function markChallengeAsCompleted() {
return $this
->setIsCompleted(true)
->save();
}
public function setResponseToken(PhutilOpaqueEnvelope $token) {
if (!$this->getUserPHID()) {
throw new PhutilInvalidStateException('setUserPHID');
}
if ($this->responseToken) {
throw new Exception(
pht(
'This challenge already has a response token; you can not '.
'set a new response token.'));
}
if (preg_match('/ /', $token->openEnvelope())) {
throw new Exception(
pht(
'The response token for this challenge is invalid: response '.
'tokens may not include spaces.'));
}
$digest = PhabricatorHash::digestWithNamedKey(
$token->openEnvelope(),
self::TOKEN_DIGEST_KEY);
if ($this->responseDigest !== null) {
if (!phutil_hashes_are_identical($digest, $this->responseDigest)) {
throw new Exception(
pht(
'Invalid response token for this challenge: token digest does '.
'not match stored digest.'));
}
} else {
$this->responseDigest = $digest;
}
$this->responseToken = $token;
return $this;
}
public function getResponseToken() {
return $this->responseToken;
}
public function setResponseDigest($value) {
throw new Exception(
pht(
'You can not set the response digest for a challenge directly. '.
'Instead, set a response token. A response digest will be computed '.
'automatically.'));
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function getProperty($key, $default = null) {
return $this->properties[$key];
}
public function setIsNewChallenge($is_new_challenge) {
$this->isNewChallenge = $is_new_challenge;
return $this;
}
public function getIsNewChallenge() {
return $this->isNewChallenge;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::POLICY_NOONE;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return ($viewer->getPHID() === $this->getUserPHID());
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:18 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127901
Default Alt Text
(7 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment