Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/auth/application/PhabricatorApplicationAuth.php b/src/applications/auth/application/PhabricatorApplicationAuth.php
index 1858b95037..b47da354a4 100644
--- a/src/applications/auth/application/PhabricatorApplicationAuth.php
+++ b/src/applications/auth/application/PhabricatorApplicationAuth.php
@@ -1,89 +1,90 @@
<?php
final class PhabricatorApplicationAuth extends PhabricatorApplication {
public function canUninstall() {
return false;
}
public function getBaseURI() {
return '/auth/';
}
public function getIconName() {
return 'authentication';
}
public function getHelpURI() {
// NOTE: Although reasonable help exists for this in "Configuring Accounts
// and Registration", specifying a help URI 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 null;
}
public function buildMainMenuItems(
PhabricatorUser $user,
PhabricatorController $controller = null) {
$items = array();
if ($user->isLoggedIn()) {
$item = id(new PHUIListItemView())
->addClass('core-menu-item')
->setName(pht('Log Out'))
->setIcon('power')
->setWorkflow(true)
->setHref('/logout/')
->setSelected(($controller instanceof PhabricatorLogoutController));
$items[] = $item;
}
return $items;
}
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>[^/]+)/' => 'PhabricatorAuthLoginController',
'register/(?:(?P<akey>[^/]+)/)?' => 'PhabricatorAuthRegisterController',
'start/' => 'PhabricatorAuthStartController',
'validate/' => 'PhabricatorAuthValidateController',
'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController',
- 'link/(?P<pkey>[^/]+)/' => 'PhabricatorAuthLinkController',
+ '(?P<action>link|refresh)/(?P<pkey>[^/]+)/'
+ => 'PhabricatorAuthLinkController',
'confirmlink/(?P<akey>[^/]+)/'
=> 'PhabricatorAuthConfirmLinkController',
),
'/oauth/(?P<provider>\w+)/login/'
=> 'PhabricatorAuthOldOAuthRedirectController',
'/login/' => array(
'' => 'PhabricatorAuthStartController',
'email/' => 'PhabricatorEmailLoginController',
'etoken/(?P<token>\w+)/' => 'PhabricatorEmailTokenController',
'refresh/' => 'PhabricatorRefreshCSRFController',
'mustverify/' => 'PhabricatorMustVerifyEmailController',
),
'/logout/' => 'PhabricatorLogoutController',
);
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthLinkController.php b/src/applications/auth/controller/PhabricatorAuthLinkController.php
index 20e309337e..cb7d29c0d2 100644
--- a/src/applications/auth/controller/PhabricatorAuthLinkController.php
+++ b/src/applications/auth/controller/PhabricatorAuthLinkController.php
@@ -1,81 +1,138 @@
<?php
final class PhabricatorAuthLinkController
extends PhabricatorAuthController {
+ private $action;
private $providerKey;
public function willProcessRequest(array $data) {
$this->providerKey = $data['pkey'];
+ $this->action = $data['action'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$this->providerKey);
if (!$provider) {
return new Aphront404Response();
}
- if (!$provider->shouldAllowAccountLink()) {
- return $this->renderErrorPage(
- pht('Account Not Linkable'),
- array(
- pht('This provider is not configured to allow linking.'),
- ));
+ switch ($this->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());
- if ($account) {
- return $this->renderErrorPage(
- pht('Account Already Linked'),
- array(
- pht(
- 'Your Phabricator account is already linked to an external '.
- 'account for this provider.'),
- ));
+
+ switch ($this->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/';
$request->setCookie('phcid', Filesystem::readRandomCharacters(16));
- $form = $provider->buildLinkForm($this);
+ switch ($this->action) {
+ case 'link':
+ $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 ($this->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->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Link Account'))
->setHref($panel_uri));
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
- ->setName($provider->getProviderName()));
+ ->setName($provider->getProviderName($name)));
return $this->buildApplicationPage(
array(
$crumbs,
$form,
),
array(
- 'title' => pht('Link %s Account', $provider->getProviderName()),
+ 'title' => $title,
'dust' => true,
'device' => true,
));
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthLoginController.php b/src/applications/auth/controller/PhabricatorAuthLoginController.php
index c94180c999..2e99ac82db 100644
--- a/src/applications/auth/controller/PhabricatorAuthLoginController.php
+++ b/src/applications/auth/controller/PhabricatorAuthLoginController.php
@@ -1,222 +1,223 @@
<?php
final class PhabricatorAuthLoginController
extends PhabricatorAuthController {
private $providerKey;
private $provider;
public function shouldRequireLogin() {
return false;
}
public function willProcessRequest(array $data) {
$this->providerKey = $data['pkey'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$response = $this->loadProvider();
if ($response) {
return $response;
}
$provider = $this->provider;
list($account, $response) = $provider->processLoginRequest($this);
if ($response) {
return $response;
}
if (!$account) {
throw new Exception(
"Auth provider failed to load an account from 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()) {
- return $this->renderError(
- pht(
- 'This external account ("%s") is already linked to your '.
- 'Phabricator account.'));
+ // 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 alerady '.
'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 ($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("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()->setCookie('phreg', $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();
if ($this->getRequest()->getUser()->isLoggedIn()) {
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Link Account'))
->setHref($provider->getSettingsURI()));
} else {
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Login'))
->setHref($this->getApplicationURI('start/')));
}
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($provider->getProviderName()));
return $this->buildApplicationPage(
array(
$crumbs,
$content,
),
array(
'title' => pht('Login'),
'device' => true,
'dust' => true,
));
}
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/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php
index e73cda8e36..4f7e0bad5d 100644
--- a/src/applications/auth/provider/PhabricatorAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorAuthProvider.php
@@ -1,341 +1,350 @@
<?php
abstract class PhabricatorAuthProvider {
private $providerConfig;
public function attachProviderConfig(PhabricatorAuthProviderConfig $config) {
$this->providerConfig = $config;
return $this;
}
public function hasProviderConfig() {
return (bool)$this->providerConfig;
}
public function getProviderConfig() {
if ($this->providerConfig === null) {
throw new Exception(
"Call attachProviderConfig() before getProviderConfig()!");
}
return $this->providerConfig;
}
public function getConfigurationHelp() {
return null;
}
public function getDefaultProviderConfig() {
return id(new PhabricatorAuthProviderConfig())
->setProviderClass(get_class($this))
->setIsEnabled(1)
->setShouldAllowLogin(1)
->setShouldAllowRegistration(1)
->setShouldAllowLink(1)
->setShouldAllowUnlink(1);
}
public function getNameForCreate() {
return $this->getProviderName();
}
public function getDescriptionForCreate() {
return null;
}
public function getProviderKey() {
return $this->getAdapter()->getAdapterKey();
}
public function getProviderType() {
return $this->getAdapter()->getAdapterType();
}
public function getProviderDomain() {
return $this->getAdapter()->getAdapterDomain();
}
public static function getAllBaseProviders() {
static $providers;
if ($providers === null) {
$objects = id(new PhutilSymbolLoader())
->setAncestorClass(__CLASS__)
->loadObjects();
$providers = $objects;
}
return $providers;
}
public static function getAllProviders() {
static $providers;
if ($providers === null) {
$objects = self::getAllBaseProviders();
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->execute();
$providers = array();
foreach ($configs as $config) {
if (!isset($objects[$config->getProviderClass()])) {
// This configuration is for a provider which is not installed.
continue;
}
$object = clone $objects[$config->getProviderClass()];
$object->attachProviderConfig($config);
$key = $object->getProviderKey();
if (isset($providers[$key])) {
throw new Exception(
pht(
"Two authentication providers use the same provider key ".
"('%s'). Each provider must be identified by a unique ".
"key.",
$key));
}
$providers[$key] = $object;
}
}
return $providers;
}
public static function getAllEnabledProviders() {
$providers = self::getAllProviders();
foreach ($providers as $key => $provider) {
if (!$provider->isEnabled()) {
unset($providers[$key]);
}
}
return $providers;
}
public static function getEnabledProviderByKey($provider_key) {
return idx(self::getAllEnabledProviders(), $provider_key);
}
abstract public function getProviderName();
abstract public function getAdapter();
public function isEnabled() {
return $this->getProviderConfig()->getIsEnabled();
}
public function shouldAllowLogin() {
return $this->getProviderConfig()->getShouldAllowLogin();
}
public function shouldAllowRegistration() {
return $this->getProviderConfig()->getShouldAllowRegistration();
}
public function shouldAllowAccountLink() {
return $this->getProviderConfig()->getShouldAllowLink();
}
public function shouldAllowAccountUnlink() {
return $this->getProviderConfig()->getShouldAllowUnlink();
}
public function buildLoginForm(
PhabricatorAuthStartController $controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'start');
}
abstract public function processLoginRequest(
PhabricatorAuthLoginController $controller);
public function buildLinkForm(
PhabricatorAuthLinkController $controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'link');
}
+ public function shouldAllowAccountRefresh() {
+ return true;
+ }
+
+ public function buildRefreshForm(
+ PhabricatorAuthLinkController $controller) {
+ return $this->renderLoginForm($controller->getRequest(), $mode = 'refresh');
+ }
+
protected function renderLoginForm(
AphrontRequest $request,
$mode) {
throw new Exception("Not implemented!");
}
public function createProviders() {
return array($this);
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
return;
}
public function willRegisterAccount(PhabricatorExternalAccount $account) {
return;
}
protected function loadOrCreateAccount($account_id) {
if (!strlen($account_id)) {
throw new Exception(
"loadOrCreateAccount(...): empty account ID!");
}
$adapter = $this->getAdapter();
$adapter_class = get_class($adapter);
if (!strlen($adapter->getAdapterType())) {
throw new Exception(
"AuthAdapter (of class '{$adapter_class}') has an invalid ".
"implementation: no adapter type.");
}
if (!strlen($adapter->getAdapterDomain())) {
throw new Exception(
"AuthAdapter (of class '{$adapter_class}') has an invalid ".
"implementation: no adapter domain.");
}
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
'accountType = %s AND accountDomain = %s AND accountID = %s',
$adapter->getAdapterType(),
$adapter->getAdapterDomain(),
$account_id);
if (!$account) {
$account = id(new PhabricatorExternalAccount())
->setAccountType($adapter->getAdapterType())
->setAccountDomain($adapter->getAdapterDomain())
->setAccountID($account_id);
}
$account->setUsername($adapter->getAccountName());
$account->setRealName($adapter->getAccountRealName());
$account->setEmail($adapter->getAccountEmail());
$account->setAccountURI($adapter->getAccountURI());
$account->setProfileImagePHID(null);
$image_uri = $adapter->getAccountImageURI();
if ($image_uri) {
try {
$name = PhabricatorSlug::normalize($this->getProviderName());
$name = $name.'-profile.jpg';
// TODO: If the image has not changed, we do not need to make a new
// file entry for it, but there's no convenient way to do this with
// PhabricatorFile right now. The storage will get shared, so the impact
// here is negligible.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$image_file = PhabricatorFile::newFromFileDownload(
$image_uri,
array(
'name' => $name,
));
unset($unguarded);
if ($image_file) {
$account->setProfileImagePHID($image_file->getPHID());
}
} catch (Exception $ex) {
// Log this but proceed, it's not especially important that we
// be able to pull profile images.
phlog($ex);
}
}
$this->willSaveAccount($account);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
return $account;
}
public function getLoginURI() {
$app = PhabricatorApplication::getByClass('PhabricatorApplicationAuth');
$uri = $app->getApplicationURI('/login/'.$this->getProviderKey().'/');
return PhabricatorEnv::getURI($uri);
}
public function getSettingsURI() {
return '/settings/panel/external/';
}
public function getStartURI() {
$app = PhabricatorApplication::getByClass('PhabricatorApplicationAuth');
$uri = $app->getApplicationURI('/start/');
return $uri;
}
public function isDefaultRegistrationProvider() {
return false;
}
public function shouldRequireRegistrationPassword() {
return false;
}
public function getDefaultExternalAccount() {
throw new Exception("Not implemented!");
}
public function getLoginOrder() {
return '500-'.$this->getProviderName();
}
protected function getLoginIcon() {
return 'Generic';
}
public function isLoginFormAButton() {
return false;
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
return null;
}
public function readFormValuesFromProvider() {
return array();
}
public function readFormValuesFromRequest(AphrontRequest $request) {
return array();
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
return;
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PhabricatorObjectItemView $item,
PhabricatorExternalAccount $account) {
$account_view = id(new PhabricatorAuthAccountView())
->setExternalAccount($account)
->setAuthProvider($this);
$item->appendChild(
phutil_tag(
'div',
array(
'class' => 'mmr mml mst mmb',
),
$account_view));
}
}
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderLDAP.php b/src/applications/auth/provider/PhabricatorAuthProviderLDAP.php
index 5b6e97b47d..a3f0432937 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderLDAP.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderLDAP.php
@@ -1,396 +1,400 @@
<?php
final class PhabricatorAuthProviderLDAP
extends PhabricatorAuthProvider {
private $adapter;
public function getProviderName() {
return pht('LDAP');
}
public function getDescriptionForCreate() {
return pht(
'Configure a connection to an LDAP server so that users can use their '.
'LDAP credentials to log in to Phabricator.');
}
public function getDefaultProviderConfig() {
return parent::getDefaultProviderConfig()
->setProperty(self::KEY_PORT, 389)
->setProperty(self::KEY_VERSION, 3);
}
public function getAdapter() {
if (!$this->adapter) {
$conf = $this->getProviderConfig();
$adapter = id(new PhutilAuthAdapterLDAP())
->setHostname(
$conf->getProperty(self::KEY_HOSTNAME))
->setPort(
$conf->getProperty(self::KEY_PORT))
->setBaseDistinguishedName(
$conf->getProperty(self::KEY_DISTINGUISHED_NAME))
->setSearchAttribute(
$conf->getProperty(self::KEY_SEARCH_ATTRIBUTE))
->setUsernameAttribute(
$conf->getProperty(self::KEY_USERNAME_ATTRIBUTE))
->setRealNameAttributes(
$conf->getProperty(self::KEY_REALNAME_ATTRIBUTES, array()))
->setLDAPVersion(
$conf->getProperty(self::KEY_VERSION))
->setLDAPReferrals(
$conf->getProperty(self::KEY_REFERRALS))
->setLDAPStartTLS(
$conf->getProperty(self::KEY_START_TLS))
->setAnonymousUsername(
$conf->getProperty(self::KEY_ANONYMOUS_USERNAME))
->setAnonymousPassword(
new PhutilOpaqueEnvelope(
$conf->getProperty(self::KEY_ANONYMOUS_PASSWORD)))
->setSearchFirst(
$conf->getProperty(self::KEY_SEARCH_FIRST))
->setActiveDirectoryDomain(
$conf->getProperty(self::KEY_ACTIVEDIRECTORY_DOMAIN));
$this->adapter = $adapter;
}
return $this->adapter;
}
protected function renderLoginForm(AphrontRequest $request, $mode) {
$viewer = $request->getUser();
$dialog = id(new AphrontDialogView())
->setSubmitURI($this->getLoginURI())
->setUser($viewer);
if ($mode == 'link') {
$dialog->setTitle(pht('Link LDAP Account'));
$dialog->addSubmitButton(pht('Link Accounts'));
$dialog->addCancelButton($this->getSettingsURI());
+ } else if ($mode == 'refresh') {
+ $dialog->setTitle(pht('Refresh LDAP Account'));
+ $dialog->addSubmitButton(pht('Refresh Account'));
+ $dialog->addCancelButton($this->getSettingsURI());
} else {
if ($this->shouldAllowRegistration()) {
$dialog->setTitle(pht('Login or Register with LDAP'));
$dialog->addSubmitButton(pht('Login or Register'));
} else {
$dialog->setTitle(pht('Login with LDAP'));
$dialog->addSubmitButton(pht('Login'));
}
if ($mode == 'login') {
$dialog->addCancelButton($this->getStartURI());
}
}
$v_user = $request->getStr('ldap_username');
$e_user = null;
$e_pass = null;
$errors = array();
if ($request->isHTTPPost()) {
// NOTE: This is intentionally vague so as not to disclose whether a
// given username exists.
$e_user = pht('Invalid');
$e_pass = pht('Invalid');
$errors[] = pht('Username or password are incorrect.');
}
$form = id(new AphrontFormLayoutView())
->setUser($viewer)
->setFullWidth(true)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('LDAP Username')
->setName('ldap_username')
->setValue($v_user)
->setError($e_user))
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel('LDAP Password')
->setName('ldap_password')
->setError($e_pass));
if ($errors) {
$errors = id(new AphrontErrorView())->setErrors($errors);
}
$dialog->appendChild($errors);
$dialog->appendChild($form);
return $dialog;
}
public function processLoginRequest(
PhabricatorAuthLoginController $controller) {
$request = $controller->getRequest();
$viewer = $request->getUser();
$response = null;
$account = null;
$username = $request->getStr('ldap_username');
$password = $request->getStr('ldap_password');
$has_password = strlen($password);
$password = new PhutilOpaqueEnvelope($password);
if (!strlen($username) || !$has_password) {
$response = $controller->buildProviderPageResponse(
$this,
$this->renderLoginForm($request, 'login'));
return array($account, $response);
}
try {
if (strlen($username) && $has_password) {
$adapter = $this->getAdapter();
$adapter->setLoginUsername($username);
$adapter->setLoginPassword($password);
// TODO: This calls ldap_bind() eventually, which dumps cleartext
// passwords to the error log. See note in PhutilAuthAdapterLDAP.
// See T3351.
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
$account_id = $adapter->getAccountID();
DarkConsoleErrorLogPluginAPI::disableDiscardMode();
} else {
throw new Exception("Username and password are required!");
}
} catch (Exception $ex) {
// TODO: Make this cleaner.
throw $ex;
}
return array($this->loadOrCreateAccount($account_id), $response);
}
const KEY_HOSTNAME = 'ldap:host';
const KEY_PORT = 'ldap:port';
const KEY_DISTINGUISHED_NAME = 'ldap:dn';
const KEY_SEARCH_ATTRIBUTE = 'ldap:search-attribute';
const KEY_USERNAME_ATTRIBUTE = 'ldap:username-attribute';
const KEY_REALNAME_ATTRIBUTES = 'ldap:realname-attributes';
const KEY_VERSION = 'ldap:version';
const KEY_REFERRALS = 'ldap:referrals';
const KEY_START_TLS = 'ldap:start-tls';
const KEY_ANONYMOUS_USERNAME = 'ldap:anoynmous-username';
const KEY_ANONYMOUS_PASSWORD = 'ldap:anonymous-password';
const KEY_SEARCH_FIRST = 'ldap:search-first';
const KEY_ACTIVEDIRECTORY_DOMAIN = 'ldap:activedirectory-domain';
private function getPropertyKeys() {
return array(
self::KEY_HOSTNAME,
self::KEY_PORT,
self::KEY_DISTINGUISHED_NAME,
self::KEY_SEARCH_ATTRIBUTE,
self::KEY_USERNAME_ATTRIBUTE,
self::KEY_VERSION,
self::KEY_REFERRALS,
self::KEY_START_TLS,
self::KEY_ANONYMOUS_USERNAME,
self::KEY_ANONYMOUS_PASSWORD,
self::KEY_SEARCH_FIRST,
self::KEY_ACTIVEDIRECTORY_DOMAIN,
);
}
private function getPropertyLabels() {
return array(
self::KEY_HOSTNAME => pht('LDAP Hostname'),
self::KEY_PORT => pht('LDAP Port'),
self::KEY_DISTINGUISHED_NAME => pht('Base Distinguished Name'),
self::KEY_SEARCH_ATTRIBUTE => pht('Search Attribute'),
self::KEY_USERNAME_ATTRIBUTE => pht('Username Attribute'),
self::KEY_REALNAME_ATTRIBUTES => pht('Realname Attributes'),
self::KEY_VERSION => pht('LDAP Version'),
self::KEY_REFERRALS => pht('Enable Referrals'),
self::KEY_START_TLS => pht('Use TLS'),
self::KEY_SEARCH_FIRST => pht('Search First'),
self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'),
self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'),
self::KEY_ACTIVEDIRECTORY_DOMAIN => pht('ActiveDirectory Domain'),
);
}
public function readFormValuesFromProvider() {
$properties = array();
foreach ($this->getPropertyLabels() as $key => $ignored) {
$properties[$key] = $this->getProviderConfig()->getProperty($key);
}
return $properties;
}
public function readFormValuesFromRequest(AphrontRequest $request) {
$values = array();
foreach ($this->getPropertyKeys() as $key) {
switch ($key) {
case self::KEY_REALNAME_ATTRIBUTES:
$values[$key] = $request->getStrList($key, array());
break;
default:
$values[$key] = $request->getStr($key);
break;
}
}
return $values;
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
$labels = $this->getPropertyLabels();
$captions = array(
self::KEY_HOSTNAME =>
pht('Example: %s',
hsprintf('<tt>%s</tt>', pht('ldap.example.com'))),
self::KEY_DISTINGUISHED_NAME =>
pht('Example: %s',
hsprintf('<tt>%s</tt>', pht('ou=People, dc=example, dc=com'))),
self::KEY_SEARCH_ATTRIBUTE =>
pht('Example: %s',
hsprintf('<tt>%s</tt>', pht('sn'))),
self::KEY_USERNAME_ATTRIBUTE =>
pht('Optional, if different from search attribute.'),
self::KEY_REALNAME_ATTRIBUTES =>
pht('Optional. Example: %s',
hsprintf('<tt>%s</tt>', pht('firstname, lastname'))),
self::KEY_REFERRALS =>
pht('Follow referrals. Disable this for Windows AD 2003.'),
self::KEY_START_TLS =>
pht('Start TLS after binding to the LDAP server.'),
self::KEY_SEARCH_FIRST =>
pht(
'When the user enters their username, search for a matching '.
'record using the "Search Attribute", then try to bind using '.
'the DN for the record. This is useful if usernames are not '.
'part of the record DN.'),
self::KEY_ANONYMOUS_USERNAME =>
pht('Username to bind with before searching.'),
self::KEY_ANONYMOUS_PASSWORD =>
pht('Password to bind with before searching.'),
);
$types = array(
self::KEY_REFERRALS => 'checkbox',
self::KEY_START_TLS => 'checkbox',
self::KEY_SEARCH_FIRST => 'checkbox',
self::KEY_REALNAME_ATTRIBUTES => 'list',
self::KEY_ANONYMOUS_PASSWORD => 'password',
);
foreach ($labels as $key => $label) {
$caption = idx($captions, $key);
$type = idx($types, $key);
$value = idx($values, $key);
$control = null;
switch ($type) {
case 'checkbox':
$control = id(new AphrontFormCheckboxControl())
->addCheckbox(
$key,
1,
hsprintf('<strong>%s:</strong> %s', $label, $caption),
$value);
break;
case 'list':
$control = id(new AphrontFormTextControl())
->setName($key)
->setLabel($label)
->setCaption($caption)
->setValue($value ? implode(', ', $value) : null);
break;
case 'password':
$control = id(new AphrontFormPasswordControl())
->setName($key)
->setLabel($label)
->setCaption($caption)
->setValue($value);
break;
default:
$control = id(new AphrontFormTextControl())
->setName($key)
->setLabel($label)
->setCaption($caption)
->setValue($value);
break;
}
$form->appendChild($control);
}
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$key = $xaction->getMetadataValue(
PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
$labels = $this->getPropertyLabels();
if (isset($labels[$key])) {
$label = $labels[$key];
$mask = false;
switch ($key) {
case self::KEY_ANONYMOUS_PASSWORD:
$mask = true;
break;
}
if ($mask) {
return pht(
'%s updated the "%s" value.',
$xaction->renderHandleLink($author_phid),
$label);
}
if (!strlen($old)) {
return pht(
'%s set the "%s" value to "%s".',
$xaction->renderHandleLink($author_phid),
$label,
$new);
} else {
return pht(
'%s changed the "%s" value from "%s" to "%s".',
$xaction->renderHandleLink($author_phid),
$label,
$old,
$new);
}
}
return parent::renderConfigPropertyTransactionTitle($xaction);
}
public static function getLDAPProvider() {
$providers = self::getAllEnabledProviders();
foreach ($providers as $provider) {
if ($provider instanceof PhabricatorAuthProviderLDAP) {
return $provider;
}
}
return null;
}
}
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
index 97c2687edb..7c9ed7edac 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
@@ -1,376 +1,378 @@
<?php
abstract class PhabricatorAuthProviderOAuth extends PhabricatorAuthProvider {
protected $adapter;
abstract protected function newOAuthAdapter();
public function getDescriptionForCreate() {
return pht('Configure %s OAuth.', $this->getProviderName());
}
public function getAdapter() {
if (!$this->adapter) {
$adapter = $this->newOAuthAdapter();
$this->adapter = $adapter;
$this->configureAdapter($adapter);
}
return $this->adapter;
}
protected function configureAdapter(PhutilAuthAdapterOAuth $adapter) {
$config = $this->getProviderConfig();
$adapter->setClientID($config->getProperty(self::PROPERTY_APP_ID));
$adapter->setClientSecret(
new PhutilOpaqueEnvelope(
$config->getProperty(self::PROPERTY_APP_SECRET)));
$adapter->setRedirectURI($this->getLoginURI());
return $adapter;
}
public function isLoginFormAButton() {
return true;
}
protected function renderLoginForm(AphrontRequest $request, $mode) {
$viewer = $request->getUser();
if ($mode == 'link') {
$button_text = pht('Link External Account');
+ } else if ($mode == 'refresh') {
+ $button_text = pht('Refresh Account Link');
} else if ($this->shouldAllowRegistration()) {
$button_text = pht('Login or Register');
} else {
$button_text = pht('Login');
}
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
->setSpriteIcon($this->getLoginIcon());
$button = id(new PHUIButtonView())
->setSize(PHUIButtonView::BIG)
->setColor(PHUIButtonView::GREY)
->setIcon($icon)
->setText($button_text)
->setSubtext($this->getProviderName());
$adapter = $this->getAdapter();
$adapter->setState(PhabricatorHash::digest($request->getCookie('phcid')));
$uri = new PhutilURI($adapter->getAuthenticateURI());
$params = $uri->getQueryParams();
$uri->setQueryParams(array());
$content = array($button);
foreach ($params as $key => $value) {
$content[] = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => $key,
'value' => $value,
));
}
return phabricator_form(
$viewer,
array(
'method' => 'GET',
'action' => (string)$uri,
),
$content);
}
public function processLoginRequest(
PhabricatorAuthLoginController $controller) {
$request = $controller->getRequest();
$adapter = $this->getAdapter();
$account = null;
$response = null;
$error = $request->getStr('error');
if ($error) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider returned an error: %s',
$error));
return array($account, $response);
}
$code = $request->getStr('code');
if (!strlen($code)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider did not return a "code" parameter in its '.
'response.'));
return array($account, $response);
}
if ($adapter->supportsStateParameter()) {
$phcid = $request->getCookie('phcid');
if (!strlen($phcid)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'Your browser did not submit a "phcid" cookie with OAuth state '.
'information in the request. Check that cookies are enabled. '.
'If this problem persists, you may need to clear your cookies.'));
}
$state = $request->getStr('state');
$expect = PhabricatorHash::digest($phcid);
if ($state !== $expect) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider did not return the correct "state" parameter '.
'in its response. If this problem persists, you may need to clear '.
'your cookies.'));
}
}
$adapter->setCode($code);
// NOTE: As a side effect, this will cause the OAuth adapter to request
// an access token.
try {
$account_id = $adapter->getAccountID();
} catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way.
throw $ex;
}
if (!strlen($account_id)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider failed to retrieve an account ID.'));
return array($account, $response);
}
return array($this->loadOrCreateAccount($account_id), $response);
}
const PROPERTY_APP_ID = 'oauth:app:id';
const PROPERTY_APP_SECRET = 'oauth:app:secret';
public function readFormValuesFromProvider() {
$config = $this->getProviderConfig();
$id = $config->getProperty(self::PROPERTY_APP_ID);
$secret = $config->getProperty(self::PROPERTY_APP_SECRET);
return array(
self::PROPERTY_APP_ID => $id,
self::PROPERTY_APP_SECRET => $secret,
);
}
public function readFormValuesFromRequest(AphrontRequest $request) {
return array(
self::PROPERTY_APP_ID => $request->getStr(self::PROPERTY_APP_ID),
self::PROPERTY_APP_SECRET => $request->getStr(self::PROPERTY_APP_SECRET),
);
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
$key_id = self::PROPERTY_APP_ID;
$key_secret = self::PROPERTY_APP_SECRET;
if (!strlen($values[$key_id])) {
$errors[] = pht('Application ID is required.');
$issues[$key_id] = pht('Required');
}
if (!strlen($values[$key_secret])) {
$errors[] = pht('Application secret is required.');
$issues[$key_secret] = pht('Required');
}
// If the user has not changed the secret, don't update it (that is,
// don't cause a bunch of "****" to be written to the database).
if (preg_match('/^[*]+$/', $values[$key_secret])) {
unset($values[$key_secret]);
}
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
$key_id = self::PROPERTY_APP_ID;
$key_secret = self::PROPERTY_APP_SECRET;
$v_id = $values[$key_id];
$v_secret = $values[$key_secret];
if ($v_secret) {
$v_secret = str_repeat('*', strlen($v_secret));
}
$e_id = idx($issues, $key_id, $request->isFormPost() ? null : true);
$e_secret = idx($issues, $key_secret, $request->isFormPost() ? null : true);
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('OAuth App ID'))
->setName($key_id)
->setValue($v_id)
->setError($e_id))
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('OAuth App Secret'))
->setName($key_secret)
->setValue($v_secret)
->setError($e_secret));
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$key = $xaction->getMetadataValue(
PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
switch ($key) {
case self::PROPERTY_APP_ID:
if (strlen($old)) {
return pht(
'%s updated the OAuth application ID for this provider from '.
'"%s" to "%s".',
$xaction->renderHandleLink($author_phid),
$old,
$new);
} else {
return pht(
'%s set the OAuth application ID for this provider to '.
'"%s".',
$xaction->renderHandleLink($author_phid),
$new);
}
case self::PROPERTY_APP_SECRET:
if (strlen($old)) {
return pht(
'%s updated the OAuth application secret for this provider.',
$xaction->renderHandleLink($author_phid));
} else {
return pht(
'%s set the OAuth application seceret for this provider.',
$xaction->renderHandleLink($author_phid));
}
}
return parent::renderConfigPropertyTransactionTitle($xaction);
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
parent::willSaveAccount($account);
$this->synchronizeOAuthAccount($account);
}
protected function synchronizeOAuthAccount(
PhabricatorExternalAccount $account) {
$adapter = $this->getAdapter();
$oauth_token = $adapter->getAccessToken();
$account->setProperty('oauth.token.access', $oauth_token);
if ($adapter->supportsTokenRefresh()) {
$refresh_token = $adapter->getRefreshToken();
$account->setProperty('oauth.token.refresh', $refresh_token);
} else {
$account->setProperty('oauth.token.refresh', null);
}
$expires = $adapter->getAccessTokenExpires();
$account->setProperty('oauth.token.access.expires', $expires);
}
public function getOAuthAccessToken(
PhabricatorExternalAccount $account,
$force_refresh = false) {
if ($account->getProviderKey() !== $this->getProviderKey()) {
throw new Exception("Account does not match provider!");
}
if (!$force_refresh) {
$access_expires = $account->getProperty('oauth.token.access.expires');
$access_token = $account->getProperty('oauth.token.access');
// Don't return a token with fewer than this many seconds remaining until
// it expires.
$shortest_token = 60;
if ($access_token) {
if ($access_expires > (time() + $shortest_token)) {
return $access_token;
}
}
}
$refresh_token = $account->getProperty('oauth.token.refresh');
if ($refresh_token) {
$adapter = $this->getAdapter();
if ($adapter->supportsTokenRefresh()) {
$adapter->refreshAccessToken($refresh_token);
$this->synchronizeOAuthAccount($account);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
}
}
return null;
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PhabricatorObjectItemView $item,
PhabricatorExternalAccount $account) {
// Get a valid token, possibly refreshing it.
$oauth_token = $this->getOAuthAccessToken($account);
$item->addAttribute(pht('OAuth2 Account'));
if ($oauth_token) {
$oauth_expires = $account->getProperty('oauth.token.access.expires');
if ($oauth_expires) {
$item->addAttribute(
pht(
'Active OAuth Token (Expires: %s)',
phabricator_datetime($oauth_expires, $viewer)));
} else {
$item->addAttribute(
pht(
'Active OAuth Token'));
}
} else {
$item->addAttribute(pht('No OAuth Access Token'));
}
parent::willRenderLinkedAccount($viewer, $item, $account);
}
}
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderPassword.php b/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
index e5a40752b0..c56f8ea26f 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderPassword.php
@@ -1,249 +1,253 @@
<?php
final class PhabricatorAuthProviderPassword
extends PhabricatorAuthProvider {
private $adapter;
public function getProviderName() {
return pht('Username/Password');
}
public function getConfigurationHelp() {
return pht(
'You can select a minimum password length by setting '.
'`account.minimum-password-length` in configuration.');
}
public function getDescriptionForCreate() {
return pht(
'Allow users to login or register using a username and password.');
}
public function getAdapter() {
if (!$this->adapter) {
$adapter = new PhutilAuthAdapterEmpty();
$adapter->setAdapterType('password');
$adapter->setAdapterDomain('self');
$this->adapter = $adapter;
}
return $this->adapter;
}
public function getLoginOrder() {
// Make sure username/password appears first if it is enabled.
return '100-'.$this->getProviderName();
}
public function shouldAllowAccountLink() {
return false;
}
public function shouldAllowAccountUnlink() {
return false;
}
public function isDefaultRegistrationProvider() {
return true;
}
public function buildLoginForm(
PhabricatorAuthStartController $controller) {
$request = $controller->getRequest();
return $this->renderPasswordLoginForm($request);
}
public function buildLinkForm(
PhabricatorAuthLinkController $controller) {
throw new Exception("Password providers can't be linked.");
}
private function renderPasswordLoginForm(
AphrontRequest $request,
$require_captcha = false,
$captcha_valid = false) {
$viewer = $request->getUser();
$dialog = id(new AphrontDialogView())
->setSubmitURI($this->getLoginURI())
->setUser($viewer)
->setTitle(pht('Login to Phabricator'))
->addSubmitButton(pht('Login'));
if ($this->shouldAllowRegistration()) {
$dialog->addCancelButton(
'/auth/register/',
pht('Register New Account'));
}
$dialog->addFooter(
phutil_tag(
'a',
array(
'href' => '/login/email/',
),
pht('Forgot your password?')));
$v_user = nonempty(
$request->getStr('username'),
$request->getCookie('phusr'));
$e_user = null;
$e_pass = null;
$e_captcha = null;
$errors = array();
if ($require_captcha && !$captcha_valid) {
if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) {
$e_captcha = pht('Invalid');
$errors[] = pht('CAPTCHA was not entered correctly.');
} else {
$e_captcha = pht('Required');
$errors[] = pht('Too many login failures recently. You must '.
'submit a CAPTCHA with your login request.');
}
} else if ($request->isHTTPPost()) {
// NOTE: This is intentionally vague so as not to disclose whether a
// given username or email is registered.
$e_user = pht('Invalid');
$e_pass = pht('Invalid');
$errors[] = pht('Username or password are incorrect.');
}
if ($errors) {
$errors = id(new AphrontErrorView())->setErrors($errors);
}
$form = id(new AphrontFormLayoutView())
->setFullWidth(true)
->appendChild($errors)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Username or Email')
->setName('username')
->setValue($v_user)
->setError($e_user))
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel('Password')
->setName('password')
->setError($e_pass));
if ($require_captcha) {
$form->appendChild(
id(new AphrontFormRecaptchaControl())
->setError($e_captcha));
}
$dialog->appendChild($form);
return $dialog;
}
public function processLoginRequest(
PhabricatorAuthLoginController $controller) {
$request = $controller->getRequest();
$viewer = $request->getUser();
$require_captcha = false;
$captcha_valid = false;
if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
$failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
PhabricatorUserLog::ACTION_LOGIN_FAILURE,
60 * 15);
if (count($failed_attempts) > 5) {
$require_captcha = true;
$captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request);
}
}
$response = null;
$account = null;
$log_user = null;
if (!$require_captcha || $captcha_valid) {
$username_or_email = $request->getStr('username');
if (strlen($username_or_email)) {
$user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$username_or_email);
if (!$user) {
$user = PhabricatorUser::loadOneWithEmailAddress($username_or_email);
}
if ($user) {
$envelope = new PhutilOpaqueEnvelope($request->getStr('password'));
if ($user->comparePassword($envelope)) {
$account = $this->loadOrCreateAccount($user->getPHID());
$log_user = $user;
}
}
}
}
if (!$account) {
$log = PhabricatorUserLog::newLog(
null,
$log_user,
PhabricatorUserLog::ACTION_LOGIN_FAILURE);
$log->save();
$request->clearCookie('phusr');
$request->clearCookie('phsid');
$response = $controller->buildProviderPageResponse(
$this,
$this->renderPasswordLoginForm(
$request,
$require_captcha,
$captcha_valid));
}
return array($account, $response);
}
public function shouldRequireRegistrationPassword() {
return true;
}
public function getDefaultExternalAccount() {
$adapter = $this->getAdapter();
return id(new PhabricatorExternalAccount())
->setAccountType($adapter->getAdapterType())
->setAccountDomain($adapter->getAdapterDomain());
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
parent::willSaveAccount($account);
$account->setUserPHID($account->getAccountID());
}
public function willRegisterAccount(PhabricatorExternalAccount $account) {
parent::willRegisterAccount($account);
$account->setAccountID($account->getUserPHID());
}
public static function getPasswordProvider() {
$providers = self::getAllEnabledProviders();
foreach ($providers as $provider) {
if ($provider instanceof PhabricatorAuthProviderPassword) {
return $provider;
}
}
return null;
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PhabricatorObjectItemView $item,
PhabricatorExternalAccount $account) {
return;
}
+ public function shouldAllowAccountRefresh() {
+ return false;
+ }
+
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php b/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
index 755fb1edd6..86278c275e 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanelExternalAccounts.php
@@ -1,129 +1,137 @@
<?php
final class PhabricatorSettingsPanelExternalAccounts
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'external';
}
public function getPanelName() {
return pht('External Accounts');
}
public function getPanelGroup() {
return pht('Authentication');
}
public function isEnabled() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$providers = PhabricatorAuthProvider::getAllProviders();
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->needImages(true)
->execute();
$linked_head = id(new PhabricatorHeaderView())
->setHeader(pht('Linked Accounts and Authentication'));
$linked = id(new PhabricatorObjectItemListView())
->setUser($viewer)
->setNoDataString(pht('You have no linked accounts.'));
$login_accounts = 0;
foreach ($accounts as $account) {
if ($account->isUsableForLogin()) {
$login_accounts++;
}
}
foreach ($accounts as $account) {
$item = id(new PhabricatorObjectItemView());
$provider = idx($providers, $account->getProviderKey());
if ($provider) {
$item->setHeader($provider->getProviderName());
$can_unlink = $provider->shouldAllowAccountUnlink();
if (!$can_unlink) {
$item->addAttribute(pht('Permanently Linked'));
}
} else {
$item->setHeader(
pht('Unknown Account ("%s")', $account->getProviderKey()));
$can_unlink = true;
}
$can_login = $account->isUsableForLogin();
if (!$can_login) {
$item->addAttribute(
pht(
'Disabled (an administrator has disabled login for this '.
'account provider).'));
}
$can_unlink = $can_unlink && (!$can_login || ($login_accounts > 1));
+ $can_refresh = $provider && $provider->shouldAllowAccountRefresh();
+ if ($can_refresh) {
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('refresh')
+ ->setHref('/auth/refresh/'.$account->getProviderKey().'/'));
+ }
+
$item->addAction(
id(new PHUIListItemView())
->setIcon('delete')
->setWorkflow(true)
->setDisabled(!$can_unlink)
->setHref('/auth/unlink/'.$account->getProviderKey().'/'));
if ($provider) {
$provider->willRenderLinkedAccount($viewer, $item, $account);
}
$linked->addItem($item);
}
$linkable_head = id(new PhabricatorHeaderView())
->setHeader(pht('Add External Account'));
$linkable = id(new PhabricatorObjectItemListView())
->setUser($viewer)
->setNoDataString(
pht('Your account is linked with all available providers.'));
$accounts = mpull($accounts, null, 'getProviderKey');
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
$providers = msort($providers, 'getProviderName');
foreach ($providers as $key => $provider) {
if (isset($accounts[$key])) {
continue;
}
if (!$provider->shouldAllowAccountLink()) {
continue;
}
$link_uri = '/auth/link/'.$provider->getProviderKey().'/';
$item = id(new PhabricatorObjectItemView());
$item->setHeader($provider->getProviderName());
$item->setHref($link_uri);
$item->addAction(
id(new PHUIListItemView())
->setIcon('link')
->setHref($link_uri));
$linkable->addItem($item);
}
return array(
$linked_head,
$linked,
$linkable_head,
$linkable,
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 15, 8:27 AM (17 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
337721
Default Alt Text
(61 KB)

Event Timeline