You are here

class EntryForm in Two-factor Authentication (TFA) 8

TFA entry form.

Hierarchy

Expanded class hierarchy of EntryForm

1 string reference to 'EntryForm'
tfa.routing.yml in ./tfa.routing.yml
tfa.routing.yml

File

src/Form/EntryForm.php, line 19

Namespace

Drupal\tfa\Form
View source
class EntryForm extends FormBase {

  /**
   * Validation plugin manager.
   *
   * @var \Drupal\tfa\TfaValidationPluginManager
   */
  protected $tfaValidationManager;

  /**
   * Login plugin manager.
   *
   * @var \Drupal\tfa\TfaLoginPluginManager
   */
  protected $tfaLoginManager;

  /**
   * The validation plugin object.
   *
   * @var \Drupal\tfa\Plugin\TfaValidationInterface
   */
  protected $tfaValidationPlugin;

  /**
   * The login plugins.
   *
   * @var \Drupal\tfa\Plugin\TfaLoginInterface
   */
  protected $tfaLoginPlugins;

  /**
   * TFA configuration object.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $tfaSettings;

  /**
   * The flood control mechanism.
   *
   * @var \Drupal\Core\Flood\FloodInterface
   */
  protected $flood;

  /**
   * The flood control identifier.
   *
   * @var string
   */
  protected $floodIdentifier;

  /**
   * The date formatter service.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * User data service.
   *
   * @var \Drupal\user\UserDataInterface
   */
  protected $userData;

  /**
   * EntryForm constructor.
   *
   * @param \Drupal\tfa\TfaValidationPluginManager $tfa_validation_manager
   *   Plugin manager for validation plugins.
   * @param \Drupal\tfa\TfaLoginPluginManager $tfa_login_manager
   *   Plugin manager for login plugins.
   * @param \Drupal\Core\Flood\FloodInterface $flood
   *   The flood control mechanism.
   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
   *   The date service.
   * @param \Drupal\user\UserDataInterface $user_data
   *   User data service.
   */
  public function __construct(TfaValidationPluginManager $tfa_validation_manager, TfaLoginPluginManager $tfa_login_manager, FloodInterface $flood, DateFormatterInterface $date_formatter, UserDataInterface $user_data) {
    $this->tfaValidationManager = $tfa_validation_manager;
    $this->tfaLoginManager = $tfa_login_manager;
    $this->tfaSettings = $this
      ->config('tfa.settings');
    $this->flood = $flood;
    $this->dateFormatter = $date_formatter;
    $this->userData = $user_data;
  }

  /**
   * Creates service objects for the class constructor.
   *
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   *   The container to get the required services.
   *
   * @return static
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('plugin.manager.tfa.validation'), $container
      ->get('plugin.manager.tfa.login'), $container
      ->get('flood'), $container
      ->get('date.formatter'), $container
      ->get('user.data'));
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'tfa_entry_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, AccountInterface $user = NULL) {
    $alternate_plugin = $this
      ->getRequest()
      ->get('plugin');
    $validation_plugin_definitions = $this->tfaValidationManager
      ->getDefinitions();
    $user_settings = $this->userData
      ->get('tfa', $user
      ->id(), 'tfa_user_settings');
    $user_enabled_validation_plugins = isset($user_settings['data']['plugins']) ? $user_settings['data']['plugins'] : [];

    // Default validation plugin, then check for enabled alternate plugin.
    $validation_plugin = $this->tfaSettings
      ->get('default_validation_plugin');
    if ($alternate_plugin && !empty($validation_plugin_definitions[$alternate_plugin]) && !empty($user_enabled_validation_plugins[$alternate_plugin])) {
      $validation_plugin = $alternate_plugin;
      $form['#cache'] = [
        'max-age' => 0,
      ];
    }

    // Get current validation plugin form.
    $this->tfaValidationPlugin = $this->tfaValidationManager
      ->createInstance($validation_plugin, [
      'uid' => $user
        ->id(),
    ]);
    $form = $this->tfaValidationPlugin
      ->getForm($form, $form_state);
    $this->tfaLoginPlugins = $this->tfaLoginManager
      ->getPlugins([
      'uid' => $user
        ->id(),
    ]);
    if ($this->tfaLoginPlugins) {
      foreach ($this->tfaLoginPlugins as $login_plugin) {
        if (method_exists($login_plugin, 'getForm')) {
          $form = $login_plugin
            ->getForm($form, $form_state);
        }
      }
    }
    $form['account'] = [
      '#type' => 'value',
      '#value' => $user,
    ];

    // Build a list of links for using other enabled validation methods.
    $other_validation_plugin_links = [];
    foreach ($user_enabled_validation_plugins as $user_enabled_validation_plugin) {

      // Do not show the current plugin.
      if ($validation_plugin == $user_enabled_validation_plugin) {
        continue;
      }

      // Do not show plugins without labels.
      if (empty($validation_plugin_definitions[$user_enabled_validation_plugin]['label'])) {
        continue;
      }
      $other_validation_plugin_links[$user_enabled_validation_plugin] = [
        'title' => $validation_plugin_definitions[$user_enabled_validation_plugin]['label'],
        'url' => Url::fromRoute('tfa.entry', [
          'user' => $user
            ->id(),
          'hash' => $this
            ->getRequest()
            ->get('hash'),
          'plugin' => $user_enabled_validation_plugin,
        ]),
      ];
    }

    // Show other enabled and configured validation plugins.
    $form['validation_plugin'] = [
      '#type' => 'value',
      '#value' => $validation_plugin,
    ];

    // Don't show an empty fieldset when no other tfa methods are available.
    if (!empty($other_validation_plugin_links)) {
      $form['change_validation_plugin'] = [
        '#type' => 'fieldset',
        '#title' => $this
          ->t('Having Trouble?'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        'content' => [
          'help' => [
            '#markup' => $this
              ->t('Try one of your other enabled validation methods.'),
          ],
          'other_validation_plugins' => [
            '#theme' => 'links',
            '#links' => $other_validation_plugin_links,
          ],
        ],
      ];
    }
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state
      ->getValues();
    $window = $this->tfaSettings
      ->get('tfa_flood_window') ?: 300;
    $threshold = $this->tfaSettings
      ->get('tfa_flood_threshold') ?: 6;
    if ($this->tfaSettings
      ->get('tfa_flood_uid_only')) {

      // Register flood events based on the uid only, so they apply for any
      // IP address. This is the most secure option.
      $this->floodIdentifier = $values['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.
      $this->floodIdentifier = $values['account']
        ->id() . '-' . $this
        ->getRequest()
        ->getClientIP();
    }

    // Flood control.
    if (!$this->flood
      ->isAllowed('tfa.failed_validation', $threshold, $window, $this->floodIdentifier)) {
      $form_state
        ->setErrorByName('', $this
        ->t('Failed validation limit reached. %limit wrong codes in @interval. Try again later.', [
        '%limit' => $threshold,
        '@interval' => $this->dateFormatter
          ->formatInterval($window),
      ]));
      return;
    }
    $validated = $this->tfaValidationPlugin
      ->validateForm($form, $form_state);
    if (!$validated) {

      // @todo Either define getErrorMessages in the TfaValidationInterface, or don't use it.
      // For now, let's just check that it exists before assuming.
      if (method_exists($this->tfaValidationPlugin, 'getErrorMessages')) {
        $form_state
          ->clearErrors();
        $errors = $this->tfaValidationPlugin
          ->getErrorMessages();
        $form_state
          ->setErrorByName(key($errors), current($errors));
      }
      $this->flood
        ->register('tfa.failed_validation', $this->tfaSettings
        ->get('tfa_flood_window'), $this->floodIdentifier);
    }
  }

  /**
   * If the form is passes validation, the user should get logged in.
   *
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $user = $form_state
      ->getValue('account');

    // @todo This could be improved with EventDispatcher.
    if (!empty($this->tfaLoginPlugins)) {
      foreach ($this->tfaLoginPlugins as $plugin) {
        if (method_exists($plugin, 'submitForm')) {
          $plugin
            ->submitForm($form, $form_state);
        }
      }
    }
    user_login_finalize($user);

    // @todo Should finalize() be after user_login_finalize or before?!
    // @todo This could be improved with EventDispatcher.
    $this
      ->finalize();
    $this->flood
      ->clear('tfa.failed_validation', $this->floodIdentifier);
    $form_state
      ->setRedirect('<front>');
  }

  /**
   * Run TFA process finalization.
   */
  public function finalize() {

    // Invoke plugin finalize.
    if (method_exists($this->tfaValidationPlugin, 'finalize')) {
      $this->tfaValidationPlugin
        ->finalize();
    }

    // Allow login plugins to act during finalization.
    if (!empty($this->tfaLoginPlugins)) {
      foreach ($this->tfaLoginPlugins as $plugin) {
        if (method_exists($plugin, 'finalize')) {
          $plugin
            ->finalize();
        }
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
EntryForm::$dateFormatter protected property The date formatter service.
EntryForm::$flood protected property The flood control mechanism.
EntryForm::$floodIdentifier protected property The flood control identifier.
EntryForm::$tfaLoginManager protected property Login plugin manager.
EntryForm::$tfaLoginPlugins protected property The login plugins.
EntryForm::$tfaSettings protected property TFA configuration object.
EntryForm::$tfaValidationManager protected property Validation plugin manager.
EntryForm::$tfaValidationPlugin protected property The validation plugin object.
EntryForm::$userData protected property User data service.
EntryForm::buildForm public function Form constructor. Overrides FormInterface::buildForm
EntryForm::create public static function Creates service objects for the class constructor. Overrides FormBase::create
EntryForm::finalize public function Run TFA process finalization.
EntryForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
EntryForm::submitForm public function If the form is passes validation, the user should get logged in. Overrides FormInterface::submitForm
EntryForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
EntryForm::__construct public function EntryForm constructor.
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.