Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2893858
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
59 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php
index f5699295a7..c663f80fc1 100644
--- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php
@@ -1,476 +1,477 @@
<?php
final class PhabricatorLDAPAuthProvider 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();
$realname_attributes = $conf->getProperty(self::KEY_REALNAME_ATTRIBUTES);
if (!is_array($realname_attributes)) {
$realname_attributes = array();
}
$search_attributes = $conf->getProperty(self::KEY_SEARCH_ATTRIBUTES);
$search_attributes = phutil_split_lines($search_attributes, false);
$search_attributes = array_filter($search_attributes);
$adapter = id(new PhutilLDAPAuthAdapter())
->setHostname(
$conf->getProperty(self::KEY_HOSTNAME))
->setPort(
$conf->getProperty(self::KEY_PORT))
->setBaseDistinguishedName(
$conf->getProperty(self::KEY_DISTINGUISHED_NAME))
->setSearchAttributes($search_attributes)
->setUsernameAttribute(
$conf->getProperty(self::KEY_USERNAME_ATTRIBUTE))
->setRealNameAttributes($realname_attributes)
->setLDAPVersion(
$conf->getProperty(self::KEY_VERSION))
->setLDAPReferrals(
$conf->getProperty(self::KEY_REFERRALS))
->setLDAPStartTLS(
$conf->getProperty(self::KEY_START_TLS))
->setAlwaysSearch($conf->getProperty(self::KEY_ALWAYS_SEARCH))
->setAnonymousUsername(
$conf->getProperty(self::KEY_ANONYMOUS_USERNAME))
->setAnonymousPassword(
new PhutilOpaqueEnvelope(
$conf->getProperty(self::KEY_ANONYMOUS_PASSWORD)))
->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 PHUIFormLayoutView())
->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);
}
if ($request->isFormPost()) {
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 PhutilLDAPAuthAdapter.
// See T3351.
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
$account_id = $adapter->getAccountID();
DarkConsoleErrorLogPluginAPI::disableDiscardMode();
} else {
throw new Exception('Username and password are required!');
}
} catch (PhutilAuthCredentialException $ex) {
$response = $controller->buildProviderPageResponse(
$this,
$this->renderLoginForm($request, 'login'));
return array($account, $response);
} 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_ATTRIBUTES = '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_ALWAYS_SEARCH = 'ldap:always-search';
const KEY_ACTIVEDIRECTORY_DOMAIN = 'ldap:activedirectory-domain';
private function getPropertyKeys() {
return array_keys($this->getPropertyLabels());
}
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_ATTRIBUTES => pht('Search Attributes'),
self::KEY_ALWAYS_SEARCH => pht('Always Search'),
self::KEY_ANONYMOUS_USERNAME => pht('Anonymous Username'),
self::KEY_ANONYMOUS_PASSWORD => pht('Anonymous Password'),
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_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%sFor LDAPS, use: %s',
phutil_tag('tt', array(), pht('ldap.example.com')),
phutil_tag('br'),
phutil_tag('tt', array(), pht('ldaps://ldaps.example.com/'))),
self::KEY_DISTINGUISHED_NAME =>
pht('Example: %s',
phutil_tag('tt', array(), pht('ou=People, dc=example, dc=com'))),
self::KEY_USERNAME_ATTRIBUTE =>
pht('Example: %s',
phutil_tag('tt', array(), pht('sn'))),
self::KEY_REALNAME_ATTRIBUTES =>
pht('Example: %s',
phutil_tag('tt', array(), 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_ALWAYS_SEARCH =>
pht('Always bind and search, even without a username and password.'),
);
$types = array(
self::KEY_REFERRALS => 'checkbox',
self::KEY_START_TLS => 'checkbox',
self::KEY_SEARCH_ATTRIBUTES => 'textarea',
self::KEY_REALNAME_ATTRIBUTES => 'list',
self::KEY_ANONYMOUS_PASSWORD => 'password',
self::KEY_ALWAYS_SEARCH => 'checkbox',
);
$instructions = array(
self::KEY_SEARCH_ATTRIBUTES => pht(
"When a user types their LDAP username and password into Phabricator, ".
"Phabricator can either bind to LDAP with those credentials directly ".
"(which is simpler, but not as powerful) or bind to LDAP with ".
"anonymous credentials, then search for record matching the supplied ".
"credentials (which is more complicated, but more powerful).\n\n".
"For many installs, direct binding is sufficient. However, you may ".
"want to search first if:\n\n".
" - You want users to be able to login with either their username ".
" or their email address.\n".
" - The login/username is not part of the distinguished name in ".
" your LDAP records.\n".
" - You want to restrict logins to a subset of users (like only ".
" those in certain departments).\n".
" - Your LDAP server is configured in some other way that prevents ".
" direct binding from working correctly.\n\n".
"**To bind directly**, enter the LDAP attribute corresponding to the ".
"login name into the **Search Attributes** box below. Often, this is ".
"something like `sn` or `uid`. This is the simplest configuration, ".
"but will only work if the username is part of the distinguished ".
"name, and won't let you apply complex restrictions to logins.\n\n".
" lang=text,name=Simple Direct Binding\n".
" sn\n\n".
"**To search first**, provide an anonymous username and password ".
"below (or check the **Always Search** checkbox), then enter one ".
"or more search queries into this field, one per line. ".
"After binding, these queries will be used to identify the ".
"record associated with the login name the user typed.\n\n".
"Searches will be tried in order until a matching record is found. ".
"Each query can be a simple attribute name (like `sn` or `mail`), ".
"which will search for a matching record, or it can be a complex ".
"query that uses the string `\${login}` to represent the login ".
"name.\n\n".
"A common simple configuration is just an attribute name, like ".
"`sn`, which will work the same way direct binding works:\n\n".
" lang=text,name=Simple Example\n".
" sn\n\n".
"A slightly more complex configuration might let the user login with ".
"either their login name or email address:\n\n".
" lang=text,name=Match Several Attributes\n".
" mail\n".
" sn\n\n".
"If your LDAP directory is more complex, or you want to perform ".
"sophisticated filtering, you can use more complex queries. Depending ".
"on your directory structure, this example might allow users to login ".
"with either their email address or username, but only if they're in ".
"specific departments:\n\n".
" lang=text,name=Complex Example\n".
" (&(mail=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n".
" (&(sn=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n\n".
"All of the attribute names used here are just examples: your LDAP ".
"server may use different attribute names."),
self::KEY_ALWAYS_SEARCH => pht(
'To search for an LDAP record before authenticating, either check '.
'the **Always Search** checkbox or enter an anonymous '.
'username and password to use to perform the search.'),
self::KEY_USERNAME_ATTRIBUTE => pht(
'Optionally, specify a username attribute to use to prefill usernames '.
'when registering a new account. This is purely cosmetic and does not '.
'affect the login process, but you can configure it to make sure '.
'users get the same default username as their LDAP username, so '.
'usernames remain consistent across systems.'),
self::KEY_REALNAME_ATTRIBUTES => pht(
'Optionally, specify one or more comma-separated attributes to use to '.
'prefill the "Real Name" field when registering a new account. This '.
'is purely cosmetic and does not affect the login process, but can '.
'make registration a little easier.'),
);
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)
+ ->setDisableAutocomplete(true)
->setValue($value);
break;
case 'textarea':
$control = id(new AphrontFormTextAreaControl())
->setName($key)
->setLabel($label)
->setCaption($caption)
->setValue($value);
break;
default:
$control = id(new AphrontFormTextControl())
->setName($key)
->setLabel($label)
->setCaption($caption)
->setValue($value);
break;
}
$instruction_text = idx($instructions, $key);
if (strlen($instruction_text)) {
$form->appendRemarkupInstructions($instruction_text);
}
$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 ($old === null || $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 PhabricatorLDAPAuthProvider) {
return $provider;
}
}
return null;
}
}
diff --git a/src/applications/auth/provider/PhabricatorOAuthAuthProvider.php b/src/applications/auth/provider/PhabricatorOAuthAuthProvider.php
index d2cc062a5b..8f7eae8aac 100644
--- a/src/applications/auth/provider/PhabricatorOAuthAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorOAuthAuthProvider.php
@@ -1,168 +1,169 @@
<?php
abstract class PhabricatorOAuthAuthProvider extends PhabricatorAuthProvider {
const PROPERTY_NOTE = 'oauth:app:note';
protected $adapter;
abstract protected function newOAuthAdapter();
abstract protected function getIDKey();
abstract protected function getSecretKey();
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;
}
public function isLoginFormAButton() {
return true;
}
public function readFormValuesFromProvider() {
$config = $this->getProviderConfig();
$id = $config->getProperty($this->getIDKey());
$secret = $config->getProperty($this->getSecretKey());
$note = $config->getProperty(self::PROPERTY_NOTE);
return array(
$this->getIDKey() => $id,
$this->getSecretKey() => $secret,
self::PROPERTY_NOTE => $note,
);
}
public function readFormValuesFromRequest(AphrontRequest $request) {
return array(
$this->getIDKey() => $request->getStr($this->getIDKey()),
$this->getSecretKey() => $request->getStr($this->getSecretKey()),
self::PROPERTY_NOTE => $request->getStr(self::PROPERTY_NOTE),
);
}
protected function processOAuthEditForm(
AphrontRequest $request,
array $values,
$id_error,
$secret_error) {
$errors = array();
$issues = array();
$key_id = $this->getIDKey();
$key_secret = $this->getSecretKey();
if (!strlen($values[$key_id])) {
$errors[] = $id_error;
$issues[$key_id] = pht('Required');
}
if (!strlen($values[$key_secret])) {
$errors[] = $secret_error;
$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 getConfigurationHelp() {
$help = $this->getProviderConfigurationHelp();
return $help."\n\n".
pht('Use the **OAuth App Notes** field to record details about which '.
'account the external application is registered under.');
}
abstract protected function getProviderConfigurationHelp();
protected function extendOAuthEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues,
$id_label,
$secret_label) {
$key_id = $this->getIDKey();
$key_secret = $this->getSecretKey();
$key_note = self::PROPERTY_NOTE;
$v_id = $values[$key_id];
$v_secret = $values[$key_secret];
if ($v_secret) {
$v_secret = str_repeat('*', strlen($v_secret));
}
$v_note = $values[$key_note];
$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($id_label)
+ ->setLabel($id_label)
->setName($key_id)
->setValue($v_id)
->setError($e_id))
->appendChild(
id(new AphrontFormPasswordControl())
- ->setLabel($secret_label)
+ ->setLabel($secret_label)
+ ->setDisableAutocomplete(true)
->setName($key_secret)
->setValue($v_secret)
->setError($e_secret))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel(pht('OAuth App Notes'))
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setName($key_note)
->setValue($v_note));
}
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_NOTE:
if (strlen($old)) {
return pht(
'%s updated the OAuth application notes for this provider.',
$xaction->renderHandleLink($author_phid));
} else {
return pht(
'%s set the OAuth application notes for this provider.',
$xaction->renderHandleLink($author_phid));
}
}
return parent::renderConfigPropertyTransactionTitle($xaction);
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
parent::willSaveAccount($account);
$this->synchronizeOAuthAccount($account);
}
abstract protected function synchronizeOAuthAccount(
PhabricatorExternalAccount $account);
}
diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordPanel.php
index a17241f4c3..c45acb7185 100644
--- a/src/applications/diffusion/panel/DiffusionSetPasswordPanel.php
+++ b/src/applications/diffusion/panel/DiffusionSetPasswordPanel.php
@@ -1,249 +1,252 @@
<?php
final class DiffusionSetPasswordPanel extends PhabricatorSettingsPanel {
public function isEditableByAdministrators() {
return true;
}
public function getPanelKey() {
return 'vcspassword';
}
public function getPanelName() {
return pht('VCS Password');
}
public function getPanelGroup() {
return pht('Authentication');
}
public function isEnabled() {
return PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
'/settings/');
$vcspassword = id(new PhabricatorRepositoryVCSPassword())
->loadOneWhere(
'userPHID = %s',
$user->getPHID());
if (!$vcspassword) {
$vcspassword = id(new PhabricatorRepositoryVCSPassword());
$vcspassword->setUserPHID($user->getPHID());
}
$panel_uri = $this->getPanelURI('?saved=true');
$errors = array();
$e_password = true;
$e_confirm = true;
if ($request->isFormPost()) {
if ($request->getBool('remove')) {
if ($vcspassword->getID()) {
$vcspassword->delete();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
}
$new_password = $request->getStr('password');
$confirm = $request->getStr('confirm');
if (!strlen($new_password)) {
$e_password = pht('Required');
$errors[] = pht('Password is required.');
} else {
$e_password = null;
}
if (!strlen($confirm)) {
$e_confirm = pht('Required');
$errors[] = pht('You must confirm the new password.');
} else {
$e_confirm = null;
}
if (!$errors) {
$envelope = new PhutilOpaqueEnvelope($new_password);
if ($new_password !== $confirm) {
$e_password = pht('Does Not Match');
$e_confirm = pht('Does Not Match');
$errors[] = pht('Password and confirmation do not match.');
} else if ($viewer->comparePassword($envelope)) {
// NOTE: The above test is against $viewer (not $user), so that the
// error message below makes sense in the case that the two are
// different, and because an admin reusing their own password is bad,
// while system agents generally do not have passwords anyway.
$e_password = pht('Not Unique');
$e_confirm = pht('Not Unique');
$errors[] = pht(
'This password is the same as another password associated '.
'with your account. You must use a unique password for '.
'VCS access.');
} else if (
PhabricatorCommonPasswords::isCommonPassword($new_password)) {
$e_password = pht('Very Weak');
$e_confirm = pht('Very Weak');
$errors[] = pht(
'This password is extremely weak: it is one of the most common '.
'passwords in use. Choose a stronger password.');
}
if (!$errors) {
$vcspassword->setPassword($envelope, $user);
$vcspassword->save();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
}
}
$title = pht('Set VCS Password');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
'To access repositories hosted by Phabricator over HTTP, you must '.
'set a version control password. This password should be unique.'.
"\n\n".
"This password applies to all repositories available over ".
"HTTP."));
if ($vcspassword->getID()) {
$form
->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setLabel(pht('Current Password'))
->setDisabled(true)
->setValue('********************'));
} else {
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Current Password'))
->setValue(phutil_tag('em', array(), pht('No Password Set'))));
}
$form
->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setName('password')
->setLabel(pht('New VCS Password'))
->setError($e_password))
->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setName('confirm')
->setLabel(pht('Confirm VCS Password'))
->setError($e_confirm))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Change Password')));
if (!$vcspassword->getID()) {
$is_serious = PhabricatorEnv::getEnvConfig(
'phabricator.serious-business');
$suggest = Filesystem::readRandomBytes(128);
$suggest = preg_replace('([^A-Za-z0-9/!().,;{}^&*%~])', '', $suggest);
$suggest = substr($suggest, 0, 20);
if ($is_serious) {
$form->appendRemarkupInstructions(
pht(
'Having trouble coming up with a good password? Try this randomly '.
'generated one, made by a computer:'.
"\n\n".
"`%s`",
$suggest));
} else {
$form->appendRemarkupInstructions(
pht(
'Having trouble coming up with a good password? Try this '.
'artisinal password, hand made in small batches by our expert '.
'craftspeople: '.
"\n\n".
"`%s`",
$suggest));
}
}
$hash_envelope = new PhutilOpaqueEnvelope($vcspassword->getPasswordHash());
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Current Algorithm'))
->setValue(
PhabricatorPasswordHasher::getCurrentAlgorithmName($hash_envelope)));
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Best Available Algorithm'))
->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
if (strlen($hash_envelope->openEnvelope())) {
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
$errors[] = pht(
'The strength of your stored VCS password hash can be upgraded. '.
'To upgrade, either: use the password to authenticate with a '.
'repository; or change your password.');
}
}
$object_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form)
->setFormErrors($errors);
$remove_form = id(new AphrontFormView())
->setUser($viewer);
if ($vcspassword->getID()) {
$remove_form
->addHiddenInput('remove', true)
->appendRemarkupInstructions(
pht(
'You can remove your VCS password, which will prevent your '.
'account from accessing repositories.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Remove Password')));
} else {
$remove_form->appendRemarkupInstructions(
pht(
'You do not currently have a VCS password set. If you set one, you '.
'can remove it here later.'));
}
$remove_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Remove VCS Password'))
->setForm($remove_form);
$saved = null;
if ($request->getBool('saved')) {
$saved = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle(pht('Password Updated'))
->appendChild(pht('Your VCS password has been updated.'));
}
return array(
$saved,
$object_box,
$remove_box,
);
}
}
diff --git a/src/applications/passphrase/controller/PassphraseCredentialEditController.php b/src/applications/passphrase/controller/PassphraseCredentialEditController.php
index ea40551d73..b971013fdc 100644
--- a/src/applications/passphrase/controller/PassphraseCredentialEditController.php
+++ b/src/applications/passphrase/controller/PassphraseCredentialEditController.php
@@ -1,366 +1,367 @@
<?php
final class PassphraseCredentialEditController extends PassphraseController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
if ($this->id) {
$credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$credential) {
return new Aphront404Response();
}
$type = $this->getCredentialType($credential->getCredentialType());
$is_new = false;
} else {
$type_const = $request->getStr('type');
$type = $this->getCredentialType($type_const);
if (!$type->isCreateable()) {
throw new Exception(
pht(
'Credential has noncreateable type "%s"!',
$credential->getCredentialType()));
}
$credential = PassphraseCredential::initializeNewCredential($viewer)
->setCredentialType($type->getCredentialType())
->setProvidesType($type->getProvidesType());
$is_new = true;
// Prefill username if provided.
$credential->setUsername($request->getStr('username'));
if (!$request->getStr('isInitialized')) {
$type->didInitializeNewCredential($viewer, $credential);
}
}
$errors = array();
$v_name = $credential->getName();
$e_name = true;
$v_desc = $credential->getDescription();
$v_username = $credential->getUsername();
$e_username = true;
$v_is_locked = false;
$bullet = "\xE2\x80\xA2";
$v_secret = $credential->getSecretID() ? str_repeat($bullet, 32) : null;
if ($is_new && ($v_secret === null)) {
// If we're creating a new credential, the credential type may have
// populated the secret for us (for example, generated an SSH key). In
// this case,
try {
$v_secret = $credential->getSecret()->openEnvelope();
} catch (Exception $ex) {
// Ignore this.
}
}
$validation_exception = null;
$errors = array();
$e_password = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_desc = $request->getStr('description');
$v_username = $request->getStr('username');
$v_view_policy = $request->getStr('viewPolicy');
$v_edit_policy = $request->getStr('editPolicy');
$v_is_locked = $request->getStr('lock');
$v_secret = $request->getStr('secret');
$v_password = $request->getStr('password');
$v_decrypt = $v_secret;
$env_secret = new PhutilOpaqueEnvelope($v_secret);
$env_password = new PhutilOpaqueEnvelope($v_password);
if ($type->requiresPassword($env_secret)) {
if (strlen($v_password)) {
$v_decrypt = $type->decryptSecret($env_secret, $env_password);
if ($v_decrypt === null) {
$e_password = pht('Incorrect');
$errors[] = pht(
'This key requires a password, but the password you provided '.
'is incorrect.');
} else {
$v_decrypt = $v_decrypt->openEnvelope();
}
} else {
$e_password = pht('Required');
$errors[] = pht(
'This key requires a password. You must provide the password '.
'for the key.');
}
}
if (!$errors) {
$type_name = PassphraseCredentialTransaction::TYPE_NAME;
$type_desc = PassphraseCredentialTransaction::TYPE_DESCRIPTION;
$type_username = PassphraseCredentialTransaction::TYPE_USERNAME;
$type_destroy = PassphraseCredentialTransaction::TYPE_DESTROY;
$type_secret_id = PassphraseCredentialTransaction::TYPE_SECRET_ID;
$type_is_locked = PassphraseCredentialTransaction::TYPE_LOCK;
$type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_desc)
->setNewValue($v_desc);
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_view_policy)
->setNewValue($v_view_policy);
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_edit_policy)
->setNewValue($v_edit_policy);
// Open a transaction in case we're writing a new secret; this limits
// the amount of code which handles secret plaintexts.
$credential->openTransaction();
if (!$credential->getIsLocked()) {
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_username)
->setNewValue($v_username);
$min_secret = str_replace($bullet, '', trim($v_decrypt));
if (strlen($min_secret)) {
// If the credential was previously destroyed, restore it when it is
// edited if a secret is provided.
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_destroy)
->setNewValue(0);
$new_secret = id(new PassphraseSecret())
->setSecretData($v_decrypt)
->save();
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_secret_id)
->setNewValue($new_secret->getID());
}
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_is_locked)
->setNewValue($v_is_locked);
}
try {
$editor = id(new PassphraseCredentialTransactionEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->applyTransactions($credential, $xactions);
$credential->saveTransaction();
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())->setContent(
array(
'phid' => $credential->getPHID(),
'name' => 'K'.$credential->getID().' '.$credential->getName(),
));
} else {
return id(new AphrontRedirectResponse())
->setURI('/K'.$credential->getID());
}
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$credential->killTransaction();
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$e_username = $ex->getShortMessage($type_username);
$credential->setViewPolicy($v_view_policy);
$credential->setEditPolicy($v_edit_policy);
}
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($credential)
->execute();
$secret_control = $type->newSecretControl();
$credential_is_locked = $credential->getIsLocked();
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('isInitialized', true)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormTextAreaControl())
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setName('description')
->setLabel(pht('Description'))
->setValue($v_desc))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Credential Type'))
->setValue($type->getCredentialTypeName()))
->appendChild(
id(new AphrontFormDividerControl()))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($credential)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($credential)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild(
id(new AphrontFormDividerControl()));
if ($credential_is_locked) {
$form->appendRemarkupInstructions(
pht('This credential is permanently locked and can not be edited.'));
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('username')
->setLabel(pht('Login/Username'))
->setValue($v_username)
->setDisabled($credential_is_locked)
->setError($e_username))
->appendChild(
$secret_control
->setName('secret')
->setLabel($type->getSecretLabel())
->setDisabled($credential_is_locked)
->setValue($v_secret));
if ($type->shouldShowPasswordField()) {
$form->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setName('password')
->setLabel($type->getPasswordLabel())
->setDisabled($credential_is_locked)
->setError($e_password));
}
if ($is_new) {
$form->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'lock',
1,
array(
phutil_tag('strong', array(), pht('Lock Permanently:')),
' ',
pht('Prevent the secret from being revealed or changed.'),
),
$v_is_locked)
->setDisabled($credential_is_locked));
}
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$title = pht('Create Credential');
$header = pht('Create New Credential');
$crumbs->addTextCrumb(pht('Create'));
$cancel_uri = $this->getApplicationURI();
} else {
$title = pht('Edit Credential');
$header = pht('Edit Credential %s', 'K'.$credential->getID());
$crumbs->addTextCrumb(
'K'.$credential->getID(),
'/K'.$credential->getID());
$crumbs->addTextCrumb(pht('Edit'));
$cancel_uri = '/K'.$credential->getID();
}
if ($request->isAjax()) {
if ($errors) {
$errors = id(new AphrontErrorView())->setErrors($errors);
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle($title)
->appendChild($errors)
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Create Credential'))
->addCancelButton($cancel_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setFormErrors($errors)
->setValidationException($validation_exception)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
private function getCredentialType($type_const) {
$type = PassphraseCredentialType::getTypeByConstant($type_const);
if (!$type) {
throw new Exception(
pht('Credential has invalid type "%s"!', $type_const));
}
return $type;
}
}
diff --git a/src/applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php b/src/applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php
index 7f3ef9e6e1..bc55f1e4da 100644
--- a/src/applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php
+++ b/src/applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php
@@ -1,33 +1,34 @@
<?php
final class PassphraseCredentialTypePassword
extends PassphraseCredentialType {
const CREDENTIAL_TYPE = 'password';
const PROVIDES_TYPE = 'provides/password';
public function getCredentialType() {
return self::CREDENTIAL_TYPE;
}
public function getProvidesType() {
return self::PROVIDES_TYPE;
}
public function getCredentialTypeName() {
return pht('Password');
}
public function getCredentialTypeDescription() {
return pht('Store a plaintext password.');
}
public function getSecretLabel() {
return pht('Password');
}
public function newSecretControl() {
- return new AphrontFormPasswordControl();
+ return id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true);
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleLdapController.php b/src/applications/people/controller/PhabricatorPeopleLdapController.php
index d373399e04..57b3383ad2 100644
--- a/src/applications/people/controller/PhabricatorPeopleLdapController.php
+++ b/src/applications/people/controller/PhabricatorPeopleLdapController.php
@@ -1,217 +1,218 @@
<?php
final class PhabricatorPeopleLdapController
extends PhabricatorPeopleController {
public function processRequest() {
$request = $this->getRequest();
$admin = $request->getUser();
$content = array();
$form = id(new AphrontFormView())
->setAction($request->getRequestURI()
->alter('search', 'true')->alter('import', null))
->setUser($admin)
->appendChild(
id(new AphrontFormTextControl())
- ->setLabel(pht('LDAP username'))
- ->setName('username'))
+ ->setLabel(pht('LDAP username'))
+ ->setName('username'))
->appendChild(
id(new AphrontFormPasswordControl())
- ->setLabel(pht('Password'))
- ->setName('password'))
+ ->setDisableAutocomplete(true)
+ ->setLabel(pht('Password'))
+ ->setName('password'))
->appendChild(
id(new AphrontFormTextControl())
- ->setLabel(pht('LDAP query'))
- ->setCaption(pht('A filter such as (objectClass=*)'))
- ->setName('query'))
+ ->setLabel(pht('LDAP query'))
+ ->setCaption(pht('A filter such as (objectClass=*)'))
+ ->setName('query'))
->appendChild(
id(new AphrontFormSubmitControl())
- ->setValue(pht('Search')));
+ ->setValue(pht('Search')));
$panel = id(new AphrontPanelView())
->setHeader(pht('Import LDAP Users'))
->setNoBackground()
->setWidth(AphrontPanelView::WIDTH_FORM)
->appendChild($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Import Ldap Users'),
$this->getApplicationURI('/ldap/'));
$nav = $this->buildSideNavView();
$nav->setCrumbs($crumbs);
$nav->selectFilter('ldap');
$nav->appendChild($content);
if ($request->getStr('import')) {
$nav->appendChild($this->processImportRequest($request));
}
$nav->appendChild($panel);
if ($request->getStr('search')) {
$nav->appendChild($this->processSearchRequest($request));
}
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Import Ldap Users'),
));
}
private function processImportRequest($request) {
$admin = $request->getUser();
$usernames = $request->getArr('usernames');
$emails = $request->getArr('email');
$names = $request->getArr('name');
$notice_view = new AphrontErrorView();
$notice_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice_view->setTitle(pht('Import Successful'));
$notice_view->setErrors(array(
pht('Successfully imported users from LDAP'),
));
$list = new PHUIObjectItemListView();
$list->setNoDataString(pht('No users imported?'));
foreach ($usernames as $username) {
$user = new PhabricatorUser();
$user->setUsername($username);
$user->setRealname($names[$username]);
$email_obj = id(new PhabricatorUserEmail())
->setAddress($emails[$username])
->setIsVerified(1);
try {
id(new PhabricatorUserEditor())
->setActor($admin)
->createNewUser($user, $email_obj);
id(new PhabricatorExternalAccount())
->setUserPHID($user->getPHID())
->setAccountType('ldap')
->setAccountDomain('self')
->setAccountID($username)
->save();
$header = pht('Successfully added %s', $username);
$attribute = null;
$color = 'green';
} catch (Exception $ex) {
$header = pht('Failed to add %s', $username);
$attribute = $ex->getMessage();
$color = 'red';
}
$item = id(new PHUIObjectItemView())
->setHeader($header)
->addAttribute($attribute)
->setBarColor($color);
$list->addItem($item);
}
return array(
$notice_view,
$list,
);
}
private function processSearchRequest($request) {
$panel = new AphrontPanelView();
$admin = $request->getUser();
$search = $request->getStr('query');
$ldap_provider = PhabricatorLDAPAuthProvider::getLDAPProvider();
if (!$ldap_provider) {
throw new Exception('No LDAP provider enabled!');
}
$ldap_adapter = $ldap_provider->getAdapter();
$ldap_adapter->setLoginUsername($request->getStr('username'));
$ldap_adapter->setLoginPassword(
new PhutilOpaqueEnvelope($request->getStr('password')));
// This causes us to connect and bind.
// TODO: Clean up this discard mode stuff.
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
$ldap_adapter->getAccountID();
DarkConsoleErrorLogPluginAPI::disableDiscardMode();
$results = $ldap_adapter->searchLDAP('%Q', $search);
foreach ($results as $key => $record) {
$account_id = $ldap_adapter->readLDAPRecordAccountID($record);
if (!$account_id) {
unset($results[$key]);
continue;
}
$info = array(
$account_id,
$ldap_adapter->readLDAPRecordEmail($record),
$ldap_adapter->readLDAPRecordRealName($record),
);
$results[$key] = $info;
$results[$key][] = $this->renderUserInputs($info);
}
$form = id(new AphrontFormView())
->setUser($admin);
$table = new AphrontTableView($results);
$table->setHeaders(
array(
pht('Username'),
pht('Email'),
pht('Real Name'),
pht('Import?'),
));
$form->appendChild($table);
$form->setAction($request->getRequestURI()
->alter('import', 'true')->alter('search', null))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Import')));
$panel->appendChild($form);
return $panel;
}
private function renderUserInputs($user) {
$username = $user[0];
return hsprintf(
'%s%s%s',
phutil_tag(
'input',
array(
'type' => 'checkbox',
'name' => 'usernames[]',
'value' => $username,
)),
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => "email[$username]",
'value' => $user[1],
)),
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => "name[$username]",
'value' => $user[2],
)));
}
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelPassword.php b/src/applications/settings/panel/PhabricatorSettingsPanelPassword.php
index 0c7fc81934..2aef196b96 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanelPassword.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanelPassword.php
@@ -1,201 +1,203 @@
<?php
final class PhabricatorSettingsPanelPassword
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'password';
}
public function getPanelName() {
return pht('Password');
}
public function getPanelGroup() {
return pht('Authentication');
}
public function isEnabled() {
// There's no sense in showing a change password panel if the user
// can't change their password...
if (!PhabricatorEnv::getEnvConfig('account.editable')) {
return false;
}
// ...or this install doesn't support password authentication at all.
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
return false;
}
return true;
}
public function processRequest(AphrontRequest $request) {
$user = $request->getUser();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$user,
$request,
'/settings/');
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
// NOTE: To change your password, you need to prove you own the account,
// either by providing the old password or by carrying a token to
// the workflow from a password reset email.
$key = $request->getStr('key');
$token = null;
if ($key) {
$token = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($user)
->withObjectPHIDs(array($user->getPHID()))
->withTokenTypes(
array(PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE))
->withTokenCodes(array(PhabricatorHash::digest($key)))
->withExpired(false)
->executeOne();
}
$e_old = true;
$e_new = true;
$e_conf = true;
$errors = array();
if ($request->isFormPost()) {
if (!$token) {
$envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
if (!$user->comparePassword($envelope)) {
$errors[] = pht('The old password you entered is incorrect.');
$e_old = pht('Invalid');
}
}
$pass = $request->getStr('new_pw');
$conf = $request->getStr('conf_pw');
if (strlen($pass) < $min_len) {
$errors[] = pht('Your new password is too short.');
$e_new = pht('Too Short');
} else if ($pass !== $conf) {
$errors[] = pht('New password and confirmation do not match.');
$e_conf = pht('Invalid');
} else if (PhabricatorCommonPasswords::isCommonPassword($pass)) {
$e_new = pht('Very Weak');
$e_conf = pht('Very Weak');
$errors[] = pht(
'Your new password is very weak: it is one of the most common '.
'passwords in use. Choose a stronger password.');
}
if (!$errors) {
// This write is unguarded because the CSRF token has already
// been checked in the call to $request->isFormPost() and
// the CSRF token depends on the password hash, so when it
// is changed here the CSRF token check will fail.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$envelope = new PhutilOpaqueEnvelope($pass);
id(new PhabricatorUserEditor())
->setActor($user)
->changePassword($user, $envelope);
unset($unguarded);
if ($token) {
// Destroy the token.
$token->delete();
// If this is a password set/reset, kick the user to the home page
// after we update their account.
$next = '/';
} else {
$next = $this->getPanelURI('?saved=true');
}
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
$request->getCookie(PhabricatorCookies::COOKIE_SESSION));
return id(new AphrontRedirectResponse())->setURI($next);
}
}
$hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
if (strlen($hash_envelope->openEnvelope())) {
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
$errors[] = pht(
'The strength of your stored password hash can be upgraded. '.
'To upgrade, either: log out and log in using your password; or '.
'change your password.');
}
}
$len_caption = null;
if ($min_len) {
$len_caption = pht('Minimum password length: %d characters.', $min_len);
}
$form = new AphrontFormView();
$form
->setUser($user)
->addHiddenInput('key', $key);
if (!$token) {
$form->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Old Password'))
->setError($e_old)
->setName('old_pw'));
}
$form
->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setLabel(pht('New Password'))
->setError($e_new)
->setName('new_pw'));
$form
->appendChild(
id(new AphrontFormPasswordControl())
+ ->setDisableAutocomplete(true)
->setLabel(pht('Confirm Password'))
->setCaption($len_caption)
->setError($e_conf)
->setName('conf_pw'));
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Change Password')));
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Current Algorithm'))
->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName(
new PhutilOpaqueEnvelope($user->getPasswordHash()))));
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Best Available Algorithm'))
->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
$form->appendRemarkupInstructions(
pht(
'NOTE: Changing your password will terminate any other outstanding '.
'login sessions.'));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Change Password'))
->setFormSaved($request->getStr('saved'))
->setFormErrors($errors)
->setForm($form);
return array(
$form_box,
);
}
}
diff --git a/src/view/form/control/AphrontFormPasswordControl.php b/src/view/form/control/AphrontFormPasswordControl.php
index b2a3f1ef50..9e37d7aa20 100644
--- a/src/view/form/control/AphrontFormPasswordControl.php
+++ b/src/view/form/control/AphrontFormPasswordControl.php
@@ -1,21 +1,29 @@
<?php
final class AphrontFormPasswordControl extends AphrontFormControl {
+ private $disableAutocomplete;
+
+ public function setDisableAutocomplete($disable_autocomplete) {
+ $this->disableAutocomplete = $disable_autocomplete;
+ return $this;
+ }
+
protected function getCustomControlClass() {
return 'aphront-form-control-password';
}
protected function renderInput() {
return phutil_tag(
'input',
array(
'type' => 'password',
'name' => $this->getName(),
'value' => $this->getValue(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
+ 'autocomplete' => ($this->disableAutocomplete ? 'off' : null),
'id' => $this->getID(),
));
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:07 (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1127726
Default Alt Text
(59 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment