You are here

class TfaTrustedBrowser in Two-factor Authentication (TFA) 8

Trusted browser validation class.

Plugin annotation


@TfaLogin(
  id = "tfa_trusted_browser",
  label = @Translation("TFA Trusted Browser"),
  description = @Translation("TFA Trusted Browser Plugin"),
  setupPluginId = "tfa_trusted_browser_setup",
)

Hierarchy

Expanded class hierarchy of TfaTrustedBrowser

1 file declares its use of TfaTrustedBrowser
TfaTrustedBrowserSetup.php in src/Plugin/TfaSetup/TfaTrustedBrowserSetup.php

File

src/Plugin/TfaLogin/TfaTrustedBrowser.php, line 24

Namespace

Drupal\tfa\Plugin\TfaLogin
View source
class TfaTrustedBrowser extends TfaBasePlugin implements TfaLoginInterface, TfaValidationInterface {
  use StringTranslationTrait;

  /**
   * Trust browser.
   *
   * @var bool
   */
  protected $trustBrowser;

  /**
   * The cookie name.
   *
   * @var string
   */
  protected $cookieName;

  /**
   * Cookie expiration time.
   *
   * @var string
   */
  protected $expiration;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, UserDataInterface $user_data, EncryptionProfileManagerInterface $encryption_profile_manager, EncryptServiceInterface $encrypt_service) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $user_data, $encryption_profile_manager, $encrypt_service);
    $config = \Drupal::config('tfa.settings');
    $this->cookieName = $config
      ->get('cookie_name') ?: 'TFA';

    // Expiration defaults to 30 days.
    $this->expiration = $config
      ->get('trust_cookie_expiration') ?: 86400 * 30;
    $this->userData = $user_data;
  }

  /**
   * {@inheritdoc}
   */
  public function loginAllowed() {
    if (isset($_COOKIE[$this->cookieName]) && $this
      ->trustedBrowser($_COOKIE[$this->cookieName]) !== FALSE) {
      $this
        ->setUsed($_COOKIE[$this->cookieName]);
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getForm(array $form, FormStateInterface $form_state) {
    $form['trust_browser'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Remember this browser?'),
      '#description' => $this
        ->t('Not recommended if you are on a public or shared computer.'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array $form, FormStateInterface $form_state) {
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array $form, FormStateInterface &$form_state) {
    $trust_browser = $form_state
      ->getValue('trust_browser');
    if (!empty($trust_browser)) {
      $this
        ->setTrusted($this
        ->generateBrowserId(), $this
        ->getAgent());
    }
  }

  /**
   * Finalize the browser setup.
   *
   * @throws \Exception
   */
  public function finalize() {
    if ($this->trustBrowser) {
      $name = $this
        ->getAgent();
      $this
        ->setTrusted($this
        ->generateBrowserId(), $name);
    }
  }

  /**
   * Generate a random value to identify the browser.
   *
   * @return string
   *   Base64 encoded browser id.
   *
   * @throws \Exception
   */
  protected function generateBrowserId() {
    $id = base64_encode(random_bytes(32));
    return strtr($id, [
      '+' => '-',
      '/' => '_',
      '=' => '',
    ]);
  }

  /**
   * Store browser value and issue cookie for user.
   *
   * @param string $id
   *   Trusted browser id.
   * @param string $name
   *   The custom browser name.
   */
  protected function setTrusted($id, $name = '') {

    // Currently broken.
    // Store id for account.
    $records = $this
      ->getUserData('tfa', 'tfa_trusted_browser', $this->configuration['uid'], $this->userData) ?: [];
    $request_time = \Drupal::time()
      ->getRequestTime();
    $records[$id] = [
      'created' => $request_time,
      'ip' => \Drupal::request()
        ->getClientIp(),
      'name' => $name,
    ];
    $data = [
      'tfa_trusted_browser' => $records,
    ];
    $this
      ->setUserData('tfa', $data, $this->configuration['uid'], $this->userData);

    // Issue cookie with ID.
    $cookie_secure = ini_get('session.cookie_secure');
    $expiration = $request_time + $this->expiration;
    $domain = strpos($_SERVER['HTTP_HOST'], 'localhost') === FALSE ? $_SERVER['HTTP_HOST'] : FALSE;
    setcookie($this->cookieName, $id, $expiration, '/', $domain, empty($cookie_secure) ? FALSE : TRUE, TRUE);
    $name = empty($name) ? $this
      ->getAgent() : $name;

    // @todo use services defined in module instead this procedural way.
    \Drupal::logger('tfa')
      ->info('Set trusted browser for user UID @uid, browser @name', [
      '@name' => $name,
      '@uid' => $this->uid,
    ]);
  }

  /**
   * Updated browser last used time.
   *
   * @param int $id
   *   Internal browser ID to update.
   */
  protected function setUsed($id) {
    $result = $this
      ->getUserData('tfa', 'tfa_trusted_browser', $this->uid, $this->userData);
    $result[$id]['last_used'] = \Drupal::time()
      ->getRequestTime();
    $data = [
      'tfa_trusted_browser' => $result,
    ];
    $this
      ->setUserData('tfa', $data, $this->uid, $this->userData);
  }

  /**
   * Check if browser id matches user's saved browser.
   *
   * @param string $id
   *   The browser ID.
   *
   * @return bool
   *   TRUE if ID exists otherwise FALSE.
   */
  protected function trustedBrowser($id) {

    // Check if $id has been saved for this user.
    $result = $this
      ->getUserData('tfa', 'tfa_trusted_browser', $this->uid, $this->userData);
    if (isset($result[$id])) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Delete users trusted browser.
   *
   * @param string $id
   *   (optional) Id of the browser to be purged.
   *
   * @return bool
   *   TRUE is id found and purged otherwise FALSE.
   */
  protected function deleteTrusted($id = '') {
    $result = $this
      ->getUserData('tfa', 'tfa_trusted_browser', $this->uid, $this->userData);
    if ($id) {
      if (isset($result[$id])) {
        unset($result[$id]);
        $data = [
          'tfa_trusted_browser' => $result,
        ];
        $this
          ->setUserData('tfa', $data, $this->uid, $this->userData);
        return TRUE;
      }
    }
    else {
      $this
        ->deleteUserData('tfa', 'tfa_trusted_browser', $this->uid, $this->userData);
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Get simplified browser name from user agent.
   *
   * @param string $name
   *   Default browser name.
   *
   * @return string
   *   Simplified browser name.
   */
  protected function getAgent($name = '') {
    if (isset($_SERVER['HTTP_USER_AGENT'])) {

      // Match popular user agents.
      $agent = $_SERVER['HTTP_USER_AGENT'];
      if (preg_match("/like\\sGecko\\)\\sChrome\\//", $agent)) {
        $name = 'Chrome';
      }
      elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== FALSE) {
        $name = 'Firefox';
      }
      elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) {
        $name = 'Internet Explorer';
      }
      elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') !== FALSE) {
        $name = 'Safari';
      }
      else {

        // Otherwise filter agent and truncate to column size.
        $name = substr($agent, 0, 255);
      }
    }
    return $name;
  }

  /**
   * {@inheritdoc}
   */
  public function ready() {
    return TRUE;
  }

}

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
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
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.
TfaBasePlugin::$alreadyAccepted protected property Whether the code has been used before.
TfaBasePlugin::$code protected property The user submitted code to be validated.
TfaBasePlugin::$codeLength protected property The allowed code length.
TfaBasePlugin::$encryptionProfile protected property Encryption profile.
TfaBasePlugin::$encryptService protected property Encryption service.
TfaBasePlugin::$errorMessages protected property The error for the current validation.
TfaBasePlugin::$isValid protected property Whether the validation succeeded or not.
TfaBasePlugin::$uid protected property The user id.
TfaBasePlugin::$userData protected property Provides the user data service object.
TfaBasePlugin::alreadyAcceptedCode protected function Whether code has already been used.
TfaBasePlugin::decrypt protected function Decrypt a encrypted string.
TfaBasePlugin::encrypt protected function Encrypt a plaintext string.
TfaBasePlugin::getErrorMessages public function Get error messages suitable for form_set_error().
TfaBasePlugin::getLabel public function Get the plugin label.
TfaBasePlugin::storeAcceptedCode protected function Store validated code to prevent replay attack.
TfaBasePlugin::validate protected function Validate code. 1
TfaDataTrait::deleteUserData protected function Deletes data stored for the current validated user account.
TfaDataTrait::getUserData protected function Returns data stored for the current validated user account.
TfaDataTrait::setUserData protected function Store user specific information.
TfaDataTrait::tfaGetTfaData protected function Get TFA data for an account.
TfaDataTrait::tfaSaveTfaData public function Save TFA data for an account.
TfaTrustedBrowser::$cookieName protected property The cookie name.
TfaTrustedBrowser::$expiration protected property Cookie expiration time.
TfaTrustedBrowser::$trustBrowser protected property Trust browser.
TfaTrustedBrowser::deleteTrusted protected function Delete users trusted browser.
TfaTrustedBrowser::finalize public function Finalize the browser setup.
TfaTrustedBrowser::generateBrowserId protected function Generate a random value to identify the browser.
TfaTrustedBrowser::getAgent protected function Get simplified browser name from user agent.
TfaTrustedBrowser::getForm public function Get TFA process form from plugin. Overrides TfaValidationInterface::getForm
TfaTrustedBrowser::loginAllowed public function Whether login is allowed. Overrides TfaLoginInterface::loginAllowed
TfaTrustedBrowser::ready public function Determine if the plugin can run for the current TFA context. Overrides TfaBasePlugin::ready
TfaTrustedBrowser::setTrusted protected function Store browser value and issue cookie for user.
TfaTrustedBrowser::setUsed protected function Updated browser last used time.
TfaTrustedBrowser::submitForm public function Submit form. Overrides TfaBasePlugin::submitForm
TfaTrustedBrowser::trustedBrowser protected function Check if browser id matches user's saved browser.
TfaTrustedBrowser::validateForm public function Validate form. Overrides TfaValidationInterface::validateForm
TfaTrustedBrowser::__construct public function Constructs a new Tfa plugin object. Overrides TfaBasePlugin::__construct