Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894106
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
37 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/auth/application/PhabricatorAuthApplication.php b/src/applications/auth/application/PhabricatorAuthApplication.php
index 20547d8ca3..4c0003e806 100644
--- a/src/applications/auth/application/PhabricatorAuthApplication.php
+++ b/src/applications/auth/application/PhabricatorAuthApplication.php
@@ -1,144 +1,146 @@
<?php
final class PhabricatorAuthApplication extends PhabricatorApplication {
public function canUninstall() {
return false;
}
public function getBaseURI() {
return '/auth/';
}
public function getIcon() {
return 'fa-key';
}
public function isPinnedByDefault(PhabricatorUser $viewer) {
return $viewer->getIsAdmin();
}
public function getName() {
return pht('Auth');
}
public function getShortDescription() {
return pht('Login/Registration');
}
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
// NOTE: Although reasonable help exists for this in "Configuring Accounts
// and Registration", specifying help items here means we get the menu
// item in all the login/link interfaces, which is confusing and not
// helpful.
// TODO: Special case this, or split the auth and auth administration
// applications?
return array();
}
public function getApplicationGroup() {
return self::GROUP_ADMIN;
}
public function getRoutes() {
return array(
'/auth/' => array(
'' => 'PhabricatorAuthListController',
'config/' => array(
'new/' => 'PhabricatorAuthNewController',
'new/(?P<className>[^/]+)/' => 'PhabricatorAuthEditController',
'edit/(?P<id>\d+)/' => 'PhabricatorAuthEditController',
'(?P<action>enable|disable)/(?P<id>\d+)/'
=> 'PhabricatorAuthDisableController',
),
'login/(?P<pkey>[^/]+)/(?:(?P<extra>[^/]+)/)?'
=> 'PhabricatorAuthLoginController',
'(?P<loggedout>loggedout)/' => 'PhabricatorAuthStartController',
'invite/(?P<code>[^/]+)/' => 'PhabricatorAuthInviteController',
'register/(?:(?P<akey>[^/]+)/)?' => 'PhabricatorAuthRegisterController',
'start/' => 'PhabricatorAuthStartController',
'validate/' => 'PhabricatorAuthValidateController',
'finish/' => 'PhabricatorAuthFinishController',
'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController',
'(?P<action>link|refresh)/(?P<pkey>[^/]+)/'
=> 'PhabricatorAuthLinkController',
'confirmlink/(?P<akey>[^/]+)/'
=> 'PhabricatorAuthConfirmLinkController',
'session/terminate/(?P<id>[^/]+)/'
=> 'PhabricatorAuthTerminateSessionController',
'token/revoke/(?P<id>[^/]+)/'
=> 'PhabricatorAuthRevokeTokenController',
'session/downgrade/'
=> 'PhabricatorAuthDowngradeSessionController',
- 'multifactor/'
- => 'PhabricatorAuthNeedsMultiFactorController',
+ 'enroll/' => array(
+ '(?:(?P<pageKey>[^/]+)/)?(?:(?P<formSaved>saved)/)?'
+ => 'PhabricatorAuthNeedsMultiFactorController',
+ ),
'sshkey/' => array(
$this->getQueryRoutePattern('for/(?P<forPHID>[^/]+)/')
=> 'PhabricatorAuthSSHKeyListController',
'generate/' => 'PhabricatorAuthSSHKeyGenerateController',
'upload/' => 'PhabricatorAuthSSHKeyEditController',
'edit/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyEditController',
'revoke/(?P<id>\d+)/'
=> 'PhabricatorAuthSSHKeyRevokeController',
'view/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyViewController',
),
'password/' => 'PhabricatorAuthSetPasswordController',
'mfa/' => array(
$this->getQueryRoutePattern() =>
'PhabricatorAuthFactorProviderListController',
$this->getEditRoutePattern('edit/') =>
'PhabricatorAuthFactorProviderEditController',
'(?P<id>[1-9]\d*)/' =>
'PhabricatorAuthFactorProviderViewController',
),
'message/' => array(
$this->getQueryRoutePattern() =>
'PhabricatorAuthMessageListController',
$this->getEditRoutePattern('edit/') =>
'PhabricatorAuthMessageEditController',
'(?P<id>[1-9]\d*)/' =>
'PhabricatorAuthMessageViewController',
),
'contact/' => array(
$this->getEditRoutePattern('edit/') =>
'PhabricatorAuthContactNumberEditController',
'(?P<id>[1-9]\d*)/' =>
'PhabricatorAuthContactNumberViewController',
),
),
'/oauth/(?P<provider>\w+)/login/'
=> 'PhabricatorAuthOldOAuthRedirectController',
'/login/' => array(
'' => 'PhabricatorAuthStartController',
'email/' => 'PhabricatorEmailLoginController',
'once/'.
'(?P<type>[^/]+)/'.
'(?P<id>\d+)/'.
'(?P<key>[^/]+)/'.
'(?:(?P<emailID>\d+)/)?' => 'PhabricatorAuthOneTimeLoginController',
'refresh/' => 'PhabricatorRefreshCSRFController',
'mustverify/' => 'PhabricatorMustVerifyEmailController',
),
'/emailverify/(?P<code>[^/]+)/'
=> 'PhabricatorEmailVerificationController',
'/logout/' => 'PhabricatorLogoutController',
);
}
protected function getCustomCapabilities() {
return array(
AuthManageProvidersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
index 27e03485ca..92328b2000 100644
--- a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
+++ b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
@@ -1,109 +1,216 @@
<?php
final class PhabricatorAuthNeedsMultiFactorController
extends PhabricatorAuthController {
public function shouldRequireMultiFactorEnrollment() {
// Users need access to this controller in order to enroll in multi-factor
// auth.
return false;
}
public function shouldRequireEnabledUser() {
// Users who haven't been approved yet are allowed to enroll in MFA. We'll
// kick disabled users out later.
return false;
}
public function shouldRequireEmailVerification() {
// Users who haven't verified their email addresses yet can still enroll
// in MFA.
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
if ($viewer->getIsDisabled()) {
// We allowed unapproved and disabled users to hit this controller, but
// want to kick out disabled users now.
return new Aphront400Response();
}
- $panel = id(new PhabricatorMultiFactorSettingsPanel())
- ->setUser($viewer)
- ->setViewer($viewer)
- ->setOverrideURI($this->getApplicationURI('/multifactor/'))
- ->processRequest($request);
+ $panels = $this->loadPanels();
+
+ $multifactor_key = id(new PhabricatorMultiFactorSettingsPanel())
+ ->getPanelKey();
+
+ $panel_key = $request->getURIData('pageKey');
+ if (!strlen($panel_key)) {
+ $panel_key = $multifactor_key;
+ }
- if ($panel instanceof AphrontResponse) {
- return $panel;
+ if (!isset($panels[$panel_key])) {
+ return new Aphront404Response();
}
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Add Multi-Factor Auth'));
+ $nav = $this->newNavigation();
+ $nav->selectFilter($panel_key);
+
+ $panel = $panels[$panel_key];
$viewer->updateMultiFactorEnrollment();
- if (!$viewer->getIsEnrolledInMultiFactor()) {
- $help = id(new PHUIInfoView())
- ->setTitle(pht('Add Multi-Factor Authentication To Your Account'))
- ->setSeverity(PHUIInfoView::SEVERITY_WARNING)
- ->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.'),
- ));
+ if ($panel_key === $multifactor_key) {
+ $header_text = pht('Add Multi-Factor Auth');
+ $help = $this->newGuidance();
+ $panel->setIsEnrollment(true);
} else {
- $help = id(new PHUIInfoView())
- ->setTitle(pht('Multi-Factor Authentication Configured'))
- ->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')))),
- ));
+ $header_text = $panel->getPanelName();
+ $help = null;
}
- $view = array(
- $help,
- $panel,
- );
+ $response = $panel
+ ->setController($this)
+ ->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()
->setTitle(pht('Add Multi-Factor Authentication'))
->setCrumbs($crumbs)
+ ->setNavigation($nav)
->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;
+ }
+
}
diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php
index a713f48a3b..3ae923fbbc 100644
--- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php
+++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberController.php
@@ -1,16 +1,31 @@
<?php
abstract class PhabricatorAuthContactNumberController
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() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Contact Numbers'),
pht('/settings/panel/contact/'));
return $crumbs;
}
}
diff --git a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
index 7834bff593..3e4ed8880e 100644
--- a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
@@ -1,73 +1,77 @@
<?php
final class PhabricatorContactNumbersSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'contact';
}
public function getPanelName() {
return pht('Contact Numbers');
}
public function getPanelMenuIcon() {
return 'fa-mobile';
}
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
+ public function isMultiFactorEnrollmentPanel() {
+ return true;
+ }
+
public function processRequest(AphrontRequest $request) {
$user = $this->getUser();
$viewer = $request->getUser();
$numbers = id(new PhabricatorAuthContactNumberQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->execute();
$rows = array();
foreach ($numbers as $number) {
$rows[] = array(
$number->newIconView(),
phutil_tag(
'a',
array(
'href' => $number->getURI(),
),
$number->getDisplayName()),
phabricator_datetime($number->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(
pht("You haven't added any contact numbers to your account."))
->setHeaders(
array(
null,
pht('Number'),
pht('Created'),
))
->setColumnClasses(
array(
null,
'wide pri',
'right',
));
$buttons = array();
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add Contact Number'))
->setHref('/auth/contact/edit/')
->setColor(PHUIButtonView::GREY);
return $this->newBox(pht('Contact Numbers'), $table, $buttons);
}
}
diff --git a/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php b/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
index 65a0be4e79..39bd5deac9 100644
--- a/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
@@ -1,28 +1,32 @@
<?php
final class PhabricatorLanguageSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'language';
public function getPanelName() {
return pht('Language');
}
public function getPanelMenuIcon() {
return 'fa-globe';
}
public function getPanelGroupKey() {
return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY;
}
public function isManagementPanel() {
return true;
}
public function isTemplatePanel() {
return true;
}
+ public function isMultiFactorEnrollmentPanel() {
+ return true;
+ }
+
}
diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
index 93a95254a9..d2e9c34247 100644
--- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
@@ -1,333 +1,356 @@
<?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()))
->setOrderVector(array('-id'))
->execute();
$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;
}
$rows[] = array(
javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?edit='.$factor->getID()),
'sigil' => 'workflow',
),
$factor->getFactorName()),
$provider->getDisplayName(),
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(
pht('Name'),
pht('Type'),
pht('Created'),
'',
));
$table->setColumnClasses(
array(
'wide pri',
'',
'right',
'action',
));
$table->setRowClasses($rowc);
$table->setDeviceVisibility(
array(
true,
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;
+ }
+
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add Auth Factor'))
->setHref($this->getPanelURI('?new=true'))
->setWorkflow(true)
- ->setColor(PHUIButtonView::GREY);
+ ->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 = id(new PhabricatorAuthFactorProviderQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorAuthFactorProvider::STATUS_ACTIVE))
->execute();
if (!$providers) {
return $this->newDialog()
->setTitle(pht('No MFA Providers'))
->appendParagraph(
pht(
'There are no active MFA providers. At least one active provider '.
'must be available to add new MFA factors.'))
->addCancelButton($cancel_uri);
}
$providers = mpull($providers, null, 'getPHID');
$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];
}
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()))
->setQueryParam('providerPHID', $provider_phid);
$item = id(new PHUIObjectItemView())
->setHeader($provider->getDisplayName())
->setHref($provider_uri)
->setClickable(true)
->setImageIcon($provider->newIconView())
->addAttribute($provider->getDisplayDescription());
$menu->addItem($item);
}
return $this->newDialog()
->setTitle(pht('Choose Factor Type'))
->appendChild($menu)
->addCancelButton($cancel_uri);
}
$form = id(new AphrontFormView())
->setViewer($viewer);
$config = $selected_provider->processAddFactorForm(
$form,
$request,
$user);
if ($config) {
$config->save();
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$user->getPHID(),
PhabricatorUserLog::ACTION_MULTI_ADD);
$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())
->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(),
PhabricatorUserLog::ACTION_MULTI_REMOVE);
$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);
}
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php
index 8250418812..e2efd92093 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php
@@ -1,311 +1,322 @@
<?php
/**
* Defines a settings panel. Settings panels appear in the Settings application,
* and behave like lightweight controllers -- generally, they render some sort
* of form with options in it, and then update preferences when the user
* submits the form. By extending this class, you can add new settings
* panels.
*
* @task config Panel Configuration
* @task panel Panel Implementation
* @task internal Internals
*/
abstract class PhabricatorSettingsPanel extends Phobject {
private $user;
private $viewer;
private $controller;
private $navigation;
private $overrideURI;
private $preferences;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->user;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setOverrideURI($override_uri) {
$this->overrideURI = $override_uri;
return $this;
}
final public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
}
final public function getController() {
return $this->controller;
}
final public function setNavigation(AphrontSideNavFilterView $navigation) {
$this->navigation = $navigation;
return $this;
}
final public function getNavigation() {
return $this->navigation;
}
public function setPreferences(PhabricatorUserPreferences $preferences) {
$this->preferences = $preferences;
return $this;
}
public function getPreferences() {
return $this->preferences;
}
final public static function getAllPanels() {
$panels = id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getPanelKey')
->execute();
return msortv($panels, 'getPanelOrderVector');
}
final public static function getAllDisplayPanels() {
$panels = array();
$groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels();
foreach ($groups as $group) {
foreach ($group->getPanels() as $key => $panel) {
$panels[$key] = $panel;
}
}
return $panels;
}
final public function getPanelGroup() {
$group_key = $this->getPanelGroupKey();
$groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels();
$group = idx($groups, $group_key);
if (!$group) {
throw new Exception(
pht(
'No settings panel group with key "%s" exists!',
$group_key));
}
return $group;
}
/* -( Panel Configuration )------------------------------------------------ */
/**
* Return a unique string used in the URI to identify this panel, like
* "example".
*
* @return string Unique panel identifier (used in URIs).
* @task config
*/
public function getPanelKey() {
return $this->getPhobjectClassConstant('PANELKEY');
}
/**
* Return a human-readable description of the panel's contents, like
* "Example Settings".
*
* @return string Human-readable panel name.
* @task config
*/
abstract public function getPanelName();
/**
* Return an icon for the panel in the menu.
*
* @return string Icon identifier.
* @task config
*/
public function getPanelMenuIcon() {
return 'fa-wrench';
}
/**
* Return a panel group key constant for this panel.
*
* @return const Panel group key.
* @task config
*/
abstract public function getPanelGroupKey();
/**
* Return false to prevent this panel from being displayed or used. You can
* do, e.g., configuration checks here, to determine if the feature your
* panel controls is unavailable in this install. By default, all panels are
* enabled.
*
* @return bool True if the panel should be shown.
* @task config
*/
public function isEnabled() {
return true;
}
/**
* Return true if this panel is available to users while editing their own
* settings.
*
* @return bool True to enable management on behalf of a user.
* @task config
*/
public function isUserPanel() {
return true;
}
/**
* Return true if this panel is available to administrators while managing
* bot and mailing list accounts.
*
* @return bool True to enable management on behalf of accounts.
* @task config
*/
public function isManagementPanel() {
return false;
}
/**
* Return true if this panel is available while editing settings templates.
*
* @return bool True to allow editing in templates.
* @task config
*/
public function isTemplatePanel() {
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 )----------------------------------------------- */
/**
* Process a user request for this settings panel. Implement this method like
* a lightweight controller. If you return an @{class:AphrontResponse}, the
* response will be used in whole. If you return anything else, it will be
* treated as a view and composed into a normal settings page.
*
* Generally, render your settings panel by returning a form, then return
* a redirect when the user saves settings.
*
* @param AphrontRequest Incoming request.
* @return wild Response to request, either as an
* @{class:AphrontResponse} or something which can
* be composed into a @{class:AphrontView}.
* @task panel
*/
abstract public function processRequest(AphrontRequest $request);
/**
* Get the URI for this panel.
*
* @param string? Optional path to append.
* @return string Relative URI for the panel.
* @task panel
*/
final public function getPanelURI($path = '') {
$path = ltrim($path, '/');
if ($this->overrideURI) {
return rtrim($this->overrideURI, '/').'/'.$path;
}
$key = $this->getPanelKey();
$key = phutil_escape_uri($key);
$user = $this->getUser();
if ($user) {
if ($user->isLoggedIn()) {
$username = $user->getUsername();
return "/settings/user/{$username}/page/{$key}/{$path}";
} else {
// For logged-out users, we can't put their username in the URI. This
// page will prompt them to login, then redirect them to the correct
// location.
return "/settings/panel/{$key}/";
}
} else {
$builtin = $this->getPreferences()->getBuiltinKey();
return "/settings/builtin/{$builtin}/page/{$key}/{$path}";
}
}
/* -( Internals )---------------------------------------------------------- */
/**
* Generates a key to sort the list of panels.
*
* @return string Sortable key.
* @task internal
*/
final public function getPanelOrderVector() {
return id(new PhutilSortVector())
->addString($this->getPanelName());
}
protected function newDialog() {
return $this->getController()->newDialog();
}
protected function writeSetting(
PhabricatorUserPreferences $preferences,
$key,
$value) {
$viewer = $this->getViewer();
$request = $this->getController()->getRequest();
$editor = id(new PhabricatorUserPreferencesEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$xactions = array();
$xactions[] = $preferences->newTransaction($key, $value);
$editor->applyTransactions($preferences, $xactions);
}
public function newBox($title, $content, $actions = array()) {
$header = id(new PHUIHeaderView())
->setHeader($title);
foreach ($actions as $action) {
$header->addActionLink($action);
}
$view = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($content)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG);
return $view;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:24 (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127936
Default Alt Text
(37 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment