Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php b/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php
index 799a8e691e..664a97885f 100644
--- a/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php
+++ b/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php
@@ -1,81 +1,78 @@
<?php
final class PhabricatorAuthConfirmLinkController
extends PhabricatorAuthController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$accountkey = $request->getURIData('akey');
$result = $this->loadAccountForRegistrationOrLinking($accountkey);
list($account, $provider, $response) = $result;
if ($response) {
return $response;
}
if (!$provider->shouldAllowAccountLink()) {
return $this->renderError(pht('This account is not linkable.'));
}
$panel_uri = '/settings/panel/external/';
if ($request->isFormPost()) {
$account->setUserPHID($viewer->getPHID());
$account->save();
$this->clearRegistrationCookies();
// TODO: Send the user email about the new account link.
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
// TODO: Provide more information about the external account. Clicking
// through this form blindly is dangerous.
// TODO: If the user has password authentication, require them to retype
// their password here.
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Confirm %s Account Link', $provider->getProviderName()))
->addCancelButton($panel_uri)
->addSubmitButton(pht('Confirm Account Link'));
$form = id(new PHUIFormLayoutView())
->setFullWidth(true)
->appendChild(
phutil_tag(
'div',
array(
'class' => 'aphront-form-instructions',
),
pht(
'Confirm the link with this %s account. This account will be '.
'able to log in to your Phabricator account.',
$provider->getProviderName())))
->appendChild(
id(new PhabricatorAuthAccountView())
->setUser($viewer)
->setExternalAccount($account)
->setAuthProvider($provider));
$dialog->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Confirm Link'), $panel_uri);
$crumbs->addTextCrumb($provider->getProviderName());
+ $crumbs->setBorder(true);
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $dialog,
- ),
- array(
- 'title' => pht('Confirm External Account Link'),
- ));
+ return $this->newPage()
+ ->setTitle(pht('Confirm External Account Link'))
+ ->setCrumbs($crumbs)
+ ->appendChild($dialog);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php
index 76161e6c77..ca4b89f571 100644
--- a/src/applications/auth/controller/PhabricatorAuthController.php
+++ b/src/applications/auth/controller/PhabricatorAuthController.php
@@ -1,293 +1,291 @@
<?php
abstract class PhabricatorAuthController extends PhabricatorController {
protected function renderErrorPage($title, array $messages) {
$view = new PHUIInfoView();
$view->setTitle($title);
$view->setErrors($messages);
- return $this->buildApplicationPage(
- $view,
- array(
- 'title' => $title,
- ));
+ return $this->newPage()
+ ->setTitle($title)
+ ->appendChild($view);
}
/**
* Returns true if this install is newly setup (i.e., there are no user
* accounts yet). In this case, we enter a special mode to permit creation
* of the first account form the web UI.
*/
protected function isFirstTimeSetup() {
// If there are any auth providers, this isn't first time setup, even if
// we don't have accounts.
if (PhabricatorAuthProvider::getAllEnabledProviders()) {
return false;
}
// Otherwise, check if there are any user accounts. If not, we're in first
// time setup.
$any_users = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->setLimit(1)
->execute();
return !$any_users;
}
/**
* Log a user into a web session and return an @{class:AphrontResponse} which
* corresponds to continuing the login process.
*
* Normally, this is a redirect to the validation controller which makes sure
* the user's cookies are set. However, event listeners can intercept this
* event and do something else if they prefer.
*
* @param PhabricatorUser User to log the viewer in as.
* @return AphrontResponse Response which continues the login process.
*/
protected function loginUser(PhabricatorUser $user) {
$response = $this->buildLoginValidateResponse($user);
$session_type = PhabricatorAuthSession::TYPE_WEB;
$event_type = PhabricatorEventType::TYPE_AUTH_WILLLOGINUSER;
$event_data = array(
'user' => $user,
'type' => $session_type,
'response' => $response,
'shouldLogin' => true,
);
$event = id(new PhabricatorEvent($event_type, $event_data))
->setUser($user);
PhutilEventEngine::dispatchEvent($event);
$should_login = $event->getValue('shouldLogin');
if ($should_login) {
$session_key = id(new PhabricatorAuthSessionEngine())
->establishSession($session_type, $user->getPHID(), $partial = true);
// NOTE: We allow disabled users to login and roadblock them later, so
// there's no check for users being disabled here.
$request = $this->getRequest();
$request->setCookie(
PhabricatorCookies::COOKIE_USERNAME,
$user->getUsername());
$request->setCookie(
PhabricatorCookies::COOKIE_SESSION,
$session_key);
$this->clearRegistrationCookies();
}
return $event->getValue('response');
}
protected function clearRegistrationCookies() {
$request = $this->getRequest();
// Clear the registration key.
$request->clearCookie(PhabricatorCookies::COOKIE_REGISTRATION);
// Clear the client ID / OAuth state key.
$request->clearCookie(PhabricatorCookies::COOKIE_CLIENTID);
// Clear the invite cookie.
$request->clearCookie(PhabricatorCookies::COOKIE_INVITE);
}
private function buildLoginValidateResponse(PhabricatorUser $user) {
$validate_uri = new PhutilURI($this->getApplicationURI('validate/'));
$validate_uri->setQueryParam('expect', $user->getUsername());
return id(new AphrontRedirectResponse())->setURI((string)$validate_uri);
}
protected function renderError($message) {
return $this->renderErrorPage(
pht('Authentication Error'),
array(
$message,
));
}
protected function loadAccountForRegistrationOrLinking($account_key) {
$request = $this->getRequest();
$viewer = $request->getUser();
$account = null;
$provider = null;
$response = null;
if (!$account_key) {
$response = $this->renderError(
pht('Request did not include account key.'));
return array($account, $provider, $response);
}
// NOTE: We're using the omnipotent user because the actual user may not
// be logged in yet, and because we want to tailor an error message to
// distinguish between "not usable" and "does not exist". We do explicit
// checks later on to make sure this account is valid for the intended
// operation. This requires edit permission for completeness and consistency
// but it won't actually be meaningfully checked because we're using the
// ominpotent user.
$account = id(new PhabricatorExternalAccountQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withAccountSecrets(array($account_key))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
$response = $this->renderError(pht('No valid linkable account.'));
return array($account, $provider, $response);
}
if ($account->getUserPHID()) {
if ($account->getUserPHID() != $viewer->getPHID()) {
$response = $this->renderError(
pht(
'The account you are attempting to register or link is already '.
'linked to another user.'));
} else {
$response = $this->renderError(
pht(
'The account you are attempting to link is already linked '.
'to your account.'));
}
return array($account, $provider, $response);
}
$registration_key = $request->getCookie(
PhabricatorCookies::COOKIE_REGISTRATION);
// NOTE: This registration key check is not strictly necessary, because
// we're only creating new accounts, not linking existing accounts. It
// might be more hassle than it is worth, especially for email.
//
// The attack this prevents is getting to the registration screen, then
// copy/pasting the URL and getting someone else to click it and complete
// the process. They end up with an account bound to credentials you
// control. This doesn't really let you do anything meaningful, though,
// since you could have simply completed the process yourself.
if (!$registration_key) {
$response = $this->renderError(
pht(
'Your browser did not submit a registration key with the request. '.
'You must use the same browser to begin and complete registration. '.
'Check that cookies are enabled and try again.'));
return array($account, $provider, $response);
}
// We store the digest of the key rather than the key itself to prevent a
// theoretical attacker with read-only access to the database from
// hijacking registration sessions.
$actual = $account->getProperty('registrationKey');
$expect = PhabricatorHash::digest($registration_key);
if (!phutil_hashes_are_identical($actual, $expect)) {
$response = $this->renderError(
pht(
'Your browser submitted a different registration key than the one '.
'associated with this account. You may need to clear your cookies.'));
return array($account, $provider, $response);
}
$other_account = id(new PhabricatorExternalAccount())->loadAllWhere(
'accountType = %s AND accountDomain = %s AND accountID = %s
AND id != %d',
$account->getAccountType(),
$account->getAccountDomain(),
$account->getAccountID(),
$account->getID());
if ($other_account) {
$response = $this->renderError(
pht(
'The account you are attempting to register with already belongs '.
'to another user.'));
return array($account, $provider, $response);
}
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$account->getProviderKey());
if (!$provider) {
$response = $this->renderError(
pht(
'The account you are attempting to register with uses a nonexistent '.
'or disabled authentication provider (with key "%s"). An '.
'administrator may have recently disabled this provider.',
$account->getProviderKey()));
return array($account, $provider, $response);
}
return array($account, $provider, null);
}
protected function loadInvite() {
$invite_cookie = PhabricatorCookies::COOKIE_INVITE;
$invite_code = $this->getRequest()->getCookie($invite_cookie);
if (!$invite_code) {
return null;
}
$engine = id(new PhabricatorAuthInviteEngine())
->setViewer($this->getViewer())
->setUserHasConfirmedVerify(true);
try {
return $engine->processInviteCode($invite_code);
} catch (Exception $ex) {
// If this fails for any reason, just drop the invite. In normal
// circumstances, we gave them a detailed explanation of any error
// before they jumped into this workflow.
return null;
}
}
protected function renderInviteHeader(PhabricatorAuthInvite $invite) {
$viewer = $this->getViewer();
$invite_author = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($invite->getAuthorPHID()))
->needProfileImage(true)
->executeOne();
// If we can't load the author for some reason, just drop this message.
// We lose the value of contextualizing things without author details.
if (!$invite_author) {
return null;
}
$invite_item = id(new PHUIObjectItemView())
->setHeader(pht('Welcome to Phabricator!'))
->setImageURI($invite_author->getProfileImageURI())
->addAttribute(
pht(
'%s has invited you to join Phabricator.',
$invite_author->getFullName()));
$invite_list = id(new PHUIObjectItemListView())
->addItem($invite_item)
->setFlush(true);
return id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($invite_list);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthLinkController.php b/src/applications/auth/controller/PhabricatorAuthLinkController.php
index d50bcf1d8a..44176a278e 100644
--- a/src/applications/auth/controller/PhabricatorAuthLinkController.php
+++ b/src/applications/auth/controller/PhabricatorAuthLinkController.php
@@ -1,130 +1,127 @@
<?php
final class PhabricatorAuthLinkController
extends PhabricatorAuthController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$action = $request->getURIData('action');
$provider_key = $request->getURIData('pkey');
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$provider_key);
if (!$provider) {
return new Aphront404Response();
}
switch ($action) {
case 'link':
if (!$provider->shouldAllowAccountLink()) {
return $this->renderErrorPage(
pht('Account Not Linkable'),
array(
pht('This provider is not configured to allow linking.'),
));
}
break;
case 'refresh':
if (!$provider->shouldAllowAccountRefresh()) {
return $this->renderErrorPage(
pht('Account Not Refreshable'),
array(
pht('This provider does not allow refreshing.'),
));
}
break;
default:
return new Aphront400Response();
}
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
'accountType = %s AND accountDomain = %s AND userPHID = %s',
$provider->getProviderType(),
$provider->getProviderDomain(),
$viewer->getPHID());
switch ($action) {
case 'link':
if ($account) {
return $this->renderErrorPage(
pht('Account Already Linked'),
array(
pht(
'Your Phabricator account is already linked to an external '.
'account for this provider.'),
));
}
break;
case 'refresh':
if (!$account) {
return $this->renderErrorPage(
pht('No Account Linked'),
array(
pht(
'You do not have a linked account on this provider, and thus '.
'can not refresh it.'),
));
}
break;
default:
return new Aphront400Response();
}
$panel_uri = '/settings/panel/external/';
PhabricatorCookies::setClientIDCookie($request);
switch ($action) {
case 'link':
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$panel_uri);
$form = $provider->buildLinkForm($this);
break;
case 'refresh':
$form = $provider->buildRefreshForm($this);
break;
default:
return new Aphront400Response();
}
if ($provider->isLoginFormAButton()) {
require_celerity_resource('auth-css');
$form = phutil_tag(
'div',
array(
'class' => 'phabricator-link-button pl',
),
$form);
}
switch ($action) {
case 'link':
$name = pht('Link Account');
$title = pht('Link %s Account', $provider->getProviderName());
break;
case 'refresh':
$name = pht('Refresh Account');
$title = pht('Refresh %s Account', $provider->getProviderName());
break;
default:
return new Aphront400Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Link Account'), $panel_uri);
$crumbs->addTextCrumb($provider->getProviderName($name));
+ $crumbs->setBorder(true);
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $form,
- ),
- array(
- 'title' => $title,
- ));
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($form);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthLoginController.php b/src/applications/auth/controller/PhabricatorAuthLoginController.php
index be610e223a..b9b2a8d876 100644
--- a/src/applications/auth/controller/PhabricatorAuthLoginController.php
+++ b/src/applications/auth/controller/PhabricatorAuthLoginController.php
@@ -1,271 +1,267 @@
<?php
final class PhabricatorAuthLoginController
extends PhabricatorAuthController {
private $providerKey;
private $extraURIData;
private $provider;
public function shouldRequireLogin() {
return false;
}
public function shouldAllowRestrictedParameter($parameter_name) {
// Whitelist the OAuth 'code' parameter.
if ($parameter_name == 'code') {
return true;
}
return parent::shouldAllowRestrictedParameter($parameter_name);
}
public function getExtraURIData() {
return $this->extraURIData;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->providerKey = $request->getURIData('pkey');
$this->extraURIData = $request->getURIData('extra');
$response = $this->loadProvider();
if ($response) {
return $response;
}
$provider = $this->provider;
try {
list($account, $response) = $provider->processLoginRequest($this);
} catch (PhutilAuthUserAbortedException $ex) {
if ($viewer->isLoggedIn()) {
// If a logged-in user cancels, take them back to the external accounts
// panel.
$next_uri = '/settings/panel/external/';
} else {
// If a logged-out user cancels, take them back to the auth start page.
$next_uri = '/';
}
// User explicitly hit "Cancel".
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Authentication Canceled'))
->appendChild(
pht('You canceled authentication.'))
->addCancelButton($next_uri, pht('Continue'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
if ($response) {
return $response;
}
if (!$account) {
throw new Exception(
pht(
'Auth provider failed to load an account from %s!',
'processLoginRequest()'));
}
if ($account->getUserPHID()) {
// The account is already attached to a Phabricator user, so this is
// either a login or a bad account link request.
if (!$viewer->isLoggedIn()) {
if ($provider->shouldAllowLogin()) {
return $this->processLoginUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow logins on this Phabricator install. '.
'An administrator may have recently disabled it.',
$provider->getProviderName()));
}
} else if ($viewer->getPHID() == $account->getUserPHID()) {
// This is either an attempt to re-link an existing and already
// linked account (which is silly) or a refresh of an external account
// (e.g., an OAuth account).
return id(new AphrontRedirectResponse())
->setURI('/settings/panel/external/');
} else {
return $this->renderError(
pht(
'The external account ("%s") you just used to login is already '.
'associated with another Phabricator user account. Login to the '.
'other Phabricator account and unlink the external account before '.
'linking it to a new Phabricator account.',
$provider->getProviderName()));
}
} else {
// The account is not yet attached to a Phabricator user, so this is
// either a registration or an account link request.
if (!$viewer->isLoggedIn()) {
if ($provider->shouldAllowRegistration()) {
return $this->processRegisterUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow registration on this Phabricator '.
'install. An administrator may have recently disabled it.',
$provider->getProviderName()));
}
} else {
// If the user already has a linked account of this type, prevent them
// from linking a second account. This can happen if they swap logins
// and then refresh the account link. See T6707. We will eventually
// allow this after T2549.
$existing_accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withAccountTypes(array($account->getAccountType()))
->execute();
if ($existing_accounts) {
return $this->renderError(
pht(
'Your Phabricator account is already connected to an external '.
'account on this provider ("%s"), but you are currently logged '.
'in to the provider with a different account. Log out of the '.
'external service, then log back in with the correct account '.
'before refreshing the account link.',
$provider->getProviderName()));
}
if ($provider->shouldAllowAccountLink()) {
return $this->processLinkUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow account linking on this Phabricator '.
'install. An administrator may have recently disabled it.',
$provider->getProviderName()));
}
}
}
// This should be unreachable, but fail explicitly if we get here somehow.
return new Aphront400Response();
}
private function processLoginUser(PhabricatorExternalAccount $account) {
$user = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$account->getUserPHID());
if (!$user) {
return $this->renderError(
pht(
'The external account you just logged in with is not associated '.
'with a valid Phabricator user.'));
}
return $this->loginUser($user);
}
private function processRegisterUser(PhabricatorExternalAccount $account) {
$account_secret = $account->getAccountSecret();
$register_uri = $this->getApplicationURI('register/'.$account_secret.'/');
return $this->setAccountKeyAndContinue($account, $register_uri);
}
private function processLinkUser(PhabricatorExternalAccount $account) {
$account_secret = $account->getAccountSecret();
$confirm_uri = $this->getApplicationURI('confirmlink/'.$account_secret.'/');
return $this->setAccountKeyAndContinue($account, $confirm_uri);
}
private function setAccountKeyAndContinue(
PhabricatorExternalAccount $account,
$next_uri) {
if ($account->getUserPHID()) {
throw new Exception(pht('Account is already registered or linked.'));
}
// Regenerate the registration secret key, set it on the external account,
// set a cookie on the user's machine, and redirect them to registration.
// See PhabricatorAuthRegisterController for discussion of the registration
// key.
$registration_key = Filesystem::readRandomCharacters(32);
$account->setProperty(
'registrationKey',
PhabricatorHash::digest($registration_key));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
$this->getRequest()->setTemporaryCookie(
PhabricatorCookies::COOKIE_REGISTRATION,
$registration_key);
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
private function loadProvider() {
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$this->providerKey);
if (!$provider) {
return $this->renderError(
pht(
'The account you are attempting to login with uses a nonexistent '.
'or disabled authentication provider (with key "%s"). An '.
'administrator may have recently disabled this provider.',
$this->providerKey));
}
$this->provider = $provider;
return null;
}
protected function renderError($message) {
return $this->renderErrorPage(
pht('Login Failed'),
array($message));
}
public function buildProviderPageResponse(
PhabricatorAuthProvider $provider,
$content) {
$crumbs = $this->buildApplicationCrumbs();
- $crumbs->setBorder(true);
if ($this->getRequest()->getUser()->isLoggedIn()) {
$crumbs->addTextCrumb(pht('Link Account'), $provider->getSettingsURI());
} else {
$crumbs->addTextCrumb(pht('Login'), $this->getApplicationURI('start/'));
}
$crumbs->addTextCrumb($provider->getProviderName());
+ $crumbs->setBorder(true);
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $content,
- ),
- array(
- 'title' => pht('Login'),
- ));
+ return $this->newPage()
+ ->setTitle(pht('Login'))
+ ->setCrumbs($crumbs)
+ ->appendChild($content);
}
public function buildProviderErrorResponse(
PhabricatorAuthProvider $provider,
$message) {
$message = pht(
'Authentication provider ("%s") encountered an error during login. %s',
$provider->getProviderName(),
$message);
return $this->renderError($message);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php
index 0d07470560..ba28816375 100644
--- a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php
+++ b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php
@@ -1,38 +1,37 @@
<?php
final class PhabricatorAuthNeedsApprovalController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function shouldRequireEmailVerification() {
return false;
}
public function shouldRequireEnabledUser() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$wait_for_approval = pht(
"Your account has been created, but needs to be approved by an ".
"administrator. You'll receive an email once your account is approved.");
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Wait for Approval'))
->appendChild($wait_for_approval)
->addCancelButton('/', pht('Wait Patiently'));
- return $this->buildApplicationPage(
- $dialog,
- array(
- 'title' => pht('Wait For Approval'),
- ));
+ return $this->newPage()
+ ->setTitle(pht('Wait For Approval'))
+ ->appendChild($dialog);
+
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
index aaf3864156..f3e2562a1c 100644
--- a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
+++ b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php
@@ -1,90 +1,91 @@
<?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 handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$panel = id(new PhabricatorMultiFactorSettingsPanel())
->setUser($viewer)
->setViewer($viewer)
->setOverrideURI($this->getApplicationURI('/multifactor/'))
->processRequest($request);
if ($panel instanceof AphrontResponse) {
return $panel;
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Add Multi-Factor Auth'));
$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.'),
));
} 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')))),
));
}
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $help,
- $panel,
- ),
- array(
- 'title' => pht('Add Multi-Factor Authentication'),
- ));
+ $view = array(
+ $help,
+ $panel,
+ );
+
+ return $this->newPage()
+ ->setTitle(pht('Add Multi-Factor Authentication'))
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
+
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
index 655f63acb9..27d54e4ad6 100644
--- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php
+++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php
@@ -1,651 +1,656 @@
<?php
final class PhabricatorAuthRegisterController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account_key = $request->getURIData('akey');
if ($request->getUser()->isLoggedIn()) {
return $this->renderError(pht('You are already logged in.'));
}
$is_setup = false;
if (strlen($account_key)) {
$result = $this->loadAccountForRegistrationOrLinking($account_key);
list($account, $provider, $response) = $result;
$is_default = false;
} else if ($this->isFirstTimeSetup()) {
list($account, $provider, $response) = $this->loadSetupAccount();
$is_default = true;
$is_setup = true;
} else {
list($account, $provider, $response) = $this->loadDefaultAccount();
$is_default = true;
}
if ($response) {
return $response;
}
$invite = $this->loadInvite();
if (!$provider->shouldAllowRegistration()) {
if ($invite) {
// If the user has an invite, we allow them to register with any
// provider, even a login-only provider.
} else {
// TODO: This is a routine error if you click "Login" on an external
// auth source which doesn't allow registration. The error should be
// more tailored.
return $this->renderError(
pht(
'The account you are attempting to register with uses an '.
'authentication provider ("%s") which does not allow '.
'registration. An administrator may have recently disabled '.
'registration with this provider.',
$provider->getProviderName()));
}
}
$user = new PhabricatorUser();
$default_username = $account->getUsername();
$default_realname = $account->getRealName();
$default_email = $account->getEmail();
if ($invite) {
$default_email = $invite->getEmailAddress();
}
if (!PhabricatorUserEmail::isValidAddress($default_email)) {
$default_email = null;
}
if ($default_email !== null) {
// We should bypass policy here becase e.g. limiting an application use
// to a subset of users should not allow the others to overwrite
// configured application emails
$application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withAddresses(array($default_email))
->executeOne();
if ($application_email) {
$default_email = null;
}
}
if ($default_email !== null) {
// If the account source provided an email, but it's not allowed by
// the configuration, roadblock the user. Previously, we let the user
// pick a valid email address instead, but this does not align well with
// user expectation and it's not clear the cases it enables are valuable.
// See discussion in T3472.
if (!PhabricatorUserEmail::isAllowedAddress($default_email)) {
return $this->renderError(
array(
pht(
'The account you are attempting to register with has an invalid '.
'email address (%s). This Phabricator install only allows '.
'registration with specific email addresses:',
$default_email),
phutil_tag('br'),
phutil_tag('br'),
PhabricatorUserEmail::describeAllowedAddresses(),
));
}
// If the account source provided an email, but another account already
// has that email, just pretend we didn't get an email.
// TODO: See T3472.
if ($default_email !== null) {
$same_email = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$default_email);
if ($same_email) {
if ($invite) {
// We're allowing this to continue. The fact that we loaded the
// invite means that the address is nonprimary and unverified and
// we're OK to steal it.
} else {
$default_email = null;
}
}
}
}
$profile = id(new PhabricatorRegistrationProfile())
->setDefaultUsername($default_username)
->setDefaultEmail($default_email)
->setDefaultRealName($default_realname)
->setCanEditUsername(true)
->setCanEditEmail(($default_email === null))
->setCanEditRealName(true)
->setShouldVerifyEmail(false);
$event_type = PhabricatorEventType::TYPE_AUTH_WILLREGISTERUSER;
$event_data = array(
'account' => $account,
'profile' => $profile,
);
$event = id(new PhabricatorEvent($event_type, $event_data))
->setUser($user);
PhutilEventEngine::dispatchEvent($event);
$default_username = $profile->getDefaultUsername();
$default_email = $profile->getDefaultEmail();
$default_realname = $profile->getDefaultRealName();
$can_edit_username = $profile->getCanEditUsername();
$can_edit_email = $profile->getCanEditEmail();
$can_edit_realname = $profile->getCanEditRealName();
$must_set_password = $provider->shouldRequireRegistrationPassword();
$can_edit_anything = $profile->getCanEditAnything() || $must_set_password;
$force_verify = $profile->getShouldVerifyEmail();
// Automatically verify the administrator's email address during first-time
// setup.
if ($is_setup) {
$force_verify = true;
}
$value_username = $default_username;
$value_realname = $default_realname;
$value_email = $default_email;
$value_password = null;
$errors = array();
$require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name');
$e_username = strlen($value_username) ? null : true;
$e_realname = $require_real_name ? true : null;
$e_email = strlen($value_email) ? null : true;
$e_password = true;
$e_captcha = true;
$skip_captcha = false;
if ($invite) {
// If the user is accepting an invite, assume they're trustworthy enough
// that we don't need to CAPTCHA them.
$skip_captcha = true;
}
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
$from_invite = $request->getStr('invite');
if ($from_invite && $can_edit_username) {
$value_username = $request->getStr('username');
$e_username = null;
}
if (($request->isFormPost() || !$can_edit_anything) && !$from_invite) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
if ($must_set_password && !$skip_captcha) {
$e_captcha = pht('Again');
$captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request);
if (!$captcha_ok) {
$errors[] = pht('Captcha response is incorrect, try again.');
$e_captcha = pht('Invalid');
}
}
if ($can_edit_username) {
$value_username = $request->getStr('username');
if (!strlen($value_username)) {
$e_username = pht('Required');
$errors[] = pht('Username is required.');
} else if (!PhabricatorUser::validateUsername($value_username)) {
$e_username = pht('Invalid');
$errors[] = PhabricatorUser::describeValidUsername();
} else {
$e_username = null;
}
}
if ($must_set_password) {
$value_password = $request->getStr('password');
$value_confirm = $request->getStr('confirm');
if (!strlen($value_password)) {
$e_password = pht('Required');
$errors[] = pht('You must choose a password.');
} else if ($value_password !== $value_confirm) {
$e_password = pht('No Match');
$errors[] = pht('Password and confirmation must match.');
} else if (strlen($value_password) < $min_len) {
$e_password = pht('Too Short');
$errors[] = pht(
'Password is too short (must be at least %d characters long).',
$min_len);
} else if (
PhabricatorCommonPasswords::isCommonPassword($value_password)) {
$e_password = pht('Very Weak');
$errors[] = pht(
'Password is pathologically weak. This password is one of the '.
'most common passwords in use, and is extremely easy for '.
'attackers to guess. You must choose a stronger password.');
} else {
$e_password = null;
}
}
if ($can_edit_email) {
$value_email = $request->getStr('email');
if (!strlen($value_email)) {
$e_email = pht('Required');
$errors[] = pht('Email is required.');
} else if (!PhabricatorUserEmail::isValidAddress($value_email)) {
$e_email = pht('Invalid');
$errors[] = PhabricatorUserEmail::describeValidAddresses();
} else if (!PhabricatorUserEmail::isAllowedAddress($value_email)) {
$e_email = pht('Disallowed');
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
} else {
$e_email = null;
}
}
if ($can_edit_realname) {
$value_realname = $request->getStr('realName');
if (!strlen($value_realname) && $require_real_name) {
$e_realname = pht('Required');
$errors[] = pht('Real name is required.');
} else {
$e_realname = null;
}
}
if (!$errors) {
$image = $this->loadProfilePicture($account);
if ($image) {
$user->setProfileImagePHID($image->getPHID());
}
try {
$verify_email = false;
if ($force_verify) {
$verify_email = true;
}
if ($value_email === $default_email) {
if ($account->getEmailVerified()) {
$verify_email = true;
}
if ($provider->shouldTrustEmails()) {
$verify_email = true;
}
if ($invite) {
$verify_email = true;
}
}
$email_obj = null;
if ($invite) {
// If we have a valid invite, this email may exist but be
// nonprimary and unverified, so we'll reassign it.
$email_obj = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$value_email);
}
if (!$email_obj) {
$email_obj = id(new PhabricatorUserEmail())
->setAddress($value_email);
}
$email_obj->setIsVerified((int)$verify_email);
$user->setUsername($value_username);
$user->setRealname($value_realname);
if ($is_setup) {
$must_approve = false;
} else if ($invite) {
$must_approve = false;
} else {
$must_approve = PhabricatorEnv::getEnvConfig(
'auth.require-approval');
}
if ($must_approve) {
$user->setIsApproved(0);
} else {
$user->setIsApproved(1);
}
if ($invite) {
$allow_reassign_email = true;
} else {
$allow_reassign_email = false;
}
$user->openTransaction();
$editor = id(new PhabricatorUserEditor())
->setActor($user);
$editor->createNewUser($user, $email_obj, $allow_reassign_email);
if ($must_set_password) {
$envelope = new PhutilOpaqueEnvelope($value_password);
$editor->changePassword($user, $envelope);
}
if ($is_setup) {
$editor->makeAdminUser($user, true);
}
$account->setUserPHID($user->getPHID());
$provider->willRegisterAccount($account);
$account->save();
$user->saveTransaction();
if (!$email_obj->getIsVerified()) {
$email_obj->sendVerificationEmail($user);
}
if ($must_approve) {
$this->sendWaitingForApprovalEmail($user);
}
if ($invite) {
$invite->setAcceptedByPHID($user->getPHID())->save();
}
return $this->loginUser($user);
} catch (AphrontDuplicateKeyQueryException $exception) {
$same_username = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$user->getUserName());
$same_email = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$value_email);
if ($same_username) {
$e_username = pht('Duplicate');
$errors[] = pht('Another user already has that username.');
}
if ($same_email) {
// TODO: See T3340.
$e_email = pht('Duplicate');
$errors[] = pht('Another user already has that email.');
}
if (!$same_username && !$same_email) {
throw $exception;
}
}
}
unset($unguarded);
}
$form = id(new AphrontFormView())
->setUser($request->getUser());
if (!$is_default) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('External Account'))
->setValue(
id(new PhabricatorAuthAccountView())
->setUser($request->getUser())
->setExternalAccount($account)
->setAuthProvider($provider)));
}
if ($can_edit_username) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Phabricator Username'))
->setName('username')
->setValue($value_username)
->setError($e_username));
} else {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Phabricator Username'))
->setValue($value_username)
->setError($e_username));
}
if ($can_edit_realname) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Real Name'))
->setName('realName')
->setValue($value_realname)
->setError($e_realname));
}
if ($must_set_password) {
$form->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Password'))
->setName('password')
->setError($e_password));
$form->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Confirm Password'))
->setName('confirm')
->setError($e_password)
->setCaption(
$min_len
? pht('Minimum length of %d characters.', $min_len)
: null));
}
if ($can_edit_email) {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setName('email')
->setValue($value_email)
->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
->setError($e_email));
}
if ($must_set_password && !$skip_captcha) {
$form->appendChild(
id(new AphrontFormRecaptchaControl())
->setLabel(pht('Captcha'))
->setError($e_captcha));
}
$submit = id(new AphrontFormSubmitControl());
if ($is_setup) {
$submit
->setValue(pht('Create Admin Account'));
} else {
$submit
->addCancelButton($this->getApplicationURI('start/'))
->setValue(pht('Register Phabricator Account'));
}
$form->appendChild($submit);
$crumbs = $this->buildApplicationCrumbs();
if ($is_setup) {
$crumbs->addTextCrumb(pht('Setup Admin Account'));
$title = pht('Welcome to Phabricator');
} else {
$crumbs->addTextCrumb(pht('Register'));
$crumbs->addTextCrumb($provider->getProviderName());
$title = pht('Phabricator Registration');
}
+ $crumbs->setBorder(true);
$welcome_view = null;
if ($is_setup) {
$welcome_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setTitle(pht('Welcome to Phabricator'))
->appendChild(
pht(
'Installation is complete. Register your administrator account '.
'below to log in. You will be able to configure options and add '.
'other authentication mechanisms (like LDAP or OAuth) later on.'));
}
$object_box = id(new PHUIObjectBoxView())
- ->setHeaderText($title)
->setForm($form)
->setFormErrors($errors);
$invite_header = null;
if ($invite) {
$invite_header = $this->renderInviteHeader($invite);
}
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $welcome_view,
- $invite_header,
- $object_box,
- ),
- array(
- 'title' => $title,
- ));
+ $header = id(new PHUIHeaderView())
+ ->setHeader($title);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter(array(
+ $welcome_view,
+ $invite_header,
+ $object_box,
+ ));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
}
private function loadDefaultAccount() {
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
$account = null;
$provider = null;
$response = null;
foreach ($providers as $key => $candidate_provider) {
if (!$candidate_provider->shouldAllowRegistration()) {
unset($providers[$key]);
continue;
}
if (!$candidate_provider->isDefaultRegistrationProvider()) {
unset($providers[$key]);
}
}
if (!$providers) {
$response = $this->renderError(
pht(
'There are no configured default registration providers.'));
return array($account, $provider, $response);
} else if (count($providers) > 1) {
$response = $this->renderError(
pht('There are too many configured default registration providers.'));
return array($account, $provider, $response);
}
$provider = head($providers);
$account = $provider->getDefaultExternalAccount();
return array($account, $provider, $response);
}
private function loadSetupAccount() {
$provider = new PhabricatorPasswordAuthProvider();
$provider->attachProviderConfig(
id(new PhabricatorAuthProviderConfig())
->setShouldAllowRegistration(1)
->setShouldAllowLogin(1)
->setIsEnabled(true));
$account = $provider->getDefaultExternalAccount();
$response = null;
return array($account, $provider, $response);
}
private function loadProfilePicture(PhabricatorExternalAccount $account) {
$phid = $account->getProfileImagePHID();
if (!$phid) {
return null;
}
// NOTE: Use of omnipotent user is okay here because the registering user
// can not control the field value, and we can't use their user object to
// do meaningful policy checks anyway since they have not registered yet.
// Reaching this means the user holds the account secret key and the
// registration secret key, and thus has permission to view the image.
$file = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($phid))
->executeOne();
if (!$file) {
return null;
}
$xform = PhabricatorFileTransform::getTransformByKey(
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
return $xform->executeTransform($file);
}
protected function renderError($message) {
return $this->renderErrorPage(
pht('Registration Failed'),
array($message));
}
private function sendWaitingForApprovalEmail(PhabricatorUser $user) {
$title = '[Phabricator] '.pht(
'New User "%s" Awaiting Approval',
$user->getUsername());
$body = new PhabricatorMetaMTAMailBody();
$body->addRawSection(
pht(
'Newly registered user "%s" is awaiting account approval by an '.
'administrator.',
$user->getUsername()));
$body->addLinkSection(
pht('APPROVAL QUEUE'),
PhabricatorEnv::getProductionURI(
'/people/query/approval/'));
$body->addLinkSection(
pht('DISABLE APPROVAL QUEUE'),
PhabricatorEnv::getProductionURI(
'/config/edit/auth.require-approval/'));
$admins = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIsAdmin(true)
->execute();
if (!$admins) {
return;
}
$mail = id(new PhabricatorMetaMTAMail())
->addTos(mpull($admins, 'getPHID'))
->setSubject($title)
->setBody($body->render())
->saveAndSend();
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php
index 8f4a374241..d591b6313f 100644
--- a/src/applications/auth/controller/PhabricatorAuthStartController.php
+++ b/src/applications/auth/controller/PhabricatorAuthStartController.php
@@ -1,267 +1,268 @@
<?php
final class PhabricatorAuthStartController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
if ($viewer->isLoggedIn()) {
// Kick the user home if they are already logged in.
return id(new AphrontRedirectResponse())->setURI('/');
}
if ($request->isAjax()) {
return $this->processAjaxRequest();
}
if ($request->isConduit()) {
return $this->processConduitRequest();
}
// If the user gets this far, they aren't logged in, so if they have a
// user session token we can conclude that it's invalid: if it was valid,
// they'd have been logged in above and never made it here. Try to clear
// it and warn the user they may need to nuke their cookies.
$session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
if (strlen($session_token)) {
$kind = PhabricatorAuthSessionEngine::getSessionKindFromToken(
$session_token);
switch ($kind) {
case PhabricatorAuthSessionEngine::KIND_ANONYMOUS:
// If this is an anonymous session. It's expected that they won't
// be logged in, so we can just continue.
break;
default:
// The session cookie is invalid, so clear it.
$request->clearCookie(PhabricatorCookies::COOKIE_USERNAME);
$request->clearCookie(PhabricatorCookies::COOKIE_SESSION);
return $this->renderError(
pht(
'Your login session is invalid. Try reloading the page and '.
'logging in again. If that does not work, clear your browser '.
'cookies.'));
}
}
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
foreach ($providers as $key => $provider) {
if (!$provider->shouldAllowLogin()) {
unset($providers[$key]);
}
}
if (!$providers) {
if ($this->isFirstTimeSetup()) {
// If this is a fresh install, let the user register their admin
// account.
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI('/register/'));
}
return $this->renderError(
pht(
'This Phabricator install is not configured with any enabled '.
'authentication providers which can be used to log in. If you '.
'have accidentally locked yourself out by disabling all providers, '.
'you can use `%s` to recover access to an administrative account.',
'phabricator/bin/auth recover <username>'));
}
$next_uri = $request->getStr('next');
if (!strlen($next_uri)) {
if ($this->getDelegatingController()) {
// Only set a next URI from the request path if this controller was
// delegated to, which happens when a user tries to view a page which
// requires them to login.
// If this controller handled the request directly, we're on the main
// login page, and never want to redirect the user back here after they
// login.
$next_uri = (string)$this->getRequest()->getRequestURI();
}
}
if (!$request->isFormPost()) {
if (strlen($next_uri)) {
PhabricatorCookies::setNextURICookie($request, $next_uri);
}
PhabricatorCookies::setClientIDCookie($request);
}
if (!$request->getURIData('loggedout') && count($providers) == 1) {
$auto_login_provider = head($providers);
$auto_login_config = $auto_login_provider->getProviderConfig();
if ($auto_login_provider instanceof PhabricatorPhabricatorAuthProvider &&
$auto_login_config->getShouldAutoLogin()) {
$auto_login_adapter = $provider->getAdapter();
$auto_login_adapter->setState($provider->getAuthCSRFCode($request));
return id(new AphrontRedirectResponse())
->setIsExternal(true)
->setURI($provider->getAdapter()->getAuthenticateURI());
}
}
$invite = $this->loadInvite();
$not_buttons = array();
$are_buttons = array();
$providers = msort($providers, 'getLoginOrder');
foreach ($providers as $provider) {
if ($invite) {
$form = $provider->buildInviteForm($this);
} else {
$form = $provider->buildLoginForm($this);
}
if ($provider->isLoginFormAButton()) {
$are_buttons[] = $form;
} else {
$not_buttons[] = $form;
}
}
$out = array();
$out[] = $not_buttons;
if ($are_buttons) {
require_celerity_resource('auth-css');
foreach ($are_buttons as $key => $button) {
$are_buttons[$key] = phutil_tag(
'div',
array(
'class' => 'phabricator-login-button mmb',
),
$button);
}
// If we only have one button, add a second pretend button so that we
// always have two columns. This makes it easier to get the alignments
// looking reasonable.
if (count($are_buttons) == 1) {
$are_buttons[] = null;
}
$button_columns = id(new AphrontMultiColumnView())
->setFluidLayout(true);
$are_buttons = array_chunk($are_buttons, ceil(count($are_buttons) / 2));
foreach ($are_buttons as $column) {
$button_columns->addColumn($column);
}
$out[] = phutil_tag(
'div',
array(
'class' => 'phabricator-login-buttons',
),
$button_columns);
}
$handlers = PhabricatorAuthLoginHandler::getAllHandlers();
$delegating_controller = $this->getDelegatingController();
$header = array();
foreach ($handlers as $handler) {
$handler = clone $handler;
$handler->setRequest($request);
if ($delegating_controller) {
$handler->setDelegatingController($delegating_controller);
}
$header[] = $handler->getAuthLoginHeaderContent();
}
$invite_message = null;
if ($invite) {
$invite_message = $this->renderInviteHeader($invite);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Login'));
$crumbs->setBorder(true);
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $header,
- $invite_message,
- $out,
- ),
- array(
- 'title' => pht('Login to Phabricator'),
- ));
+ $title = pht('Login to Phabricator');
+ $view = array(
+ $header,
+ $invite_message,
+ $out,
+ );
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
}
private function processAjaxRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
// We end up here if the user clicks a workflow link that they need to
// login to use. We give them a dialog saying "You need to login...".
if ($request->isDialogFormPost()) {
return id(new AphrontRedirectResponse())->setURI(
$request->getRequestURI());
}
// Often, users end up here by clicking a disabled action link in the UI
// (for example, they might click "Edit Blocking Tasks" on a Maniphest
// task page). After they log in we want to send them back to that main
// object page if we can, since it's confusing to end up on a standalone
// page with only a dialog (particularly if that dialog is another error,
// like a policy exception).
$via_header = AphrontRequest::getViaHeaderName();
$via_uri = AphrontRequest::getHTTPHeader($via_header);
if (strlen($via_uri)) {
PhabricatorCookies::setNextURICookie($request, $via_uri, $force = true);
}
return $this->newDialog()
->setTitle(pht('Login Required'))
->appendParagraph(pht('You must login to take this action.'))
->addSubmitButton(pht('Login'))
->addCancelButton('/');
}
private function processConduitRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
// A common source of errors in Conduit client configuration is getting
// the request path wrong. The client will end up here, so make some
// effort to give them a comprehensible error message.
$request_path = $this->getRequest()->getPath();
$conduit_path = '/api/<method>';
$example_path = '/api/conduit.ping';
$message = pht(
'ERROR: You are making a Conduit API request to "%s", but the correct '.
'HTTP request path to use in order to access a COnduit method is "%s" '.
'(for example, "%s"). Check your configuration.',
$request_path,
$conduit_path,
$example_path);
return id(new AphrontPlainTextResponse())->setContent($message);
}
protected function renderError($message) {
return $this->renderErrorPage(
pht('Authentication Failure'),
array($message));
}
}
diff --git a/src/applications/auth/controller/PhabricatorDisabledUserController.php b/src/applications/auth/controller/PhabricatorDisabledUserController.php
index 39e390d44a..26163c3034 100644
--- a/src/applications/auth/controller/PhabricatorDisabledUserController.php
+++ b/src/applications/auth/controller/PhabricatorDisabledUserController.php
@@ -1,25 +1,24 @@
<?php
final class PhabricatorDisabledUserController
extends PhabricatorAuthController {
public function shouldRequireEnabledUser() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
if (!$viewer->getIsDisabled()) {
return new Aphront404Response();
}
- return id(new AphrontDialogView())
- ->setUser($viewer)
+ return $this->newDialog()
->setTitle(pht('Account Disabled'))
->addCancelButton('/logout/', pht('Okay'))
->appendParagraph(pht('Your account has been disabled.'));
}
}
diff --git a/src/applications/auth/controller/PhabricatorEmailLoginController.php b/src/applications/auth/controller/PhabricatorEmailLoginController.php
index 26609133ea..e9cf693514 100644
--- a/src/applications/auth/controller/PhabricatorEmailLoginController.php
+++ b/src/applications/auth/controller/PhabricatorEmailLoginController.php
@@ -1,165 +1,163 @@
<?php
final class PhabricatorEmailLoginController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
return new Aphront400Response();
}
$e_email = true;
$e_captcha = true;
$errors = array();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
if ($request->isFormPost()) {
$e_email = null;
$e_captcha = pht('Again');
$captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request);
if (!$captcha_ok) {
$errors[] = pht('Captcha response is incorrect, try again.');
$e_captcha = pht('Invalid');
}
$email = $request->getStr('email');
if (!strlen($email)) {
$errors[] = pht('You must provide an email address.');
$e_email = pht('Required');
}
if (!$errors) {
// NOTE: Don't validate the email unless the captcha is good; this makes
// it expensive to fish for valid email addresses while giving the user
// a better error if they goof their email.
$target_email = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$email);
$target_user = null;
if ($target_email) {
$target_user = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$target_email->getUserPHID());
}
if (!$target_user) {
$errors[] =
pht('There is no account associated with that email address.');
$e_email = pht('Invalid');
}
// If this address is unverified, only send a reset link to it if
// the account has no verified addresses. This prevents an opportunistic
// attacker from compromising an account if a user adds an email
// address but mistypes it and doesn't notice.
// (For a newly created account, all the addresses may be unverified,
// which is why we'll send to an unverified address in that case.)
if ($target_email && !$target_email->getIsVerified()) {
$verified_addresses = id(new PhabricatorUserEmail())->loadAllWhere(
'userPHID = %s AND isVerified = 1',
$target_email->getUserPHID());
if ($verified_addresses) {
$errors[] = pht(
'That email address is not verified. You can only send '.
'password reset links to a verified address.');
$e_email = pht('Unverified');
}
}
if (!$errors) {
$engine = new PhabricatorAuthSessionEngine();
$uri = $engine->getOneTimeLoginURI(
$target_user,
null,
PhabricatorAuthSessionEngine::ONETIME_RESET);
if ($is_serious) {
$body = pht(
"You can use this link to reset your Phabricator password:".
"\n\n %s\n",
$uri);
} else {
$body = pht(
"Condolences on forgetting your password. You can use this ".
"link to reset it:\n\n".
" %s\n\n".
"After you set a new password, consider writing it down on a ".
"sticky note and attaching it to your monitor so you don't ".
"forget again! Choosing a very short, easy-to-remember password ".
"like \"cat\" or \"1234\" might also help.\n\n".
"Best Wishes,\nPhabricator\n",
$uri);
}
$mail = id(new PhabricatorMetaMTAMail())
->setSubject(pht('[Phabricator] Password Reset'))
->setForceDelivery(true)
->addRawTos(array($target_email->getAddress()))
->setBody($body)
->saveAndSend();
return $this->newDialog()
->setTitle(pht('Check Your Email'))
->setShortTitle(pht('Email Sent'))
->appendParagraph(
pht('An email has been sent with a link you can use to login.'))
->addCancelButton('/', pht('Done'));
}
}
}
$error_view = null;
if ($errors) {
$error_view = new PHUIInfoView();
$error_view->setErrors($errors);
}
$email_auth = new PHUIFormLayoutView();
$email_auth->appendChild($error_view);
$email_auth
->setUser($request->getUser())
->setFullWidth(true)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setName('email')
->setValue($request->getStr('email'))
->setError($e_email))
->appendChild(
id(new AphrontFormRecaptchaControl())
->setLabel(pht('Captcha'))
->setError($e_captcha));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Reset Password'));
+ $crumbs->setBorder(true);
$dialog = new AphrontDialogView();
$dialog->setUser($request->getUser());
$dialog->setTitle(pht('Forgot Password / Email Login'));
$dialog->appendChild($email_auth);
$dialog->addSubmitButton(pht('Send Email'));
$dialog->setSubmitURI('/login/email/');
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $dialog,
- ),
- array(
- 'title' => pht('Forgot Password'),
- ));
+ return $this->newPage()
+ ->setTitle(pht('Forgot Password'))
+ ->setCrumbs($crumbs)
+ ->appendChild($dialog);
+
}
}
diff --git a/src/applications/auth/controller/PhabricatorEmailVerificationController.php b/src/applications/auth/controller/PhabricatorEmailVerificationController.php
index 83a370139c..e8138339af 100644
--- a/src/applications/auth/controller/PhabricatorEmailVerificationController.php
+++ b/src/applications/auth/controller/PhabricatorEmailVerificationController.php
@@ -1,91 +1,89 @@
<?php
final class PhabricatorEmailVerificationController
extends PhabricatorAuthController {
public function shouldRequireEmailVerification() {
// Since users need to be able to hit this endpoint in order to verify
// email, we can't ever require email verification here.
return false;
}
public function shouldRequireEnabledUser() {
// Unapproved users are allowed to verify their email addresses. We'll kick
// disabled users out later.
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$code = $request->getURIData('code');
if ($viewer->getIsDisabled()) {
// We allowed unapproved and disabled users to hit this controller, but
// want to kick out disabled users now.
return new Aphront400Response();
}
$email = id(new PhabricatorUserEmail())->loadOneWhere(
'userPHID = %s AND verificationCode = %s',
$viewer->getPHID(),
$code);
$submit = null;
if (!$email) {
$title = pht('Unable to Verify Email');
$content = pht(
'The verification code you provided is incorrect, or the email '.
'address has been removed, or the email address is owned by another '.
'user. Make sure you followed the link in the email correctly and are '.
'logged in with the user account associated with the email address.');
$continue = pht('Rats!');
} else if ($email->getIsVerified() && $viewer->getIsEmailVerified()) {
$title = pht('Address Already Verified');
$content = pht(
'This email address has already been verified.');
$continue = pht('Continue to Phabricator');
} else if ($request->isFormPost()) {
id(new PhabricatorUserEditor())
->setActor($viewer)
->verifyEmail($viewer, $email);
$title = pht('Address Verified');
$content = pht(
'The email address %s is now verified.',
phutil_tag('strong', array(), $email->getAddress()));
$continue = pht('Continue to Phabricator');
} else {
$title = pht('Verify Email Address');
$content = pht(
'Verify this email address (%s) and attach it to your account?',
phutil_tag('strong', array(), $email->getAddress()));
$continue = pht('Cancel');
$submit = pht('Verify %s', $email->getAddress());
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle($title)
->addCancelButton('/', $continue)
->appendChild($content);
if ($submit) {
$dialog->addSubmitButton($submit);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Verify Email'));
+ $crumbs->setBorder(true);
+
+ return $this->newPage()
+ ->setTitle(pht('Verify Email'))
+ ->setCrumbs($crumbs)
+ ->appendChild($dialog);
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $dialog,
- ),
- array(
- 'title' => pht('Verify Email'),
- ));
}
}
diff --git a/src/applications/auth/controller/PhabricatorLogoutController.php b/src/applications/auth/controller/PhabricatorLogoutController.php
index de3ac50e5d..2600a08313 100644
--- a/src/applications/auth/controller/PhabricatorLogoutController.php
+++ b/src/applications/auth/controller/PhabricatorLogoutController.php
@@ -1,72 +1,69 @@
<?php
final class PhabricatorLogoutController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return true;
}
public function shouldRequireEmailVerification() {
// Allow unverified users to logout.
return false;
}
public function shouldRequireEnabledUser() {
// Allow disabled users to logout.
return false;
}
public function shouldAllowPartialSessions() {
return true;
}
public function shouldAllowLegallyNonCompliantUsers() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
if ($request->isFormPost()) {
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$viewer->getPHID(),
PhabricatorUserLog::ACTION_LOGOUT);
$log->save();
// Destroy the user's session in the database so logout works even if
// their cookies have some issues. We'll detect cookie issues when they
// try to login again and tell them to clear any junk.
$phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
if (strlen($phsid)) {
$session = id(new PhabricatorAuthSessionQuery())
->setViewer($viewer)
->withSessionKeys(array($phsid))
->executeOne();
if ($session) {
$session->delete();
}
}
$request->clearCookie(PhabricatorCookies::COOKIE_SESSION);
return id(new AphrontRedirectResponse())
->setURI('/auth/loggedout/');
}
if ($viewer->getPHID()) {
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
+ return $this->newDialog()
->setTitle(pht('Log out of Phabricator?'))
->appendChild(pht('Are you sure you want to log out?'))
->addSubmitButton(pht('Logout'))
->addCancelButton('/');
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
}
return id(new AphrontRedirectResponse())->setURI('/');
}
}
diff --git a/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php b/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php
index 779196382d..a944a5b5c8 100644
--- a/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php
+++ b/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php
@@ -1,66 +1,67 @@
<?php
final class PhabricatorMustVerifyEmailController
extends PhabricatorAuthController {
public function shouldRequireLogin() {
return false;
}
public function shouldRequireEmailVerification() {
// NOTE: We don't technically need this since PhabricatorController forces
// us here in either case, but it's more consistent with intent.
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$email = $viewer->loadPrimaryEmail();
if ($viewer->getIsEmailVerified()) {
return id(new AphrontRedirectResponse())->setURI('/');
}
$email_address = $email->getAddress();
$sent = null;
if ($request->isFormPost()) {
$email->sendVerificationEmail($viewer);
$sent = new PHUIInfoView();
$sent->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$sent->setTitle(pht('Email Sent'));
$sent->appendChild(
pht(
'Another verification email was sent to %s.',
phutil_tag('strong', array(), $email_address)));
}
$must_verify = pht(
'You must verify your email address to login. You should have a '.
'new email message from Phabricator with verification instructions '.
'in your inbox (%s).',
phutil_tag('strong', array(), $email_address));
$send_again = pht(
'If you did not receive an email, you can click the button below '.
'to try sending another one.');
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Check Your Email'))
->appendParagraph($must_verify)
->appendParagraph($send_again)
->addSubmitButton(pht('Send Another Email'));
- return $this->buildApplicationPage(
- array(
- $sent,
- $dialog,
- ),
- array(
- 'title' => pht('Must Verify Email'),
- ));
+ $view = array(
+ $sent,
+ $dialog,
+ );
+
+ return $this->newPage()
+ ->setTitle(pht('Must Verify Email'))
+ ->appendChild($view);
+
}
}
diff --git a/src/applications/auth/controller/config/PhabricatorAuthEditController.php b/src/applications/auth/controller/config/PhabricatorAuthEditController.php
index 21ba6ef99a..049edfacef 100644
--- a/src/applications/auth/controller/config/PhabricatorAuthEditController.php
+++ b/src/applications/auth/controller/config/PhabricatorAuthEditController.php
@@ -1,380 +1,380 @@
<?php
final class PhabricatorAuthEditController
extends PhabricatorAuthProviderConfigController {
public function handleRequest(AphrontRequest $request) {
$this->requireApplicationCapability(
AuthManageProvidersCapability::CAPABILITY);
$viewer = $request->getUser();
$provider_class = $request->getURIData('className');
$config_id = $request->getURIData('id');
if ($config_id) {
$config = id(new PhabricatorAuthProviderConfigQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($config_id))
->executeOne();
if (!$config) {
return new Aphront404Response();
}
$provider = $config->getProvider();
if (!$provider) {
return new Aphront404Response();
}
$is_new = false;
} else {
$provider = null;
$providers = PhabricatorAuthProvider::getAllBaseProviders();
foreach ($providers as $candidate_provider) {
if (get_class($candidate_provider) === $provider_class) {
$provider = $candidate_provider;
break;
}
}
if (!$provider) {
return new Aphront404Response();
}
// TODO: When we have multi-auth providers, support them here.
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer($viewer)
->withProviderClasses(array(get_class($provider)))
->execute();
if ($configs) {
$id = head($configs)->getID();
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setMethod('GET')
->setSubmitURI($this->getApplicationURI('config/edit/'.$id.'/'))
->setTitle(pht('Provider Already Configured'))
->appendChild(
pht(
'This provider ("%s") already exists, and you can not add more '.
'than one instance of it. You can edit the existing provider, '.
'or you can choose a different provider.',
$provider->getProviderName()))
->addCancelButton($this->getApplicationURI('config/new/'))
->addSubmitButton(pht('Edit Existing Provider'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$config = $provider->getDefaultProviderConfig();
$provider->attachProviderConfig($config);
$is_new = true;
}
$errors = array();
$v_login = $config->getShouldAllowLogin();
$v_registration = $config->getShouldAllowRegistration();
$v_link = $config->getShouldAllowLink();
$v_unlink = $config->getShouldAllowUnlink();
$v_trust_email = $config->getShouldTrustEmails();
$v_auto_login = $config->getShouldAutoLogin();
if ($request->isFormPost()) {
$properties = $provider->readFormValuesFromRequest($request);
list($errors, $issues, $properties) = $provider->processEditForm(
$request,
$properties);
$xactions = array();
if (!$errors) {
if ($is_new) {
if (!strlen($config->getProviderType())) {
$config->setProviderType($provider->getProviderType());
}
if (!strlen($config->getProviderDomain())) {
$config->setProviderDomain($provider->getProviderDomain());
}
}
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_LOGIN)
->setNewValue($request->getInt('allowLogin', 0));
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_REGISTRATION)
->setNewValue($request->getInt('allowRegistration', 0));
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_LINK)
->setNewValue($request->getInt('allowLink', 0));
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_UNLINK)
->setNewValue($request->getInt('allowUnlink', 0));
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_TRUST_EMAILS)
->setNewValue($request->getInt('trustEmails', 0));
if ($provider instanceof PhabricatorPhabricatorAuthProvider) {
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_AUTO_LOGIN)
->setNewValue($request->getInt('autoLogin', 0));
}
foreach ($properties as $key => $value) {
$xactions[] = id(new PhabricatorAuthProviderConfigTransaction())
->setTransactionType(
PhabricatorAuthProviderConfigTransaction::TYPE_PROPERTY)
->setMetadataValue('auth:property', $key)
->setNewValue($value);
}
if ($is_new) {
$config->save();
}
$editor = id(new PhabricatorAuthProviderConfigEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->applyTransactions($config, $xactions);
if ($provider->hasSetupStep() && $is_new) {
$id = $config->getID();
$next_uri = $this->getApplicationURI('config/edit/'.$id.'/');
} else {
$next_uri = $this->getApplicationURI();
}
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
} else {
$properties = $provider->readFormValuesFromProvider();
$issues = array();
}
if ($is_new) {
if ($provider->hasSetupStep()) {
$button = pht('Next Step');
} else {
$button = pht('Add Provider');
}
$crumb = pht('Add Provider');
- $title = pht('Add Authentication Provider');
+ $title = pht('Add Auth Provider');
+ $header_icon = 'fa-plus-square';
$cancel_uri = $this->getApplicationURI('/config/new/');
} else {
$button = pht('Save');
$crumb = pht('Edit Provider');
- $title = pht('Edit Authentication Provider');
+ $title = pht('Edit Auth Provider');
+ $header_icon = 'fa-pencil';
$cancel_uri = $this->getApplicationURI();
}
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('%s: %s', $title, $provider->getProviderName()))
+ ->setHeaderIcon($header_icon);
+
+ if ($config->getIsEnabled()) {
+ $status_name = pht('Enabled');
+ $status_color = 'green';
+ $status_icon = 'fa-check';
+ $header->setStatus($status_icon, $status_color, $status_name);
+ } else if (!$is_new) {
+ $status_name = pht('Disabled');
+ $status_color = 'indigo';
+ $status_icon = 'fa-ban';
+ $header->setStatus($status_icon, $status_color, $status_name);
+ }
+
$config_name = 'auth.email-domains';
$config_href = '/config/edit/'.$config_name.'/';
$email_domains = PhabricatorEnv::getEnvConfig($config_name);
if ($email_domains) {
$registration_warning = pht(
'Users will only be able to register with a verified email address '.
'at one of the configured [[ %s | %s ]] domains: **%s**',
$config_href,
$config_name,
implode(', ', $email_domains));
} else {
$registration_warning = pht(
"NOTE: Any user who can browse to this install's login page will be ".
"able to register a Phabricator account. To restrict who can register ".
"an account, configure [[ %s | %s ]].",
$config_href,
$config_name);
}
$str_login = array(
phutil_tag('strong', array(), pht('Allow Login:')),
' ',
pht(
'Allow users to log in using this provider. If you disable login, '.
'users can still use account integrations for this provider.'),
);
$str_registration = array(
phutil_tag('strong', array(), pht('Allow Registration:')),
' ',
pht(
'Allow users to register new Phabricator accounts using this '.
'provider. If you disable registration, users can still use this '.
'provider to log in to existing accounts, but will not be able to '.
'create new accounts.'),
);
$str_link = hsprintf(
'<strong>%s:</strong> %s',
pht('Allow Linking Accounts'),
pht(
'Allow users to link account credentials for this provider to '.
'existing Phabricator accounts. There is normally no reason to '.
'disable this unless you are trying to move away from a provider '.
'and want to stop users from creating new account links.'));
$str_unlink = hsprintf(
'<strong>%s:</strong> %s',
pht('Allow Unlinking Accounts'),
pht(
'Allow users to unlink account credentials for this provider from '.
'existing Phabricator accounts. If you disable this, Phabricator '.
'accounts will be permanently bound to provider accounts.'));
$str_trusted_email = hsprintf(
'<strong>%s:</strong> %s',
pht('Trust Email Addresses'),
pht(
'Phabricator will skip email verification for accounts registered '.
'through this provider.'));
$str_auto_login = hsprintf(
'<strong>%s:</strong> %s',
pht('Allow Auto Login'),
pht(
'Phabricator will automatically login with this provider if it is '.
'the only available provider.'));
- $status_tag = id(new PHUITagView())
- ->setType(PHUITagView::TYPE_STATE);
- if ($is_new) {
- $status_tag
- ->setName(pht('New Provider'))
- ->setBackgroundColor('blue');
- } else if ($config->getIsEnabled()) {
- $status_tag
- ->setName(pht('Enabled'))
- ->setBackgroundColor('green');
- } else {
- $status_tag
- ->setName(pht('Disabled'))
- ->setBackgroundColor('red');
- }
-
$form = id(new AphrontFormView())
->setUser($viewer)
- ->appendChild(
- id(new AphrontFormStaticControl())
- ->setLabel(pht('Provider'))
- ->setValue($provider->getProviderName()))
- ->appendChild(
- id(new AphrontFormStaticControl())
- ->setLabel(pht('Status'))
- ->setValue($status_tag))
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel(pht('Allow'))
->addCheckbox(
'allowLogin',
1,
$str_login,
$v_login))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'allowRegistration',
1,
$str_registration,
$v_registration))
->appendRemarkupInstructions($registration_warning)
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'allowLink',
1,
$str_link,
$v_link))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'allowUnlink',
1,
$str_unlink,
$v_unlink));
if ($provider->shouldAllowEmailTrustConfiguration()) {
$form->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'trustEmails',
1,
$str_trusted_email,
$v_trust_email));
}
if ($provider instanceof PhabricatorPhabricatorAuthProvider) {
$form->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'autoLogin',
1,
$str_auto_login,
$v_auto_login));
}
$provider->extendEditForm($request, $form, $properties, $issues);
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($button));
$help = $provider->getConfigurationHelp();
if ($help) {
$form->appendChild(id(new PHUIFormDividerControl()));
$form->appendRemarkupInstructions($help);
}
$footer = $provider->renderConfigurationFooter();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($crumb);
+ $crumbs->setBorder(true);
$timeline = null;
if (!$is_new) {
$timeline = $this->buildTransactionTimeline(
$config,
new PhabricatorAuthProviderConfigTransactionQuery());
$xactions = $timeline->getTransactions();
foreach ($xactions as $xaction) {
$xaction->setProvider($provider);
}
+ $timeline->setShouldTerminate(true);
}
$form_box = id(new PHUIObjectBoxView())
- ->setHeaderText($title)
+ ->setHeaderText(pht('Provider'))
->setFormErrors($errors)
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
- return $this->buildApplicationPage(
- array(
- $crumbs,
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter(array(
$form_box,
$footer,
$timeline,
- ),
- array(
- 'title' => $title,
));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
+
}
}
diff --git a/src/applications/auth/controller/config/PhabricatorAuthListController.php b/src/applications/auth/controller/config/PhabricatorAuthListController.php
index 6df32f50a5..cb9c21b35f 100644
--- a/src/applications/auth/controller/config/PhabricatorAuthListController.php
+++ b/src/applications/auth/controller/config/PhabricatorAuthListController.php
@@ -1,188 +1,193 @@
<?php
final class PhabricatorAuthListController
extends PhabricatorAuthProviderConfigController {
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer($viewer)
->execute();
$list = new PHUIObjectItemListView();
$can_manage = $this->hasApplicationCapability(
AuthManageProvidersCapability::CAPABILITY);
foreach ($configs as $config) {
$item = new PHUIObjectItemView();
$id = $config->getID();
$edit_uri = $this->getApplicationURI('config/edit/'.$id.'/');
$enable_uri = $this->getApplicationURI('config/enable/'.$id.'/');
$disable_uri = $this->getApplicationURI('config/disable/'.$id.'/');
$provider = $config->getProvider();
if ($provider) {
$name = $provider->getProviderName();
} else {
$name = $config->getProviderType().' ('.$config->getProviderClass().')';
}
$item->setHeader($name);
if ($provider) {
$item->setHref($edit_uri);
} else {
$item->addAttribute(pht('Provider Implementation Missing!'));
}
$domain = null;
if ($provider) {
$domain = $provider->getProviderDomain();
if ($domain !== 'self') {
$item->addAttribute($domain);
}
}
if ($config->getShouldAllowRegistration()) {
$item->addAttribute(pht('Allows Registration'));
} else {
$item->addAttribute(pht('Does Not Allow Registration'));
}
if ($config->getIsEnabled()) {
$item->setState(PHUIObjectItemView::STATE_SUCCESS);
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setHref($disable_uri)
->setDisabled(!$can_manage)
->addSigil('workflow'));
} else {
$item->setState(PHUIObjectItemView::STATE_FAIL);
$item->addIcon('fa-times grey', pht('Disabled'));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-plus')
->setHref($enable_uri)
->setDisabled(!$can_manage)
->addSigil('workflow'));
}
$list->addItem($item);
}
$list->setNoDataString(
pht(
'%s You have not added authentication providers yet. Use "%s" to add '.
'a provider, which will let users register new Phabricator accounts '.
'and log in.',
phutil_tag(
'strong',
array(),
pht('No Providers Configured:')),
phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('config/new/'),
),
pht('Add Authentication Provider'))));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Auth Providers'));
+ $crumbs->setBorder(true);
$domains_key = 'auth.email-domains';
$domains_link = $this->renderConfigLink($domains_key);
$domains_value = PhabricatorEnv::getEnvConfig($domains_key);
$approval_key = 'auth.require-approval';
$approval_link = $this->renderConfigLink($approval_key);
$approval_value = PhabricatorEnv::getEnvConfig($approval_key);
$issues = array();
if ($domains_value) {
$issues[] = pht(
'Phabricator is configured with an email domain whitelist (in %s), so '.
'only users with a verified email address at one of these %s '.
'allowed domain(s) will be able to register an account: %s',
$domains_link,
phutil_count($domains_value),
phutil_tag('strong', array(), implode(', ', $domains_value)));
} else {
$issues[] = pht(
'Anyone who can browse to this Phabricator install will be able to '.
'register an account. To add email domain restrictions, configure '.
'%s.',
$domains_link);
}
if ($approval_value) {
$issues[] = pht(
'Administrative approvals are enabled (in %s), so all new users must '.
'have their accounts approved by an administrator.',
$approval_link);
} else {
$issues[] = pht(
'Administrative approvals are disabled, so users who register will '.
'be able to use their accounts immediately. To enable approvals, '.
'configure %s.',
$approval_link);
}
if (!$domains_value && !$approval_value) {
$severity = PHUIInfoView::SEVERITY_WARNING;
$issues[] = pht(
'You can safely ignore this warning if the install itself has '.
'access controls (for example, it is deployed on a VPN) or if all of '.
'the configured providers have access controls (for example, they are '.
'all private LDAP or OAuth servers).');
} else {
$severity = PHUIInfoView::SEVERITY_NOTICE;
}
$warning = id(new PHUIInfoView())
->setSeverity($severity)
->setErrors($issues);
$button = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::SIMPLE)
->setHref($this->getApplicationURI('config/new/'))
->setIcon('fa-plus')
->setDisabled(!$can_manage)
->setText(pht('Add Provider'));
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Authentication Providers'))
- ->addActionLink($button);
-
$list->setFlush(true);
$list = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->setInfoView($warning)
+ ->setHeaderText(pht('Providers'))
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($list);
- return $this->buildApplicationPage(
- array(
- $crumbs,
+ $title = pht('Auth Providers');
+ $header = id(new PHUIHeaderView())
+ ->setHeader($title)
+ ->setHeaderIcon('fa-key')
+ ->addActionLink($button);
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter(array(
+ $warning,
$list,
- ),
- array(
- 'title' => pht('Authentication Providers'),
));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
}
private function renderConfigLink($key) {
return phutil_tag(
'a',
array(
'href' => '/config/edit/'.$key.'/',
'target' => '_blank',
),
$key);
}
}
diff --git a/src/applications/auth/controller/config/PhabricatorAuthNewController.php b/src/applications/auth/controller/config/PhabricatorAuthNewController.php
index 18b4429ea9..dbd43f9ea8 100644
--- a/src/applications/auth/controller/config/PhabricatorAuthNewController.php
+++ b/src/applications/auth/controller/config/PhabricatorAuthNewController.php
@@ -1,100 +1,111 @@
<?php
final class PhabricatorAuthNewController
extends PhabricatorAuthProviderConfigController {
public function handleRequest(AphrontRequest $request) {
$this->requireApplicationCapability(
AuthManageProvidersCapability::CAPABILITY);
$request = $this->getRequest();
$viewer = $request->getUser();
$providers = PhabricatorAuthProvider::getAllBaseProviders();
$e_provider = null;
$errors = array();
if ($request->isFormPost()) {
$provider_string = $request->getStr('provider');
if (!strlen($provider_string)) {
$e_provider = pht('Required');
$errors[] = pht('You must select an authentication provider.');
} else {
$found = false;
foreach ($providers as $provider) {
if (get_class($provider) === $provider_string) {
$found = true;
break;
}
}
if (!$found) {
$e_provider = pht('Invalid');
$errors[] = pht('You must select a valid provider.');
}
}
if (!$errors) {
return id(new AphrontRedirectResponse())->setURI(
$this->getApplicationURI('/config/new/'.$provider_string.'/'));
}
}
$options = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Provider'))
->setName('provider')
->setError($e_provider);
$configured = PhabricatorAuthProvider::getAllProviders();
$configured_classes = array();
foreach ($configured as $configured_provider) {
$configured_classes[get_class($configured_provider)] = true;
}
// Sort providers by login order, and move disabled providers to the
// bottom.
$providers = msort($providers, 'getLoginOrder');
$providers = array_diff_key($providers, $configured_classes) + $providers;
foreach ($providers as $provider) {
if (isset($configured_classes[get_class($provider)])) {
$disabled = true;
$description = pht('This provider is already configured.');
} else {
$disabled = false;
$description = $provider->getDescriptionForCreate();
}
$options->addButton(
get_class($provider),
$provider->getNameForCreate(),
$description,
$disabled ? 'disabled' : null,
$disabled);
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild($options)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())
->setValue(pht('Continue')));
$form_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Add Authentication Provider'))
+ ->setHeaderText(pht('Provider'))
->setFormErrors($errors)
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Add Provider'));
+ $crumbs->setBorder(true);
- return $this->buildApplicationPage(
- array(
- $crumbs,
+ $title = pht('Add Auth Provider');
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader($title)
+ ->setHeaderIcon('fa-plus-square');
+
+ $view = id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter(array(
$form_box,
- ),
- array(
- 'title' => pht('Add Authentication Provider'),
));
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild($view);
+
}
}
diff --git a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php
index 8314b652ce..e1453b4383 100644
--- a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php
+++ b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php
@@ -1,178 +1,178 @@
<?php
final class PhabricatorAuthProviderConfigTransaction
extends PhabricatorApplicationTransaction {
const TYPE_ENABLE = 'config:enable';
const TYPE_LOGIN = 'config:login';
const TYPE_REGISTRATION = 'config:registration';
const TYPE_LINK = 'config:link';
const TYPE_UNLINK = 'config:unlink';
const TYPE_TRUST_EMAILS = 'config:trustEmails';
const TYPE_AUTO_LOGIN = 'config:autoLogin';
const TYPE_PROPERTY = 'config:property';
const PROPERTY_KEY = 'auth:property';
private $provider;
public function setProvider(PhabricatorAuthProvider $provider) {
$this->provider = $provider;
return $this;
}
public function getProvider() {
return $this->provider;
}
public function getApplicationName() {
return 'auth';
}
public function getApplicationTransactionType() {
return PhabricatorAuthAuthProviderPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getIcon() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ENABLE:
if ($new) {
- return 'fa-play';
+ return 'fa-check';
} else {
- return 'fa-pause';
+ return 'fa-ban';
}
}
return parent::getIcon();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ENABLE:
if ($new) {
return 'green';
} else {
- return 'red';
+ return 'indigo';
}
}
return parent::getColor();
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_ENABLE:
if ($old === null) {
return pht(
'%s created this provider.',
$this->renderHandleLink($author_phid));
} else if ($new) {
return pht(
'%s enabled this provider.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled this provider.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_LOGIN:
if ($new) {
return pht(
'%s enabled login.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled login.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_REGISTRATION:
if ($new) {
return pht(
'%s enabled registration.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled registration.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_LINK:
if ($new) {
return pht(
'%s enabled account linking.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled account linking.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_UNLINK:
if ($new) {
return pht(
'%s enabled account unlinking.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled account unlinking.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_TRUST_EMAILS:
if ($new) {
return pht(
'%s enabled email trust.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled email trust.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_AUTO_LOGIN:
if ($new) {
return pht(
'%s enabled auto login.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s disabled auto login.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_PROPERTY:
$provider = $this->getProvider();
if ($provider) {
$title = $provider->renderConfigPropertyTransactionTitle($this);
if (strlen($title)) {
return $title;
}
}
return pht(
'%s edited a property of this provider.',
$this->renderHandleLink($author_phid));
break;
}
return parent::getTitle();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 28, 2:57 AM (16 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
107694
Default Alt Text
(107 KB)

Event Timeline