mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 16:52:41 +01:00
Allow users to access some settings at the "Add MFA" account setup roadblock
Summary: Depends on D20006. Ref T13222. Currently, the "MFA Is Required" gate doesn't let you do anything else, but you'll need to be able to access "Contact Numbers" if an install provides SMS MFA. Tweak this UI to give users limited access to settings, so they can set up contact numbers and change their language. (This is a little bit fiddly, and I'm doing it early on partly so it can get more testing as these changes move forward.) Test Plan: {F6146136} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D20008
This commit is contained in:
parent
d6d93dd658
commit
c4244aa177
7 changed files with 228 additions and 62 deletions
|
@ -72,8 +72,10 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
||||||
=> 'PhabricatorAuthRevokeTokenController',
|
=> 'PhabricatorAuthRevokeTokenController',
|
||||||
'session/downgrade/'
|
'session/downgrade/'
|
||||||
=> 'PhabricatorAuthDowngradeSessionController',
|
=> 'PhabricatorAuthDowngradeSessionController',
|
||||||
'multifactor/'
|
'enroll/' => array(
|
||||||
=> 'PhabricatorAuthNeedsMultiFactorController',
|
'(?:(?P<pageKey>[^/]+)/)?(?:(?P<formSaved>saved)/)?'
|
||||||
|
=> 'PhabricatorAuthNeedsMultiFactorController',
|
||||||
|
),
|
||||||
'sshkey/' => array(
|
'sshkey/' => array(
|
||||||
$this->getQueryRoutePattern('for/(?P<forPHID>[^/]+)/')
|
$this->getQueryRoutePattern('for/(?P<forPHID>[^/]+)/')
|
||||||
=> 'PhabricatorAuthSSHKeyListController',
|
=> 'PhabricatorAuthSSHKeyListController',
|
||||||
|
|
|
@ -30,80 +30,187 @@ final class PhabricatorAuthNeedsMultiFactorController
|
||||||
return new Aphront400Response();
|
return new Aphront400Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$panel = id(new PhabricatorMultiFactorSettingsPanel())
|
$panels = $this->loadPanels();
|
||||||
->setUser($viewer)
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setOverrideURI($this->getApplicationURI('/multifactor/'))
|
|
||||||
->processRequest($request);
|
|
||||||
|
|
||||||
if ($panel instanceof AphrontResponse) {
|
$multifactor_key = id(new PhabricatorMultiFactorSettingsPanel())
|
||||||
return $panel;
|
->getPanelKey();
|
||||||
|
|
||||||
|
$panel_key = $request->getURIData('pageKey');
|
||||||
|
if (!strlen($panel_key)) {
|
||||||
|
$panel_key = $multifactor_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
if (!isset($panels[$panel_key])) {
|
||||||
$crumbs->addTextCrumb(pht('Add Multi-Factor Auth'));
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$nav = $this->newNavigation();
|
||||||
|
$nav->selectFilter($panel_key);
|
||||||
|
|
||||||
|
$panel = $panels[$panel_key];
|
||||||
|
|
||||||
$viewer->updateMultiFactorEnrollment();
|
$viewer->updateMultiFactorEnrollment();
|
||||||
|
|
||||||
if (!$viewer->getIsEnrolledInMultiFactor()) {
|
if ($panel_key === $multifactor_key) {
|
||||||
$help = id(new PHUIInfoView())
|
$header_text = pht('Add Multi-Factor Auth');
|
||||||
->setTitle(pht('Add Multi-Factor Authentication To Your Account'))
|
$help = $this->newGuidance();
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
$panel->setIsEnrollment(true);
|
||||||
->setErrors(
|
|
||||||
array(
|
|
||||||
pht(
|
|
||||||
'Before you can use Phabricator, you need to add multi-factor '.
|
|
||||||
'authentication to your account.'),
|
|
||||||
pht(
|
|
||||||
'Multi-factor authentication helps secure your account by '.
|
|
||||||
'making it more difficult for attackers to gain access or '.
|
|
||||||
'take sensitive actions.'),
|
|
||||||
pht(
|
|
||||||
'To learn more about multi-factor authentication, click the '.
|
|
||||||
'%s button below.',
|
|
||||||
phutil_tag('strong', array(), pht('Help'))),
|
|
||||||
pht(
|
|
||||||
'To add an authentication factor, click the %s button below.',
|
|
||||||
phutil_tag('strong', array(), pht('Add Authentication Factor'))),
|
|
||||||
pht(
|
|
||||||
'To continue, add at least one authentication factor to your '.
|
|
||||||
'account.'),
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
$help = id(new PHUIInfoView())
|
$header_text = $panel->getPanelName();
|
||||||
->setTitle(pht('Multi-Factor Authentication Configured'))
|
$help = null;
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
|
||||||
->setErrors(
|
|
||||||
array(
|
|
||||||
pht(
|
|
||||||
'You have successfully configured multi-factor authentication '.
|
|
||||||
'for your account.'),
|
|
||||||
pht(
|
|
||||||
'You can make adjustments from the Settings panel later.'),
|
|
||||||
pht(
|
|
||||||
'When you are ready, %s.',
|
|
||||||
phutil_tag(
|
|
||||||
'strong',
|
|
||||||
array(),
|
|
||||||
phutil_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => '/',
|
|
||||||
),
|
|
||||||
pht('continue to Phabricator')))),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = array(
|
$response = $panel
|
||||||
$help,
|
->setController($this)
|
||||||
$panel,
|
->setNavigation($nav)
|
||||||
);
|
->processRequest($request);
|
||||||
|
|
||||||
|
if (($response instanceof AphrontResponse) ||
|
||||||
|
($response instanceof AphrontResponseProducerInterface)) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs()
|
||||||
|
->addTextCrumb(pht('Add Multi-Factor Auth'))
|
||||||
|
->setBorder(true);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader($header_text);
|
||||||
|
|
||||||
|
$view = id(new PHUITwoColumnView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setFooter(
|
||||||
|
array(
|
||||||
|
$help,
|
||||||
|
$response,
|
||||||
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle(pht('Add Multi-Factor Authentication'))
|
->setTitle(pht('Add Multi-Factor Authentication'))
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
|
->setNavigation($nav)
|
||||||
->appendChild($view);
|
->appendChild($view);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadPanels() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$preferences = PhabricatorUserPreferences::loadUserPreferences($viewer);
|
||||||
|
|
||||||
|
$panels = PhabricatorSettingsPanel::getAllDisplayPanels();
|
||||||
|
$base_uri = $this->newEnrollBaseURI();
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach ($panels as $key => $panel) {
|
||||||
|
$panel
|
||||||
|
->setPreferences($preferences)
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setUser($viewer)
|
||||||
|
->setOverrideURI(urisprintf('%s%s/', $base_uri, $key));
|
||||||
|
|
||||||
|
if (!$panel->isEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$panel->isUserPanel()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$panel->isMultiFactorEnrollmentPanel()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result[$key])) {
|
||||||
|
throw new Exception(pht(
|
||||||
|
"Two settings panels share the same panel key ('%s'): %s, %s.",
|
||||||
|
$key,
|
||||||
|
get_class($panel),
|
||||||
|
get_class($result[$key])));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$key] = $panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function newNavigation() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$enroll_uri = $this->newEnrollBaseURI();
|
||||||
|
|
||||||
|
$nav = id(new AphrontSideNavFilterView())
|
||||||
|
->setBaseURI(new PhutilURI($enroll_uri));
|
||||||
|
|
||||||
|
$multifactor_key = id(new PhabricatorMultiFactorSettingsPanel())
|
||||||
|
->getPanelKey();
|
||||||
|
|
||||||
|
$nav->addFilter(
|
||||||
|
$multifactor_key,
|
||||||
|
pht('Enroll in MFA'),
|
||||||
|
null,
|
||||||
|
'fa-exclamation-triangle blue');
|
||||||
|
|
||||||
|
$panels = $this->loadPanels();
|
||||||
|
|
||||||
|
if ($panels) {
|
||||||
|
$nav->addLabel(pht('Settings'));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($panels as $panel_key => $panel) {
|
||||||
|
if ($panel_key === $multifactor_key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nav->addFilter(
|
||||||
|
$panel->getPanelKey(),
|
||||||
|
$panel->getPanelName(),
|
||||||
|
null,
|
||||||
|
$panel->getPanelMenuIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newEnrollBaseURI() {
|
||||||
|
return $this->getApplicationURI('enroll/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newGuidance() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
if ($viewer->getIsEnrolledInMultiFactor()) {
|
||||||
|
$guidance = pht(
|
||||||
|
'{icon check, color="green"} **Setup Complete!**'.
|
||||||
|
"\n\n".
|
||||||
|
'You have successfully configured multi-factor authentication '.
|
||||||
|
'for your account.'.
|
||||||
|
"\n\n".
|
||||||
|
'You can make adjustments from the [[ /settings/ | Settings ]] panel '.
|
||||||
|
'later.');
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Multi-Factor Authentication Setup Complete'))
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FULL)
|
||||||
|
->appendChild(new PHUIRemarkupView($viewer, $guidance))
|
||||||
|
->addCancelButton('/', pht('Continue'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$messages = array();
|
||||||
|
|
||||||
|
$messages[] = pht(
|
||||||
|
'Before you can use Phabricator, you need to add multi-factor '.
|
||||||
|
'authentication to your account. Multi-factor authentication helps '.
|
||||||
|
'secure your account by making it more difficult for attackers to '.
|
||||||
|
'gain access or take sensitive actions.');
|
||||||
|
|
||||||
|
$view = id(new PHUIInfoView())
|
||||||
|
->setTitle(pht('Add Multi-Factor Authentication To Your Account'))
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||||
|
->setErrors($messages);
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,21 @@
|
||||||
abstract class PhabricatorAuthContactNumberController
|
abstract class PhabricatorAuthContactNumberController
|
||||||
extends PhabricatorAuthController {
|
extends PhabricatorAuthController {
|
||||||
|
|
||||||
|
// Users may need to access these controllers to enroll in SMS MFA during
|
||||||
|
// account setup.
|
||||||
|
|
||||||
|
public function shouldRequireMultiFactorEnrollment() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldRequireEnabledUser() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldRequireEmailVerification() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildApplicationCrumbs() {
|
protected function buildApplicationCrumbs() {
|
||||||
$crumbs = parent::buildApplicationCrumbs();
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ final class PhabricatorContactNumbersSettingsPanel
|
||||||
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
|
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isMultiFactorEnrollmentPanel() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function processRequest(AphrontRequest $request) {
|
public function processRequest(AphrontRequest $request) {
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$viewer = $request->getUser();
|
$viewer = $request->getUser();
|
||||||
|
|
|
@ -25,4 +25,8 @@ final class PhabricatorLanguageSettingsPanel
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isMultiFactorEnrollmentPanel() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
final class PhabricatorMultiFactorSettingsPanel
|
final class PhabricatorMultiFactorSettingsPanel
|
||||||
extends PhabricatorSettingsPanel {
|
extends PhabricatorSettingsPanel {
|
||||||
|
|
||||||
|
private $isEnrollment;
|
||||||
|
|
||||||
public function getPanelKey() {
|
public function getPanelKey() {
|
||||||
return 'multifactor';
|
return 'multifactor';
|
||||||
}
|
}
|
||||||
|
@ -19,6 +21,19 @@ final class PhabricatorMultiFactorSettingsPanel
|
||||||
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
|
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) {
|
public function processRequest(AphrontRequest $request) {
|
||||||
if ($request->getExists('new') || $request->getExists('providerPHID')) {
|
if ($request->getExists('new') || $request->getExists('providerPHID')) {
|
||||||
return $this->processNew($request);
|
return $this->processNew($request);
|
||||||
|
@ -106,13 +121,21 @@ final class PhabricatorMultiFactorSettingsPanel
|
||||||
|
|
||||||
$buttons = array();
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
$buttons[] = id(new PHUIButtonView())
|
$buttons[] = id(new PHUIButtonView())
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
->setIcon('fa-plus')
|
->setIcon('fa-plus')
|
||||||
->setText(pht('Add Auth Factor'))
|
->setText(pht('Add Auth Factor'))
|
||||||
->setHref($this->getPanelURI('?new=true'))
|
->setHref($this->getPanelURI('?new=true'))
|
||||||
->setWorkflow(true)
|
->setWorkflow(true)
|
||||||
->setColor(PHUIButtonView::GREY);
|
->setColor($add_color);
|
||||||
|
|
||||||
$buttons[] = id(new PHUIButtonView())
|
$buttons[] = id(new PHUIButtonView())
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
|
|
|
@ -198,6 +198,17 @@ abstract class PhabricatorSettingsPanel extends Phobject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if this panel should be available when enrolling in MFA on
|
||||||
|
* a new account with MFA requiredd.
|
||||||
|
*
|
||||||
|
* @return bool True to allow configuration during MFA enrollment.
|
||||||
|
* @task config
|
||||||
|
*/
|
||||||
|
public function isMultiFactorEnrollmentPanel() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Panel Implementation )----------------------------------------------- */
|
/* -( Panel Implementation )----------------------------------------------- */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue