/** * Return an instance with a new, random password * @return TemporaryPasswordAuthenticationRequest */ public static function newRandom() { $config = \ConfigFactory::getDefaultInstance()->makeConfig('main'); // get the min password length $minLength = $config->get('MinimalPasswordLength'); $policy = $config->get('PasswordPolicy'); foreach ($policy['policies'] as $p) { if (isset($p['MinimalPasswordLength'])) { $minLength = max($minLength, $p['MinimalPasswordLength']); } if (isset($p['MinimalPasswordLengthToLogin'])) { $minLength = max($minLength, $p['MinimalPasswordLengthToLogin']); } } $password = \PasswordFactory::generateRandomPasswordString($minLength); return new self($password); }
public function execute() { $userName = $this->getArg(0); if (preg_match('/^#\\d+$/', $userName)) { $user = User::newFromId(substr($userName, 1)); } else { $user = User::newFromName($userName); } if (!$user || !$user->getId() || !$user->loadFromId()) { $this->error("Error: user '{$userName}' does not exist\n", 1); } $email = $this->getArg(1); if (!Sanitizer::validateEmail($email)) { $this->error("Error: email '{$email}' is not valid\n", 1); } // Code from https://wikitech.wikimedia.org/wiki/Password_reset $user->setEmail($email); $user->setEmailAuthenticationTimestamp(wfTimestampNow()); $user->saveSettings(); // Kick whomever is currently controlling the account off $user->setPassword(PasswordFactory::generateRandomPasswordString(128)); }
/** * Test the account creation API with a valid request. Also * make sure the new account can log in and is valid. * * This test does multiple API requests so it might end up being * a bit slow. Raise the default timeout. * @group medium */ public function testValid() { global $wgServer; if (!isset($wgServer)) { $this->markTestIncomplete('This test needs $wgServer to be set in LocalSettings.php'); } $password = PasswordFactory::generateRandomPasswordString(); $ret = $this->doApiRequest(array('action' => 'createaccount', 'name' => 'Apitestnew', 'password' => $password, 'email' => '*****@*****.**', 'realname' => 'Test Name')); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $this->assertNotInternalType('null', $result['createaccount']); // Should first ask for token. $a = $result['createaccount']; $this->assertEquals('NeedToken', $a['result']); $token = $a['token']; // Finally create the account $ret = $this->doApiRequest(array('action' => 'createaccount', 'name' => 'Apitestnew', 'password' => $password, 'token' => $token, 'email' => '*****@*****.**', 'realname' => 'Test Name'), $ret[2]); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $this->assertEquals('Success', $result['createaccount']['result']); // Try logging in with the new user. $ret = $this->doApiRequest(array('action' => 'login', 'lgname' => 'Apitestnew', 'lgpassword' => $password)); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $this->assertNotInternalType('null', $result['login']); $a = $result['login']['result']; $this->assertEquals('NeedToken', $a); $token = $result['login']['token']; $ret = $this->doApiRequest(array('action' => 'login', 'lgtoken' => $token, 'lgname' => 'Apitestnew', 'lgpassword' => $password), $ret[2]); $result = $ret[0]; $this->assertNotInternalType('bool', $result); $a = $result['login']['result']; $this->assertEquals('Success', $a); // log out to destroy the session $ret = $this->doApiRequest(array('action' => 'logout'), $ret[2]); $this->assertEquals(array(), $ret[0]); }
/** * Return a random password. * * @deprecated since 1.27, use PasswordFactory::generateRandomPasswordString() * @return string New random password */ public static function randomPassword() { global $wgMinimalPasswordLength; return PasswordFactory::generateRandomPasswordString($wgMinimalPasswordLength); }
private function save(array $data) { $bp = BotPassword::newUnsaved(['centralId' => $this->userId, 'appId' => $this->par, 'restrictions' => MWRestrictions::newFromJson($data['restrictions']), 'grants' => array_merge(MWGrants::getHiddenGrants(), preg_replace('/^grant-/', '', $data['grants']))]); if ($this->operation === 'insert' || !empty($data['resetPassword'])) { $this->password = PasswordFactory::generateRandomPasswordString(max(32, $this->getConfig()->get('MinimalPasswordLength'))); $passwordFactory = new PasswordFactory(); $passwordFactory->init(RequestContext::getMain()->getConfig()); $password = $passwordFactory->newFromPlaintext($this->password); } else { $password = null; } if ($bp->save($this->operation, $password)) { return Status::newGood(); } else { // Messages: botpasswords-insert-failed, botpasswords-update-failed return Status::newFatal("botpasswords-{$this->operation}-failed", $this->par); } }
/** * Process the form. At this point we know that the user passes all the criteria in * userCanExecute(), and if the data array contains 'Username', etc, then Username * resets are allowed. * @param array $data * @throws MWException * @throws ThrottledError|PermissionsError * @return bool|array */ public function onSubmit(array $data) { global $wgAuth, $wgMinimalPasswordLength; if (isset($data['Domain'])) { if ($wgAuth->validDomain($data['Domain'])) { $wgAuth->setDomain($data['Domain']); } else { $wgAuth->setDomain('invaliddomain'); } } if (isset($data['Capture']) && !$this->getUser()->isAllowed('passwordreset')) { // The user knows they don't have the passwordreset permission, // but they tried to spoof the form. That's naughty throw new PermissionsError('passwordreset'); } /** * @var $firstUser User * @var $users User[] */ if (isset($data['Username']) && $data['Username'] !== '') { $method = 'username'; $users = [User::newFromName($data['Username'])]; } elseif (isset($data['Email']) && $data['Email'] !== '' && Sanitizer::validateEmail($data['Email'])) { $method = 'email'; $res = wfGetDB(DB_SLAVE)->select('user', User::selectFields(), ['user_email' => $data['Email']], __METHOD__); if ($res) { $users = []; foreach ($res as $row) { $users[] = User::newFromRow($row); } } else { // Some sort of database error, probably unreachable throw new MWException('Unknown database error in ' . __METHOD__); } } else { // The user didn't supply any data return false; } // Check for hooks (captcha etc), and allow them to modify the users list $error = []; if (!Hooks::run('SpecialPasswordResetOnSubmit', [&$users, $data, &$error])) { return [$error]; } $this->method = $method; if (count($users) == 0) { if ($method == 'email') { // Don't reveal whether or not an email address is in use return true; } else { return ['noname']; } } $firstUser = $users[0]; if (!$firstUser instanceof User || !$firstUser->getId()) { // Don't parse username as wikitext (bug 65501) return [['nosuchuser', wfEscapeWikiText($data['Username'])]]; } // Check against the rate limiter if ($this->getUser()->pingLimiter('mailpassword')) { throw new ThrottledError(); } // Check against password throttle foreach ($users as $user) { if ($user->isPasswordReminderThrottled()) { # Round the time in hours to 3 d.p., in case someone is specifying # minutes or seconds. return [['throttled-mailpassword', round($this->getConfig()->get('PasswordReminderResendTime'), 3)]]; } } // All the users will have the same email address if ($firstUser->getEmail() == '') { // This won't be reachable from the email route, so safe to expose the username return [['noemail', wfEscapeWikiText($firstUser->getName())]]; } // We need to have a valid IP address for the hook, but per bug 18347, we should // send the user's name if they're logged in. $ip = $this->getRequest()->getIP(); if (!$ip) { return ['badipaddress']; } $caller = $this->getUser(); Hooks::run('User::mailPasswordInternal', [&$caller, &$ip, &$firstUser]); $username = $caller->getName(); $msg = IP::isValid($username) ? 'passwordreset-emailtext-ip' : 'passwordreset-emailtext-user'; // Send in the user's language; which should hopefully be the same $userLanguage = $firstUser->getOption('language'); $passwords = []; foreach ($users as $user) { $password = PasswordFactory::generateRandomPasswordString($wgMinimalPasswordLength); $user->setNewpassword($password); $user->saveSettings(); $passwords[] = $this->msg('passwordreset-emailelement', $user->getName(), $password)->inLanguage($userLanguage)->text(); // We'll escape the whole thing later } $passwordBlock = implode("\n\n", $passwords); $this->email = $this->msg($msg)->inLanguage($userLanguage); $this->email->params($username, $passwordBlock, count($passwords), '<' . Title::newMainPage()->getCanonicalURL() . '>', round($this->getConfig()->get('NewPasswordExpiry') / 86400)); $title = $this->msg('passwordreset-emailtitle')->inLanguage($userLanguage); $this->result = $firstUser->sendMail($title->text(), $this->email->text()); if (isset($data['Capture']) && $data['Capture']) { // Save the user, will be used if an error occurs when sending the email $this->firstUser = $firstUser; } else { // Blank the email if the user is not supposed to see it $this->email = null; } if ($this->result->isGood()) { return true; } elseif (isset($data['Capture']) && $data['Capture']) { // The email didn't send, but maybe they knew that and that's why they captured it return true; } else { // @todo FIXME: The email wasn't sent, but we have already set // the password throttle timestamp, so they won't be able to try // again until it expires... :( return [['mailerror', $this->result->getMessage()]]; } }
/** * @param User $u * @param bool $throttle * @param string $emailTitle Message name of email title * @param string $emailText Message name of email text * @return Status */ function mailPasswordInternal($u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext') { global $wgNewPasswordExpiry, $wgMinimalPasswordLength; if ($u->getEmail() == '') { return Status::newFatal('noemail', $u->getName()); } $ip = $this->getRequest()->getIP(); if (!$ip) { return Status::newFatal('badipaddress'); } $currentUser = $this->getUser(); Hooks::run('User::mailPasswordInternal', [&$currentUser, &$ip, &$u]); $np = PasswordFactory::generateRandomPasswordString($wgMinimalPasswordLength); $u->setNewpassword($np, $throttle); $u->saveSettings(); $userLanguage = $u->getOption('language'); $mainPage = Title::newMainPage(); $mainPageUrl = $mainPage->getCanonicalURL(); $m = $this->msg($emailText, $ip, $u->getName(), $np, '<' . $mainPageUrl . '>', round($wgNewPasswordExpiry / 86400))->inLanguage($userLanguage)->text(); $result = $u->sendMail($this->msg($emailTitle)->inLanguage($userLanguage)->text(), $m); return $result; }
/** * Returns a (raw, unhashed) random password string. * @param Config $config * @return string */ public static function generatePassword($config) { return PasswordFactory::generateRandomPasswordString(max(32, $config->get('MinimalPasswordLength'))); }