/** * Start an account creation flow * @param User $creator User doing the account creation * @param AuthenticationRequest[] $reqs * @param string $returnToUrl Url that REDIRECT responses should eventually * return to. * @return AuthenticationResponse */ public function beginAccountCreation(User $creator, array $reqs, $returnToUrl) { $session = $this->request->getSession(); if (!$this->canCreateAccounts()) { // Caller should have called canCreateAccounts() $session->remove('AuthManager::accountCreationState'); throw new \LogicException('Account creation is not possible'); } try { $username = AuthenticationRequest::getUsernameFromRequests($reqs); } catch (\UnexpectedValueException $ex) { $username = null; } if ($username === null) { $this->logger->debug(__METHOD__ . ': No username provided'); return AuthenticationResponse::newFail(wfMessage('noname')); } // Permissions check $status = $this->checkAccountCreatePermissions($creator); if (!$status->isGood()) { $this->logger->debug(__METHOD__ . ': {creator} cannot create users: {reason}', ['user' => $username, 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } $status = $this->canCreateAccount($username, User::READ_LOCKING); if (!$status->isGood()) { $this->logger->debug(__METHOD__ . ': {user} cannot be created: {reason}', ['user' => $username, 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } $user = User::newFromName($username, 'creatable'); foreach ($reqs as $req) { $req->username = $username; $req->returnToUrl = $returnToUrl; if ($req instanceof UserDataAuthenticationRequest) { $status = $req->populateUser($user); if (!$status->isGood()) { $status = Status::wrap($status); $session->remove('AuthManager::accountCreationState'); $this->logger->debug(__METHOD__ . ': UserData is invalid: {reason}', ['user' => $user->getName(), 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } } } $this->removeAuthenticationSessionData(null); $state = ['username' => $username, 'userid' => 0, 'creatorid' => $creator->getId(), 'creatorname' => $creator->getName(), 'reqs' => $reqs, 'returnToUrl' => $returnToUrl, 'primary' => null, 'primaryResponse' => null, 'secondary' => [], 'continueRequests' => [], 'maybeLink' => [], 'ranPreTests' => false]; // Special case: converting a login to an account creation $req = AuthenticationRequest::getRequestByClass($reqs, CreateFromLoginAuthenticationRequest::class); if ($req) { $state['maybeLink'] = $req->maybeLink; // If we get here, the user didn't submit a form with any of the // usual AuthenticationRequests that are needed for an account // creation. So we need to determine if there are any and return a // UI response if so. if ($req->createRequest) { // We have a createRequest from a // PrimaryAuthenticationProvider, so don't ask. $providers = $this->getPreAuthenticationProviders() + $this->getSecondaryAuthenticationProviders(); } else { // We're only preserving maybeLink, so ask for primary fields // too. $providers = $this->getPreAuthenticationProviders() + $this->getPrimaryAuthenticationProviders() + $this->getSecondaryAuthenticationProviders(); } $reqs = $this->getAuthenticationRequestsInternal(self::ACTION_CREATE, [], $providers); // See if we need any requests to begin foreach ((array) $reqs as $r) { if (!$r instanceof UsernameAuthenticationRequest && !$r instanceof UserDataAuthenticationRequest && !$r instanceof CreationReasonAuthenticationRequest) { // Needs some reqs, so request them $reqs[] = new CreateFromLoginAuthenticationRequest($req->createRequest, []); $state['continueRequests'] = $reqs; $session->setSecret('AuthManager::accountCreationState', $state); $session->persist(); return AuthenticationResponse::newUI($reqs, wfMessage('authmanager-create-from-login')); } } // No reqs needed, so we can just continue. $req->createRequest->returnToUrl = $returnToUrl; $reqs = [$req->createRequest]; } $session->setSecret('AuthManager::accountCreationState', $state); $session->persist(); return $this->continueAccountCreation($reqs); }
public function testForAuthentication(array $reqs) { if (!$this->passwordAttemptThrottle) { return \StatusValue::newGood(); } $ip = $this->manager->getRequest()->getIP(); try { $username = AuthenticationRequest::getUsernameFromRequests($reqs); } catch (\UnexpectedValueException $e) { $username = ''; } // Get everything this username could normalize to, and throttle each one individually. // If nothing uses usernames, just throttle by IP. $usernames = $this->manager->normalizeUsername($username); $result = false; foreach ($usernames as $name) { $r = $this->passwordAttemptThrottle->increase($name, $ip, __METHOD__); if ($r && (!$result || $result['wait'] < $r['wait'])) { $result = $r; } } if ($result) { $message = wfMessage('login-throttled')->durationParams($result['wait']); return \StatusValue::newFatal($message); } else { $this->manager->setAuthenticationSessionData('LoginThrottle', ['users' => $usernames, 'ip' => $ip]); return \StatusValue::newGood(); } }
/** * Start an account creation flow * * In addition to the AuthenticationRequests returned by * $this->getAuthenticationRequests(), a client might include a * CreateFromLoginAuthenticationRequest from a previous login attempt. If * <code> * $createFromLoginAuthenticationRequest->hasPrimaryStateForAction( AuthManager::ACTION_CREATE ) * </code> * returns true, any AuthenticationRequest::PRIMARY_REQUIRED requests * should be omitted. If the CreateFromLoginAuthenticationRequest has a * username set, that username must be used for all other requests. * * @param User $creator User doing the account creation * @param AuthenticationRequest[] $reqs * @param string $returnToUrl Url that REDIRECT responses should eventually * return to. * @return AuthenticationResponse */ public function beginAccountCreation(User $creator, array $reqs, $returnToUrl) { $session = $this->request->getSession(); if (!$this->canCreateAccounts()) { // Caller should have called canCreateAccounts() $session->remove('AuthManager::accountCreationState'); throw new \LogicException('Account creation is not possible'); } try { $username = AuthenticationRequest::getUsernameFromRequests($reqs); } catch (\UnexpectedValueException $ex) { $username = null; } if ($username === null) { $this->logger->debug(__METHOD__ . ': No username provided'); return AuthenticationResponse::newFail(wfMessage('noname')); } // Permissions check $status = $this->checkAccountCreatePermissions($creator); if (!$status->isGood()) { $this->logger->debug(__METHOD__ . ': {creator} cannot create users: {reason}', ['user' => $username, 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } $status = $this->canCreateAccount($username, ['flags' => User::READ_LOCKING, 'creating' => true]); if (!$status->isGood()) { $this->logger->debug(__METHOD__ . ': {user} cannot be created: {reason}', ['user' => $username, 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } $user = User::newFromName($username, 'creatable'); foreach ($reqs as $req) { $req->username = $username; $req->returnToUrl = $returnToUrl; if ($req instanceof UserDataAuthenticationRequest) { $status = $req->populateUser($user); if (!$status->isGood()) { $status = Status::wrap($status); $session->remove('AuthManager::accountCreationState'); $this->logger->debug(__METHOD__ . ': UserData is invalid: {reason}', ['user' => $user->getName(), 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]); return AuthenticationResponse::newFail($status->getMessage()); } } } $this->removeAuthenticationSessionData(null); $state = ['username' => $username, 'userid' => 0, 'creatorid' => $creator->getId(), 'creatorname' => $creator->getName(), 'reqs' => $reqs, 'returnToUrl' => $returnToUrl, 'primary' => null, 'primaryResponse' => null, 'secondary' => [], 'continueRequests' => [], 'maybeLink' => [], 'ranPreTests' => false]; // Special case: converting a login to an account creation $req = AuthenticationRequest::getRequestByClass($reqs, CreateFromLoginAuthenticationRequest::class); if ($req) { $state['maybeLink'] = $req->maybeLink; if ($req->createRequest) { $reqs[] = $req->createRequest; $state['reqs'][] = $req->createRequest; } } $session->setSecret('AuthManager::accountCreationState', $state); $session->persist(); return $this->continueAccountCreation($reqs); }