/**
  * Checks if user was not authenticated, or if too many logins were attempted.
  *
  * This validation function should always be the last one.
  */
 public function validateFinal(array &$form, FormStateInterface $form_state)
 {
     $flood_config = $this->config('user.flood');
     if (!$form_state->get('uid')) {
         // Always register an IP-based failed login event.
         $this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
         // Register a per-user failed login event.
         if ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
             $this->flood->register('user.failed_login_user', $flood_config->get('user_window'), $flood_control_user_identifier);
         }
         if ($flood_control_triggered = $form_state->get('flood_control_triggered')) {
             if ($flood_control_triggered == 'user') {
                 $form_state->setErrorByName('name', format_plural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => $this->url('user.pass'))));
             } else {
                 // We did not find a uid, so the limit is IP-based.
                 $form_state->setErrorByName('name', $this->t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => $this->url('user.pass'))));
             }
         } else {
             $form_state->setErrorByName('name', $this->t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => $this->url('user.pass', [], array('query' => array('name' => $form_state->getValue('name')))))));
             $accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name')));
             if (!empty($accounts)) {
                 $this->logger('user')->notice('Login attempt failed for %user.', array('%user' => $form_state->getValue('name')));
             } else {
                 // If the username entered is not a valid user,
                 // only store the IP address.
                 $this->logger('user')->notice('Login attempt failed from %ip.', array('%ip' => $this->getRequest()->getClientIp()));
             }
         }
     } elseif ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
         // Clear past failures for this user so as not to block a user who might
         // log in and out more than once in an hour.
         $this->flood->clear('user.failed_login_user', $flood_control_user_identifier);
     }
 }
Example #2
0
 /**
  * Throws an exception if the current user triggers flood control.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  */
 protected function contactFloodControl()
 {
     $limit = $this->config('contact.settings')->get('flood.limit');
     $interval = $this->config('contact.settings')->get('flood.interval');
     if (!$this->flood->isAllowed('contact', $limit, $interval)) {
         drupal_set_message($this->t('You cannot send more than %limit messages in @interval. Try again later.', array('%limit' => $limit, '@interval' => format_interval($interval))), 'error');
         throw new AccessDeniedHttpException();
     }
 }
Example #3
0
 /**
  * {@inheritdoc}
  */
 public function authenticate(Request $request)
 {
     $flood_config = $this->configFactory->get('user.flood');
     $username = $request->headers->get('PHP_AUTH_USER');
     $password = $request->headers->get('PHP_AUTH_PW');
     // Flood protection: this is very similar to the user login form code.
     // @see \Drupal\user\Form\UserLoginForm::validateAuthentication()
     // Do not allow any login from the current user's IP if the limit has been
     // reached. Default is 50 failed attempts allowed in one hour. This is
     // independent of the per-user limit to catch attempts from one IP to log
     // in to many different user accounts.  We have a reasonably high limit
     // since there may be only one apparent IP for all users at an institution.
     if ($this->flood->isAllowed('basic_auth.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
         $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username, 'status' => 1));
         $account = reset($accounts);
         if ($account) {
             if ($flood_config->get('uid_only')) {
                 // Register flood events based on the uid only, so they apply for any
                 // IP address. This is the most secure option.
                 $identifier = $account->id();
             } else {
                 // The default identifier is a combination of uid and IP address. This
                 // is less secure but more resistant to denial-of-service attacks that
                 // could lock out all users with public user names.
                 $identifier = $account->id() . '-' . $request->getClientIP();
             }
             // Don't allow login if the limit for this user has been reached.
             // Default is to allow 5 failed attempts every 6 hours.
             if ($this->flood->isAllowed('basic_auth.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
                 $uid = $this->userAuth->authenticate($username, $password);
                 if ($uid) {
                     $this->flood->clear('basic_auth.failed_login_user', $identifier);
                     return $this->entityManager->getStorage('user')->load($uid);
                 } else {
                     // Register a per-user failed login event.
                     $this->flood->register('basic_auth.failed_login_user', $flood_config->get('user_window'), $identifier);
                 }
             }
         }
     }
     // Always register an IP-based failed login event.
     $this->flood->register('basic_auth.failed_login_ip', $flood_config->get('ip_window'));
     return [];
 }
 /**
  * {@inheritdoc}
  */
 public function save(array $form, FormStateInterface $form_state)
 {
     $message = $this->entity;
     $user = $this->currentUser();
     $this->mailHandler->sendMailMessages($message, $user);
     $this->flood->register('contact', $this->config('contact.settings')->get('flood.interval'));
     drupal_set_message($this->t('Your message has been sent.'));
     // To avoid false error messages caused by flood control, redirect away from
     // the contact form; either to the contacted user account or the front page.
     if ($message->isPersonal() && $user->hasPermission('access user profiles')) {
         $form_state->setRedirectUrl($message->getPersonalRecipient()->urlInfo());
     } else {
         $form_state->setRedirect('<front>');
     }
     // Save the message. In core this is a no-op but should contrib wish to
     // implement message storage, this will make the task of swapping in a real
     // storage controller straight-forward.
     $message->save();
 }
 /**
  * Enforces flood control for the current login request.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The current request.
  * @param string $username
  *   The user name sent for login credentials.
  */
 protected function floodControl(Request $request, $username)
 {
     $flood_config = $this->config('user.flood');
     if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
         throw new AccessDeniedHttpException('Access is blocked because of IP based flood prevention.', NULL, Response::HTTP_TOO_MANY_REQUESTS);
     }
     if ($identifier = $this->getLoginFloodIdentifier($request, $username)) {
         // Don't allow login if the limit for this user has been reached.
         // Default is to allow 5 failed attempts every 6 hours.
         if (!$this->flood->isAllowed('user.http_login', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
             if ($flood_config->get('uid_only')) {
                 $error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config->get('user_limit'));
             } else {
                 $error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
             }
             throw new AccessDeniedHttpException($error_message, NULL, Response::HTTP_TOO_MANY_REQUESTS);
         }
     }
 }
Example #6
0
 /**
  * Checks if user was not authenticated, or if too many logins were attempted.
  *
  * This validation function should always be the last one.
  */
 public function validateFinal(array &$form, FormStateInterface $form_state)
 {
     $flood_config = $this->config('user.flood');
     if (!$form_state->get('uid')) {
         // Always register an IP-based failed login event.
         $this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
         // Register a per-user failed login event.
         if ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
             $this->flood->register('user.failed_login_user', $flood_config->get('user_window'), $flood_control_user_identifier);
         }
         if ($flood_control_triggered = $form_state->get('flood_control_triggered')) {
             if ($flood_control_triggered == 'user') {
                 $form_state->setErrorByName('name', $this->formatPlural($flood_config->get('user_limit'), 'There has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', 'There have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', array(':url' => $this->url('user.pass'))));
             } else {
                 // We did not find a uid, so the limit is IP-based.
                 $form_state->setErrorByName('name', $this->t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', array(':url' => $this->url('user.pass'))));
             }
         } else {
             // Use $form_state->getUserInput() in the error message to guarantee
             // that we send exactly what the user typed in. The value from
             // $form_state->getValue() may have been modified by validation
             // handlers that ran earlier than this one.
             $user_input = $form_state->getUserInput();
             $query = isset($user_input['name']) ? array('name' => $user_input['name']) : array();
             $form_state->setErrorByName('name', $this->t('Unrecognized username or password. <a href=":password">Have you forgotten your password?</a>', array(':password' => $this->url('user.pass', [], array('query' => $query)))));
             $accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name')));
             if (!empty($accounts)) {
                 $this->logger('user')->notice('Login attempt failed for %user.', array('%user' => $form_state->getValue('name')));
             } else {
                 // If the username entered is not a valid user,
                 // only store the IP address.
                 $this->logger('user')->notice('Login attempt failed from %ip.', array('%ip' => $this->getRequest()->getClientIp()));
             }
         }
     } elseif ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
         // Clear past failures for this user so as not to block a user who might
         // log in and out more than once in an hour.
         $this->flood->clear('user.failed_login_user', $flood_control_user_identifier);
     }
 }
Example #7
0
 /**
  * {@inheritdoc}
  */
 public function save(array $form, array &$form_state)
 {
     $user = $this->currentUser();
     $language_interface = $this->languageManager->getCurrentLanguage();
     $message = $this->entity;
     $sender = clone $this->entityManager->getStorage('user')->load($user->id());
     if ($user->isAnonymous()) {
         // At this point, $sender contains an anonymous user, so we need to take
         // over the submitted form values.
         $sender->name = $message->getSenderName();
         $sender->mail = $message->getSenderMail();
         // Save the anonymous user information to a cookie for reuse.
         // @todo remove when https://www.drupal.org/node/749748 is in.
         user_cookie_save(array('name' => $message->getSenderName(), 'mail' => $message->getSenderMail()));
         // For the email message, clarify that the sender name is not verified; it
         // could potentially clash with a username on this site.
         $sender->name = $this->t('!name (not verified)', array('!name' => $message->getSenderName()));
     }
     // Build email parameters.
     $params['contact_message'] = $message;
     $params['sender'] = $sender;
     if (!$message->isPersonal()) {
         // Send to the category recipient(s), using the site's default language.
         $category = $message->getCategory();
         $params['contact_category'] = $category;
         $to = implode(', ', $category->recipients);
         $recipient_langcode = $this->languageManager->getDefaultLanguage()->getId();
     } elseif ($recipient = $message->getPersonalRecipient()) {
         // Send to the user in the user's preferred language.
         $to = $recipient->getEmail();
         $recipient_langcode = $recipient->getPreferredLangcode();
         $params['recipient'] = $recipient;
     } else {
         throw new \RuntimeException($this->t('Unable to determine message recipient.'));
     }
     // Send email to the recipient(s).
     $key_prefix = $message->isPersonal() ? 'user' : 'page';
     drupal_mail('contact', $key_prefix . '_mail', $to, $recipient_langcode, $params, $sender->getEmail());
     // If requested, send a copy to the user, using the current language.
     if ($message->copySender()) {
         drupal_mail('contact', $key_prefix . '_copy', $sender->getEmail(), $language_interface->id, $params, $sender->getEmail());
     }
     // If configured, send an auto-reply, using the current language.
     if (!$message->isPersonal() && $category->reply) {
         // User contact forms do not support an auto-reply message, so this
         // message always originates from the site.
         drupal_mail('contact', 'page_autoreply', $sender->getEmail(), $language_interface->id, $params);
     }
     $this->flood->register('contact', $this->config('contact.settings')->get('flood.interval'));
     if (!$message->isPersonal()) {
         watchdog('contact', '%sender-name (@sender-from) sent an email regarding %category.', array('%sender-name' => $sender->getUsername(), '@sender-from' => $sender->getEmail(), '%category' => $category->label()));
     } else {
         watchdog('contact', '%sender-name (@sender-from) sent %recipient-name an email.', array('%sender-name' => $sender->getUsername(), '@sender-from' => $sender->getEmail(), '%recipient-name' => $message->getPersonalRecipient()->getUsername()));
     }
     drupal_set_message($this->t('Your message has been sent.'));
     // To avoid false error messages caused by flood control, redirect away from
     // the contact form; either to the contacted user account or the front page.
     if ($message->isPersonal() && $user->hasPermission('access user profiles')) {
         $form_state['redirect_route'] = $message->getPersonalRecipient()->urlInfo();
     } else {
         $form_state['redirect_route']['route_name'] = '<front>';
     }
     // Save the message. In core this is a no-op but should contrib wish to
     // implement message storage, this will make the task of swapping in a real
     // storage controller straight-forward.
     $message->save();
 }