Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2889878
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
31 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/auth/factor/PhabricatorAuthFactor.php b/src/applications/auth/factor/PhabricatorAuthFactor.php
index fefd9b5fd1..79ea17de7f 100644
--- a/src/applications/auth/factor/PhabricatorAuthFactor.php
+++ b/src/applications/auth/factor/PhabricatorAuthFactor.php
@@ -1,626 +1,626 @@
<?php
abstract class PhabricatorAuthFactor extends Phobject {
abstract public function getFactorName();
abstract public function getFactorShortName();
abstract public function getFactorKey();
abstract public function getFactorCreateHelp();
abstract public function getFactorDescription();
abstract public function processAddFactorForm(
PhabricatorAuthFactorProvider $provider,
AphrontFormView $form,
AphrontRequest $request,
PhabricatorUser $user);
abstract public function renderValidateFactorForm(
PhabricatorAuthFactorConfig $config,
AphrontFormView $form,
PhabricatorUser $viewer,
PhabricatorAuthFactorResult $validation_result);
public function getParameterName(
PhabricatorAuthFactorConfig $config,
$name) {
return 'authfactor.'.$config->getID().'.'.$name;
}
public static function getAllFactors() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getFactorKey')
->execute();
}
protected function newConfigForUser(PhabricatorUser $user) {
return id(new PhabricatorAuthFactorConfig())
->setUserPHID($user->getPHID())
->setFactorSecret('');
}
protected function newResult() {
return new PhabricatorAuthFactorResult();
}
public function newIconView() {
return id(new PHUIIconView())
->setIcon('fa-mobile');
}
public function canCreateNewProvider() {
return true;
}
public function getProviderCreateDescription() {
return null;
}
public function canCreateNewConfiguration(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user) {
return true;
}
public function getConfigurationCreateDescription(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user) {
return null;
}
public function getConfigurationListDetails(
PhabricatorAuthFactorConfig $config,
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $viewer) {
return null;
}
public function newEditEngineFields(
PhabricatorEditEngine $engine,
PhabricatorAuthFactorProvider $provider) {
return array();
}
public function newChallengeStatusView(
PhabricatorAuthFactorConfig $config,
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $viewer,
PhabricatorAuthChallenge $challenge) {
return null;
}
/**
* Is this a factor which depends on the user's contact number?
*
* If a user has a "contact number" factor configured, they can not modify
* or switch their primary contact number.
*
* @return bool True if this factor should lock contact numbers.
*/
public function isContactNumberFactor() {
return false;
}
abstract public function getEnrollDescription(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user);
public function getEnrollButtonText(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user) {
return pht('Continue');
}
public function getFactorOrder() {
return 1000;
}
final public function newSortVector() {
return id(new PhutilSortVector())
->addInt($this->canCreateNewProvider() ? 0 : 1)
->addInt($this->getFactorOrder())
->addString($this->getFactorName());
}
protected function newChallenge(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer) {
$engine = $config->getSessionEngine();
return PhabricatorAuthChallenge::initializeNewChallenge()
->setUserPHID($viewer->getPHID())
->setSessionPHID($viewer->getSession()->getPHID())
->setFactorPHID($config->getPHID())
->setIsNewChallenge(true)
->setWorkflowKey($engine->getWorkflowKey());
}
abstract public function getRequestHasChallengeResponse(
PhabricatorAuthFactorConfig $config,
AphrontRequest $response);
final public function getNewIssuedChallenges(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
array $challenges) {
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
$now = PhabricatorTime::getNow();
// Factor implementations may need to perform writes in order to issue
// challenges, particularly push factors like SMS.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$new_challenges = $this->newIssuedChallenges(
$config,
$viewer,
$challenges);
if ($this->isAuthResult($new_challenges)) {
unset($unguarded);
return $new_challenges;
}
assert_instances_of($new_challenges, 'PhabricatorAuthChallenge');
foreach ($new_challenges as $new_challenge) {
$ttl = $new_challenge->getChallengeTTL();
if (!$ttl) {
throw new Exception(
pht('Newly issued MFA challenges must have a valid TTL!'));
}
if ($ttl < $now) {
throw new Exception(
pht(
'Newly issued MFA challenges must have a future TTL. This '.
'factor issued a bad TTL ("%s"). (Did you use a relative '.
'time instead of an epoch?)',
$ttl));
}
}
foreach ($new_challenges as $challenge) {
$challenge->save();
}
unset($unguarded);
return $new_challenges;
}
abstract protected function newIssuedChallenges(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
array $challenges);
final public function getResultFromIssuedChallenges(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
array $challenges) {
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
$result = $this->newResultFromIssuedChallenges(
$config,
$viewer,
$challenges);
if ($result === null) {
return $result;
}
if (!$this->isAuthResult($result)) {
throw new Exception(
pht(
'Expected "newResultFromIssuedChallenges()" to return null or '.
'an object of class "%s"; got something else (in "%s").',
'PhabricatorAuthFactorResult',
get_class($this)));
}
return $result;
}
final public function getResultForPrompt(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
AphrontRequest $request,
array $challenges) {
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
$result = $this->newResultForPrompt(
$config,
$viewer,
$request,
$challenges);
if (!$this->isAuthResult($result)) {
throw new Exception(
pht(
'Expected "newResultForPrompt()" to return an object of class "%s", '.
'but it returned something else ("%s"; in "%s").',
'PhabricatorAuthFactorResult',
phutil_describe_type($result),
get_class($this)));
}
return $result;
}
protected function newResultForPrompt(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
AphrontRequest $request,
array $challenges) {
return $this->newResult();
}
abstract protected function newResultFromIssuedChallenges(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
array $challenges);
final public function getResultFromChallengeResponse(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
AphrontRequest $request,
array $challenges) {
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
$result = $this->newResultFromChallengeResponse(
$config,
$viewer,
$request,
$challenges);
if (!$this->isAuthResult($result)) {
throw new Exception(
pht(
'Expected "newResultFromChallengeResponse()" to return an object '.
'of class "%s"; got something else (in "%s").',
'PhabricatorAuthFactorResult',
get_class($this)));
}
return $result;
}
abstract protected function newResultFromChallengeResponse(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
AphrontRequest $request,
array $challenges);
final protected function newAutomaticControl(
PhabricatorAuthFactorResult $result) {
$is_error = $result->getIsError();
if ($is_error) {
return $this->newErrorControl($result);
}
$is_continue = $result->getIsContinue();
if ($is_continue) {
return $this->newContinueControl($result);
}
$is_answered = (bool)$result->getAnsweredChallenge();
if ($is_answered) {
return $this->newAnsweredControl($result);
}
$is_wait = $result->getIsWait();
if ($is_wait) {
return $this->newWaitControl($result);
}
return null;
}
private function newWaitControl(
PhabricatorAuthFactorResult $result) {
$error = $result->getErrorMessage();
$icon = $result->getIcon();
if (!$icon) {
$icon = id(new PHUIIconView())
->setIcon('fa-clock-o', 'red');
}
return id(new PHUIFormTimerControl())
->setIcon($icon)
->appendChild($error)
->setError(pht('Wait'));
}
private function newAnsweredControl(
PhabricatorAuthFactorResult $result) {
$icon = $result->getIcon();
if (!$icon) {
$icon = id(new PHUIIconView())
->setIcon('fa-check-circle-o', 'green');
}
return id(new PHUIFormTimerControl())
->setIcon($icon)
->appendChild(
pht('You responded to this challenge correctly.'));
}
private function newErrorControl(
PhabricatorAuthFactorResult $result) {
$error = $result->getErrorMessage();
$icon = $result->getIcon();
if (!$icon) {
$icon = id(new PHUIIconView())
->setIcon('fa-times', 'red');
}
return id(new PHUIFormTimerControl())
->setIcon($icon)
->appendChild($error)
->setError(pht('Error'));
}
private function newContinueControl(
PhabricatorAuthFactorResult $result) {
$error = $result->getErrorMessage();
$icon = $result->getIcon();
if (!$icon) {
$icon = id(new PHUIIconView())
->setIcon('fa-commenting', 'green');
}
$control = id(new PHUIFormTimerControl())
->setIcon($icon)
->appendChild($error);
$status_challenge = $result->getStatusChallenge();
if ($status_challenge) {
$id = $status_challenge->getID();
$uri = "/auth/mfa/challenge/status/{$id}/";
$control->setUpdateURI($uri);
}
return $control;
}
/* -( Synchronizing New Factors )------------------------------------------ */
final protected function loadMFASyncToken(
PhabricatorAuthFactorProvider $provider,
AphrontRequest $request,
AphrontFormView $form,
PhabricatorUser $user) {
// If the form included a synchronization key, load the corresponding
// token. The user must synchronize to a key we generated because this
// raises the barrier to theoretical attacks where an attacker might
// provide a known key for factors like TOTP.
// (We store and verify the hash of the key, not the key itself, to limit
// how useful the data in the table is to an attacker.)
$sync_type = PhabricatorAuthMFASyncTemporaryTokenType::TOKENTYPE;
$sync_token = null;
- $sync_key = $request->getStr($this->getMFASyncTokenFormKey());
- if (strlen($sync_key)) {
+ $sync_key = $request->getStr($this->getMFASyncTokenFormKey(), '');
+ if ($sync_key !== '') {
$sync_key_digest = PhabricatorHash::digestWithNamedKey(
$sync_key,
PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY);
$sync_token = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($user)
->withTokenResources(array($user->getPHID()))
->withTokenTypes(array($sync_type))
->withExpired(false)
->withTokenCodes(array($sync_key_digest))
->executeOne();
}
if (!$sync_token) {
// Don't generate a new sync token if there are too many outstanding
// tokens already. This is mostly relevant for push factors like SMS,
// where generating a token has the side effect of sending a user a
// message.
$outstanding_limit = 10;
$outstanding_tokens = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($user)
->withTokenResources(array($user->getPHID()))
->withTokenTypes(array($sync_type))
->withExpired(false)
->execute();
if (count($outstanding_tokens) > $outstanding_limit) {
throw new Exception(
pht(
'Your account has too many outstanding, incomplete MFA '.
'synchronization attempts. Wait an hour and try again.'));
}
$now = PhabricatorTime::getNow();
$sync_key = Filesystem::readRandomCharacters(32);
$sync_key_digest = PhabricatorHash::digestWithNamedKey(
$sync_key,
PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY);
$sync_ttl = $this->getMFASyncTokenTTL();
$sync_token = id(new PhabricatorAuthTemporaryToken())
->setIsNewTemporaryToken(true)
->setTokenResource($user->getPHID())
->setTokenType($sync_type)
->setTokenCode($sync_key_digest)
->setTokenExpires($now + $sync_ttl);
$properties = $this->newMFASyncTokenProperties(
$provider,
$user);
if ($this->isAuthResult($properties)) {
return $properties;
}
foreach ($properties as $key => $value) {
$sync_token->setTemporaryTokenProperty($key, $value);
}
$sync_token->save();
}
$form->addHiddenInput($this->getMFASyncTokenFormKey(), $sync_key);
return $sync_token;
}
protected function newMFASyncTokenProperties(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user) {
return array();
}
private function getMFASyncTokenFormKey() {
return 'sync.key';
}
private function getMFASyncTokenTTL() {
return phutil_units('1 hour in seconds');
}
final protected function getChallengeForCurrentContext(
PhabricatorAuthFactorConfig $config,
PhabricatorUser $viewer,
array $challenges) {
$session_phid = $viewer->getSession()->getPHID();
$engine = $config->getSessionEngine();
$workflow_key = $engine->getWorkflowKey();
foreach ($challenges as $challenge) {
if ($challenge->getSessionPHID() !== $session_phid) {
continue;
}
if ($challenge->getWorkflowKey() !== $workflow_key) {
continue;
}
if ($challenge->getIsCompleted()) {
continue;
}
if ($challenge->getIsReusedChallenge()) {
continue;
}
return $challenge;
}
return null;
}
/**
* @phutil-external-symbol class QRcode
*/
final protected function newQRCode($uri) {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/phpqrcode/phpqrcode.php';
$lines = QRcode::text($uri);
$total_width = 240;
$cell_size = floor($total_width / count($lines));
$rows = array();
foreach ($lines as $line) {
$cells = array();
for ($ii = 0; $ii < strlen($line); $ii++) {
if ($line[$ii] == '1') {
$color = '#000';
} else {
$color = '#fff';
}
$cells[] = phutil_tag(
'td',
array(
'width' => $cell_size,
'height' => $cell_size,
'style' => 'background: '.$color,
),
'');
}
$rows[] = phutil_tag('tr', array(), $cells);
}
return phutil_tag(
'table',
array(
'style' => 'margin: 24px auto;',
),
$rows);
}
final protected function getInstallDisplayName() {
$uri = PhabricatorEnv::getURI('/');
$uri = new PhutilURI($uri);
return $uri->getDomain();
}
final protected function getChallengeResponseParameterName(
PhabricatorAuthFactorConfig $config) {
return $this->getParameterName($config, 'mfa.response');
}
final protected function getChallengeResponseFromRequest(
PhabricatorAuthFactorConfig $config,
AphrontRequest $request) {
$name = $this->getChallengeResponseParameterName($config);
$value = $request->getStr($name);
$value = (string)$value;
$value = trim($value);
return $value;
}
final protected function hasCSRF(PhabricatorAuthFactorConfig $config) {
$engine = $config->getSessionEngine();
$request = $engine->getRequest();
if (!$request->isHTTPPost()) {
return false;
}
return $request->validateCSRF();
}
final protected function loadConfigurationsForProvider(
PhabricatorAuthFactorProvider $provider,
PhabricatorUser $user) {
return id(new PhabricatorAuthFactorConfigQuery())
->setViewer($user)
->withUserPHIDs(array($user->getPHID()))
->withFactorProviderPHIDs(array($provider->getPHID()))
->execute();
}
final protected function isAuthResult($object) {
return ($object instanceof PhabricatorAuthFactorResult);
}
}
diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
index 0054610c28..492544bef6 100644
--- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
@@ -1,468 +1,468 @@
<?php
final class PhabricatorMultiFactorSettingsPanel
extends PhabricatorSettingsPanel {
private $isEnrollment;
public function getPanelKey() {
return 'multifactor';
}
public function getPanelName() {
return pht('Multi-Factor Auth');
}
public function getPanelMenuIcon() {
return 'fa-lock';
}
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function isMultiFactorEnrollmentPanel() {
return true;
}
public function setIsEnrollment($is_enrollment) {
$this->isEnrollment = $is_enrollment;
return $this;
}
public function getIsEnrollment() {
return $this->isEnrollment;
}
public function processRequest(AphrontRequest $request) {
if ($request->getExists('new') || $request->getExists('providerPHID')) {
return $this->processNew($request);
}
if ($request->getExists('edit')) {
return $this->processEdit($request);
}
if ($request->getExists('delete')) {
return $this->processDelete($request);
}
$user = $this->getUser();
$viewer = $request->getUser();
$factors = id(new PhabricatorAuthFactorConfigQuery())
->setViewer($viewer)
->withUserPHIDs(array($user->getPHID()))
->execute();
$factors = msortv($factors, 'newSortVector');
$rows = array();
$rowc = array();
$highlight_id = $request->getInt('id');
foreach ($factors as $factor) {
$provider = $factor->getFactorProvider();
if ($factor->getID() == $highlight_id) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$status = $provider->newStatus();
$status_icon = $status->getFactorIcon();
$status_color = $status->getFactorColor();
$icon = id(new PHUIIconView())
->setIcon("{$status_icon} {$status_color}")
->setTooltip(pht('Provider: %s', $status->getName()));
$details = $provider->getConfigurationListDetails($factor, $viewer);
$rows[] = array(
$icon,
javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?edit='.$factor->getID()),
'sigil' => 'workflow',
),
$factor->getFactorName()),
$provider->getFactor()->getFactorShortName(),
$provider->getDisplayName(),
$details,
phabricator_datetime($factor->getDateCreated(), $viewer),
javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?delete='.$factor->getID()),
'sigil' => 'workflow',
'class' => 'small button button-grey',
),
pht('Remove')),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(
pht("You haven't added any authentication factors to your account yet."));
$table->setHeaders(
array(
null,
pht('Name'),
pht('Type'),
pht('Provider'),
pht('Details'),
pht('Created'),
null,
));
$table->setColumnClasses(
array(
null,
'wide pri',
null,
null,
null,
'right',
'action',
));
$table->setRowClasses($rowc);
$table->setDeviceVisibility(
array(
true,
true,
false,
false,
false,
false,
true,
));
$help_uri = PhabricatorEnv::getDoclink(
'User Guide: Multi-Factor Authentication');
$buttons = array();
// If we're enrolling a new account in MFA, provide a small visual hint
// that this is the button they want to click.
if ($this->getIsEnrollment()) {
$add_color = PHUIButtonView::BLUE;
} else {
$add_color = PHUIButtonView::GREY;
}
$can_add = (bool)$this->loadActiveMFAProviders();
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add Auth Factor'))
->setHref($this->getPanelURI('?new=true'))
->setWorkflow(true)
->setDisabled(!$can_add)
->setColor($add_color);
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-book')
->setText(pht('Help'))
->setHref($help_uri)
->setColor(PHUIButtonView::GREY);
return $this->newBox(pht('Authentication Factors'), $table, $buttons);
}
private function processNew(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$cancel_uri = $this->getPanelURI();
// Check that we have providers before we send the user through the MFA
// gate, so you don't authenticate and then immediately get roadblocked.
$providers = $this->loadActiveMFAProviders();
if (!$providers) {
return $this->newDialog()
->setTitle(pht('No MFA Providers'))
->appendParagraph(
pht(
'This install does not have any active MFA providers configured. '.
'At least one provider must be configured and active before you '.
'can add new MFA factors.'))
->addCancelButton($cancel_uri);
}
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$cancel_uri);
$selected_phid = $request->getStr('providerPHID');
if (empty($providers[$selected_phid])) {
$selected_provider = null;
} else {
$selected_provider = $providers[$selected_phid];
// Only let the user continue creating a factor for a given provider if
// they actually pass the provider's checks.
if (!$selected_provider->canCreateNewConfiguration($viewer)) {
$selected_provider = null;
}
}
if (!$selected_provider) {
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setBig(true)
->setFlush(true);
foreach ($providers as $provider_phid => $provider) {
$provider_uri = id(new PhutilURI($this->getPanelURI()))
->replaceQueryParam('providerPHID', $provider_phid);
$is_enabled = $provider->canCreateNewConfiguration($viewer);
$item = id(new PHUIObjectItemView())
->setHeader($provider->getDisplayName())
->setImageIcon($provider->newIconView())
->addAttribute($provider->getDisplayDescription());
if ($is_enabled) {
$item
->setHref($provider_uri)
->setClickable(true);
} else {
$item->setDisabled(true);
}
$create_description = $provider->getConfigurationCreateDescription(
$viewer);
if ($create_description) {
$item->appendChild($create_description);
}
$menu->addItem($item);
}
return $this->newDialog()
->setTitle(pht('Choose Factor Type'))
->appendChild($menu)
->addCancelButton($cancel_uri);
}
// NOTE: Beyond providing guidance, this step is also providing a CSRF gate
// on this endpoint, since prompting the user to respond to a challenge
// sometimes requires us to push a challenge to them as a side effect (for
// example, with SMS).
if (!$request->isFormPost() || !$request->getBool('mfa.start')) {
$enroll = $selected_provider->getEnrollMessage();
- if (!strlen($enroll)) {
+ if (!phutil_nonempty_string($enroll)) {
$enroll = $selected_provider->getEnrollDescription($viewer);
}
return $this->newDialog()
->addHiddenInput('providerPHID', $selected_provider->getPHID())
->addHiddenInput('mfa.start', 1)
->setTitle(pht('Add Authentication Factor'))
->appendChild(new PHUIRemarkupView($viewer, $enroll))
->addCancelButton($cancel_uri)
->addSubmitButton($selected_provider->getEnrollButtonText($viewer));
}
$form = id(new AphrontFormView())
->setViewer($viewer);
if ($request->getBool('mfa.enroll')) {
// Subject users to rate limiting so that it's difficult to add factors
// by pure brute force. This is normally not much of an attack, but push
// factor types may have side effects.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthNewFactorAction(),
1);
} else {
// Test the limit before showing the user a form, so we don't give them
// a form which can never possibly work because it will always hit rate
// limiting.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthNewFactorAction(),
0);
}
$config = $selected_provider->processAddFactorForm(
$form,
$request,
$user);
if ($config) {
// If the user added a factor, give them a rate limiting point back.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthNewFactorAction(),
-1);
$config->save();
// If we used a temporary token to handle synchronizing the factor,
// revoke it now.
$sync_token = $config->getMFASyncToken();
if ($sync_token) {
$sync_token->revokeToken();
}
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$user->getPHID(),
PhabricatorAddMultifactorUserLogType::LOGTYPE);
$log->save();
$user->updateMultiFactorEnrollment();
// Terminate other sessions so they must log in and survive the
// multi-factor auth check.
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?id='.$config->getID()));
}
return $this->newDialog()
->addHiddenInput('providerPHID', $selected_provider->getPHID())
->addHiddenInput('mfa.start', 1)
->addHiddenInput('mfa.enroll', 1)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle(pht('Add Authentication Factor'))
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Continue'))
->addCancelButton($cancel_uri);
}
private function processEdit(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$factor = id(new PhabricatorAuthFactorConfig())->loadOneWhere(
'id = %d AND userPHID = %s',
$request->getInt('edit'),
$user->getPHID());
if (!$factor) {
return new Aphront404Response();
}
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
$name = $request->getStr('name');
if (!strlen($name)) {
$e_name = pht('Required');
$errors[] = pht(
'Authentication factors must have a name to identify them.');
}
if (!$errors) {
$factor->setFactorName($name);
$factor->save();
$user->updateMultiFactorEnrollment();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?id='.$factor->getID()));
}
} else {
$name = $factor->getFactorName();
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($name)
->setError($e_name));
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('edit', $factor->getID())
->setTitle(pht('Edit Authentication Factor'))
->setErrors($errors)
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Save'))
->addCancelButton($this->getPanelURI());
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
private function processDelete(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$this->getPanelURI());
$factor = id(new PhabricatorAuthFactorConfig())->loadOneWhere(
'id = %d AND userPHID = %s',
$request->getInt('delete'),
$user->getPHID());
if (!$factor) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$factor->delete();
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$user->getPHID(),
PhabricatorRemoveMultifactorUserLogType::LOGTYPE);
$log->save();
$user->updateMultiFactorEnrollment();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI());
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('delete', $factor->getID())
->setTitle(pht('Delete Authentication Factor'))
->appendParagraph(
pht(
'Really remove the authentication factor %s from your account?',
phutil_tag('strong', array(), $factor->getFactorName())))
->addSubmitButton(pht('Remove Factor'))
->addCancelButton($this->getPanelURI());
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
private function loadActiveMFAProviders() {
$viewer = $this->getViewer();
$providers = id(new PhabricatorAuthFactorProviderQuery())
->setViewer($viewer)
->withStatuses(
array(
PhabricatorAuthFactorProviderStatus::STATUS_ACTIVE,
))
->execute();
$providers = mpull($providers, null, 'getPHID');
$providers = msortv($providers, 'newSortVector');
return $providers;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 12:47 (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124724
Default Alt Text
(31 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment