public function appendFieldsToForm(AphrontFormView $form) { $enabled = array(); foreach ($this->fields as $field) { if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_EDIT)) { $enabled[] = $field; } } $phids = array(); foreach ($enabled as $field_key => $field) { $phids[$field_key] = $field->getRequiredHandlePHIDsForEdit(); } $all_phids = array_mergev($phids); if ($all_phids) { $handles = id(new PhabricatorHandleQuery())->setViewer($this->viewer)->withPHIDs($all_phids)->execute(); } else { $handles = array(); } foreach ($enabled as $field_key => $field) { $field_handles = array_select_keys($handles, $phids[$field_key]); $instructions = $field->getInstructionsForEdit(); if (strlen($instructions)) { $form->appendRemarkupInstructions($instructions); } $form->appendChild($field->renderEditControl($field_handles)); } }
public function processAddFactorForm(AphrontFormView $form, AphrontRequest $request, PhabricatorUser $user) { $totp_token_type = PhabricatorAuthTOTPKeyTemporaryTokenType::TOKENTYPE; $key = $request->getStr('totpkey'); if (strlen($key)) { // If the user is providing a key, make sure it's a key we generated. // This raises the barrier to theoretical attacks where an attacker might // provide a known key (such attacks are already prevented by CSRF, but // this is a second barrier to overcome). // (We store and verify the hash of the key, not the key itself, to limit // how useful the data in the table is to an attacker.) $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery())->setViewer($user)->withTokenResources(array($user->getPHID()))->withTokenTypes(array($totp_token_type))->withExpired(false)->withTokenCodes(array(PhabricatorHash::digest($key)))->executeOne(); if (!$temporary_token) { // If we don't have a matching token, regenerate the key below. $key = null; } } if (!strlen($key)) { $key = self::generateNewTOTPKey(); // Mark this key as one we generated, so the user is allowed to submit // a response for it. $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); id(new PhabricatorAuthTemporaryToken())->setTokenResource($user->getPHID())->setTokenType($totp_token_type)->setTokenExpires(time() + phutil_units('1 hour in seconds'))->setTokenCode(PhabricatorHash::digest($key))->save(); unset($unguarded); } $code = $request->getStr('totpcode'); $e_code = true; if ($request->getExists('totp')) { $okay = self::verifyTOTPCode($user, new PhutilOpaqueEnvelope($key), $code); if ($okay) { $config = $this->newConfigForUser($user)->setFactorName(pht('Mobile App (TOTP)'))->setFactorSecret($key); return $config; } else { if (!strlen($code)) { $e_code = pht('Required'); } else { $e_code = pht('Invalid'); } } } $form->addHiddenInput('totp', true); $form->addHiddenInput('totpkey', $key); $form->appendRemarkupInstructions(pht('First, download an authenticator application on your phone. Two ' . 'applications which work well are **Authy** and **Google ' . 'Authenticator**, but any other TOTP application should also work.')); $form->appendInstructions(pht('Launch the application on your phone, and add a new entry for ' . 'this Phabricator install. When prompted, scan the QR code or ' . 'manually enter the key shown below into the application.')); $prod_uri = new PhutilURI(PhabricatorEnv::getProductionURI('/')); $issuer = $prod_uri->getDomain(); $uri = urisprintf('otpauth://totp/%s:%s?secret=%s&issuer=%s', $issuer, $user->getUsername(), $key, $issuer); $qrcode = $this->renderQRCode($uri); $form->appendChild($qrcode); $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Key'))->setValue(phutil_tag('strong', array(), $key))); $form->appendInstructions(pht('(If given an option, select that this key is "Time Based", not ' . '"Counter Based".)')); $form->appendInstructions(pht('After entering the key, the application should display a numeric ' . 'code. Enter that code below to confirm that you have configured ' . 'the authenticator correctly:')); $form->appendChild(id(new PHUIFormNumberControl())->setLabel(pht('TOTP Code'))->setName('totpcode')->setValue($code)->setError($e_code)); }
public function appendToForm(AphrontFormView $form) { return $form->appendRemarkupInstructions($this->getValue()); }
public function processRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $user = $this->getUser(); $preferences = $user->loadPreferences(); $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX; $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT; $prefs_html_email = PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS; $errors = array(); if ($request->isFormPost()) { if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { if ($request->getStr($pref_re_prefix) == 'default') { $preferences->unsetPreference($pref_re_prefix); } else { $preferences->setPreference($pref_re_prefix, $request->getBool($pref_re_prefix)); } if ($request->getStr($pref_vary) == 'default') { $preferences->unsetPreference($pref_vary); } else { $preferences->setPreference($pref_vary, $request->getBool($pref_vary)); } if ($request->getStr($prefs_html_email) == 'default') { $preferences->unsetPreference($prefs_html_email); } else { $preferences->setPreference($prefs_html_email, $request->getBool($prefs_html_email)); } } $preferences->save(); return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true')); } $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') ? pht('Enabled') : pht('Disabled'); $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') ? pht('Vary') : pht('Do Not Vary'); $html_emails_default = pht('Plain Text'); $re_prefix_value = $preferences->getPreference($pref_re_prefix); if ($re_prefix_value === null) { $re_prefix_value = 'default'; } else { $re_prefix_value = $re_prefix_value ? 'true' : 'false'; } $vary_value = $preferences->getPreference($pref_vary); if ($vary_value === null) { $vary_value = 'default'; } else { $vary_value = $vary_value ? 'true' : 'false'; } $html_emails_value = $preferences->getPreference($prefs_html_email); if ($html_emails_value === null) { $html_emails_value = 'default'; } else { $html_emails_value = $html_emails_value ? 'true' : 'false'; } $form = new AphrontFormView(); $form->setUser($viewer); if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { $html_email_control = id(new AphrontFormSelectControl())->setName($prefs_html_email)->setOptions(array('default' => pht('Default (%s)', $html_emails_default), 'true' => pht('Send HTML Email'), 'false' => pht('Send Plain Text Email')))->setValue($html_emails_value); $re_control = id(new AphrontFormSelectControl())->setName($pref_re_prefix)->setOptions(array('default' => pht('Use Server Default (%s)', $re_prefix_default), 'true' => pht('Enable "Re:" prefix'), 'false' => pht('Disable "Re:" prefix')))->setValue($re_prefix_value); $vary_control = id(new AphrontFormSelectControl())->setName($pref_vary)->setOptions(array('default' => pht('Use Server Default (%s)', $vary_default), 'true' => pht('Vary Subjects'), 'false' => pht('Do Not Vary Subjects')))->setValue($vary_value); } else { $html_email_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $html_emails_default)); $re_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $re_prefix_default)); $vary_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $vary_default)); } $form->appendRemarkupInstructions(pht('These settings fine-tune some technical aspects of how email is ' . 'formatted. You may be able to adjust them to make mail more ' . 'useful or improve threading.')); if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) { $form->appendRemarkupInstructions(pht('NOTE: This install of Phabricator is configured to send a ' . 'single mail message to all recipients, so all settings are ' . 'locked at the server default value.')); } $form->appendRemarkupInstructions(pht("You can use the **HTML Email** setting to control whether " . "Phabricator send you HTML email (which has more color and " . "formatting) or plain text email (which is more compatible).\n" . "\n" . "WARNING: This feature is new and experimental! If you enable " . "it, mail may not render properly and replying to mail may not " . "work as well."))->appendChild($html_email_control->setLabel(pht('HTML Email')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('The **Add "Re:" Prefix** setting adds "Re:" in front of all ' . 'messages, even if they are not replies. If you use **Mail.app** on ' . 'Mac OS X, this may improve mail threading.' . "\n\n" . "| Setting | Example Mail Subject\n" . "|------------------------|----------------\n" . "| Enable \"Re:\" Prefix | " . "`Re: [Differential] [Accepted] D123: Example Revision`\n" . "| Disable \"Re:\" Prefix | " . "`[Differential] [Accepted] D123: Example Revision`"))->appendChild($re_control->setLabel(pht('Add "Re:" Prefix')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('With **Vary Subjects** enabled, most mail subject lines will ' . 'include a brief description of their content, like **[Closed]** ' . 'for a notification about someone closing a task.' . "\n\n" . "| Setting | Example Mail Subject\n" . "|----------------------|----------------\n" . "| Vary Subjects | " . "`[Maniphest] [Closed] T123: Example Task`\n" . "| Do Not Vary Subjects | " . "`[Maniphest] T123: Example Task`\n" . "\n" . 'This can make mail more useful, but some clients have difficulty ' . 'threading these messages. Disabling this option may improve ' . 'threading, at the cost of less useful subject lines.'))->appendChild($vary_control->setLabel(pht('Vary Subjects'))); $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Preferences'))); $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Email Format'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form); return id(new AphrontNullView())->appendChild(array($form_box)); }
public function appendToForm(AphrontFormView $form) { $control = $this->renderControl(); if ($control !== null) { if ($this->getIsPreview()) { if ($this->getIsHidden()) { $control->addClass('aphront-form-preview-hidden')->setError(pht('Hidden')); } else { if ($this->getIsLocked()) { $control->setError(pht('Locked')); } } } $instructions = $this->getControlInstructions(); if (strlen($instructions)) { $form->appendRemarkupInstructions($instructions); } $form->appendControl($control); } return $this; }
public function extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues) { self::assertLDAPExtensionInstalled(); $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 extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues) { $is_setup = $this->isSetup(); $e_required = $request->isFormPost() ? null : true; $v_name = $values[self::PROPERTY_MEDIAWIKI_NAME]; if ($is_setup) { $e_name = idx($issues, self::PROPERTY_MEDIAWIKI_NAME, $e_required); } else { $e_name = null; } $v_uri = $values[self::PROPERTY_MEDIAWIKI_URI]; $e_uri = idx($issues, self::PROPERTY_MEDIAWIKI_URI, $e_required); $config = $this->getProviderConfig(); if ($is_setup) { $form->appendRemarkupInstructions(pht("**MediaWiki Instance Name**\n\n" . "Choose a permanent name for this instance of MediaWiki." . "Phabricator uses this name internally to keep track of " . "this instance of MediaWiki, in case the URL changes later." . "\n\n" . "Use lowercase letters, digits, and period. For example: " . "\n\n`mediawiki`, `mediawiki.mycompany` " . "or `mediawiki.engineering` are reasonable names."))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Instance Name'))->setValue($v_name)->setName(self::PROPERTY_MEDIAWIKI_NAME)->setError($e_name)); } else { $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Instance Name'))->setValue($v_name)->setName(self::PROPERTY_MEDIAWIKI_NAME)->setDisabled(true)->setError($e_name)); } $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Base URI'))->setValue($v_uri)->setName(self::PROPERTY_MEDIAWIKI_URI)->setPlaceholder('https://www.mediawiki.org/w')->setCaption(pht('The full URL to your MediaWiki innstall, up to but not including "index.php"'))->setError($e_uri)); if (!$is_setup) { if (!strlen($config->getProperty(self::PROPERTY_CONSUMER_KEY))) { $form->appendRemarkupInstructions(pht('NOTE: Copy the keys generated by the MediaWiki OAuth' . ' consumer registration and paste them here.')); } $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Consumer Key'))->setName(self::PROPERTY_CONSUMER_KEY)->setValue($values[self::PROPERTY_CONSUMER_KEY]))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Secret Key'))->setName(self::PROPERTY_CONSUMER_SECRET)->setValue($values[self::PROPERTY_CONSUMER_SECRET])); } }
public function extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues) { if (!function_exists('openssl_pkey_new')) { // TODO: This could be a bit prettier. throw new Exception(pht("The PHP 'openssl' extension is not installed. You must install " . "this extension in order to add a JIRA authentication provider, " . "because JIRA OAuth requests use the RSA-SHA1 signing algorithm. " . "Install the 'openssl' extension, restart your webserver, and try " . "again.")); } $form->appendRemarkupInstructions(pht('NOTE: This provider **only supports JIRA 6**. It will not work with ' . 'JIRA 5 or earlier.')); $is_setup = $this->isSetup(); $viewer = $request->getViewer(); $e_required = $request->isFormPost() ? null : true; $v_name = $values[self::PROPERTY_JIRA_NAME]; if ($is_setup) { $e_name = idx($issues, self::PROPERTY_JIRA_NAME, $e_required); } else { $e_name = null; } $v_uri = $values[self::PROPERTY_JIRA_URI]; $e_uri = idx($issues, self::PROPERTY_JIRA_URI, $e_required); if ($is_setup) { $form->appendRemarkupInstructions(pht("**JIRA Instance Name**\n\n" . "Choose a permanent name for this instance of JIRA. Phabricator " . "uses this name internally to keep track of this instance of " . "JIRA, in case the URL changes later.\n\n" . "Use lowercase letters, digits, and period. For example, " . "`jira`, `jira.mycompany` or `jira.engineering` are reasonable " . "names."))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('JIRA Instance Name'))->setValue($v_name)->setName(self::PROPERTY_JIRA_NAME)->setError($e_name)); } else { $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('JIRA Instance Name'))->setValue($v_name)); } $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('JIRA Base URI'))->setValue($v_uri)->setName(self::PROPERTY_JIRA_URI)->setCaption(pht('The URI where JIRA is installed. For example: %s', phutil_tag('tt', array(), 'https://jira.mycompany.com/')))->setError($e_uri)); if (!$is_setup) { $config = $this->getProviderConfig(); $ckey = $config->getProperty(self::PROPERTY_CONSUMER_KEY); $ckey = phutil_tag('tt', array(), $ckey); $pkey = $config->getProperty(self::PROPERTY_PUBLIC_KEY); $pkey = phutil_escape_html_newlines($pkey); $pkey = phutil_tag('tt', array(), $pkey); $form->appendRemarkupInstructions(pht('NOTE: **To complete setup**, copy and paste these keys into JIRA ' . 'according to the instructions below.'))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Consumer Key'))->setValue($ckey))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Public Key'))->setValue($pkey)); $form->appendRemarkupInstructions(pht('= Integration Options = ' . "\n" . 'Configure how to record Revisions on JIRA tasks.' . "\n\n" . 'Note you\'ll have to restart the daemons for this to take ' . 'effect.'))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox(self::PROPERTY_REPORT_LINK, 1, new PHUIRemarkupView($viewer, pht('Create **Issue Link** to the Revision, as an "implemented ' . 'in" relationship.')), $this->shouldCreateJIRALink()))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox(self::PROPERTY_REPORT_COMMENT, 1, new PHUIRemarkupView($viewer, pht('**Post a comment** in the JIRA task, similar to the ' . 'emails Phabricator sends.')), $this->shouldCreateJIRAComment())); } }
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())) { try { $can_upgrade = PhabricatorPasswordHasher::canUpgradeHash($hash_envelope); } catch (PhabricatorPasswordHasherUnavailableException $ex) { $can_upgrade = false; // Only show this stuff if we aren't on the reset workflow. We can // do resets regardless of the old hasher's availability. if (!$token) { $errors[] = pht('Your password is currently hashed using an algorithm which is ' . 'no longer available on this install.'); $errors[] = pht('Because the algorithm implementation is missing, your password ' . 'can not be used or updated.'); $errors[] = pht('To set a new password, request a password reset link from the ' . 'login screen and then follow the instructions.'); } } if ($can_upgrade) { $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); }
public function processRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $user = $this->getUser(); $preferences = $user->loadPreferences(); $pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL; $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL; $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; $errors = array(); if ($request->isFormPost()) { $preferences->setPreference($pref_no_mail, $request->getStr($pref_no_mail)); $preferences->setPreference($pref_no_self_mail, $request->getStr($pref_no_self_mail)); $new_tags = $request->getArr('mailtags'); $mailtags = $preferences->getPreference('mailtags', array()); $all_tags = $this->getAllTags($user); foreach ($all_tags as $key => $label) { $mailtags[$key] = (int) idx($new_tags, $key, $value_email); } $preferences->setPreference('mailtags', $mailtags); $preferences->save(); return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true')); } $form = new AphrontFormView(); $form->setUser($viewer)->appendRemarkupInstructions(pht('These settings let you control how Phabricator notifies you about ' . 'events. You can configure Phabricator to send you an email, ' . 'just send a web notification, or not notify you at all.'))->appendRemarkupInstructions(pht('If you disable **Email Notifications**, Phabricator will never ' . 'send email to notify you about events. This preference overrides ' . 'all your other settings.' . "\n\n" . "//You may still receive some administrative email, like password " . "reset email.//"))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Email Notifications'))->setName($pref_no_mail)->setOptions(array('0' => pht('Send me email notifications'), '1' => pht('Never send email notifications')))->setValue($preferences->getPreference($pref_no_mail, 0)))->appendRemarkupInstructions(pht('If you disable **Self Actions**, Phabricator will not notify ' . 'you about actions you take.'))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Self Actions'))->setName($pref_no_self_mail)->setOptions(array('0' => pht('Send me an email when I take an action'), '1' => pht('Do not send me an email when I take an action')))->setValue($preferences->getPreference($pref_no_self_mail, 0))); $mailtags = $preferences->getPreference('mailtags', array()); $form->appendChild(id(new PHUIFormDividerControl())); $form->appendRemarkupInstructions(pht('You can adjust **Application Settings** here to customize when ' . 'you are emailed and notified.' . "\n\n" . "| Setting | Effect\n" . "| ------- | -------\n" . "| Email | You will receive an email and a notification, but the " . "notification will be marked \"read\".\n" . "| Notify | You will receive an unread notification only.\n" . "| Ignore | You will receive nothing.\n" . "\n\n" . 'If an update makes several changes (like adding CCs to a task, ' . 'closing it, and adding a comment) you will receive the strongest ' . 'notification any of the changes is configured to deliver.' . "\n\n" . 'These preferences **only** apply to objects you are connected to ' . '(for example, Revisions where you are a reviewer or tasks you are ' . 'CC\'d on). To receive email alerts when other objects are created, ' . 'configure [[ /herald/ | Herald Rules ]].')); $editors = $this->getAllEditorsWithTags($user); // Find all the tags shared by more than one application, and put them // in a "common" group. $all_tags = array(); foreach ($editors as $editor) { foreach ($editor->getMailTagsMap() as $tag => $name) { if (empty($all_tags[$tag])) { $all_tags[$tag] = array('count' => 0, 'name' => $name); } $all_tags[$tag]['count']; } } $common_tags = array(); foreach ($all_tags as $tag => $info) { if ($info['count'] > 1) { $common_tags[$tag] = $info['name']; } } // Build up the groups of application-specific options. $tag_groups = array(); foreach ($editors as $editor) { $tag_groups[] = array($editor->getEditorObjectsDescription(), array_diff_key($editor->getMailTagsMap(), $common_tags)); } // Sort them, then put "Common" at the top. $tag_groups = isort($tag_groups, 0); if ($common_tags) { array_unshift($tag_groups, array(pht('Common'), $common_tags)); } // Finally, build the controls. foreach ($tag_groups as $spec) { list($label, $map) = $spec; $control = $this->buildMailTagControl($label, $map, $mailtags); $form->appendChild($control); } $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Preferences'))); $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Email Preferences'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form); return id(new AphrontNullView())->appendChild(array($form_box)); }