You are here

abstract class MobileNumberTfa in Mobile Number 8

Same name and namespace in other branches
  1. 2.0.x src/Plugin/TfaValidation/MobileNumberTfa.php \Drupal\mobile_number\Plugin\TfaValidation\MobileNumberTfa

Class MobileNumberTfa is a validation and sending plugin for TFA.

@package Drupal\mobile_number

Hierarchy

Expanded class hierarchy of MobileNumberTfa

File

src/Plugin/TfaValidation/MobileNumberTfa.php, line 27
MobileNumberTfa.php

Namespace

Drupal\mobile_number\Plugin\TfaValidation
View source
abstract class MobileNumberTfa extends TfaBasePlugin implements TfaValidationInterface, TfaSendInterface {

  /**
   * Libphonenumber Utility object.
   *
   * @var \Drupal\mobile_number\MobileNumberUtilInterface
   */
  public $mobileNumberUtil;

  /**
   * Libphonenumber phone number object.
   *
   * @var \libphonenumber\PhoneNumber
   */
  public $mobileNumber;

  /**
   * The Messenger service.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, UserDataInterface $user_data, EncryptionProfileManagerInterface $encryption_profile_manager, EncryptServiceInterface $encrypt_service, MessengerInterface $messenger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $user_data, $encryption_profile_manager, $encrypt_service);
    $this->mobileNumberUtil = \Drupal::service('mobile_number.util');
    $this->messenger = $messenger;
    if (!empty($context['validate_context']) && !empty($context['validate_context']['code'])) {
      $this->code = $context['validate_context']['code'];
    }
    if (!empty($context['validate_context']) && !empty($context['validate_context']['verification_token'])) {
      $this->verificationToken = $context['validate_context']['verification_token'];
    }
    $this->codeLength = 4;
    if ($m = $this->mobileNumberUtil
      ->tfaAccountNumber($context['uid'])) {
      try {
        $this->mobileNumber = $this->mobileNumberUtil
          ->testMobileNumber($m);
      } catch (MobileNumberException $e) {
        throw new Exception("Two factor authentication failed: \n" . $e
          ->getMessage(), $e
          ->getCode());
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function ready() {
    return $this->mobileNumberUtil
      ->tfaAccountNumber($this->context['uid']) ? TRUE : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function begin() {
    if (!$this->code) {
      if (!$this
        ->sendCode()) {
        $this->messenger
          ->addError(t('Unable to deliver the code. Please contact support.'));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getForm(array $form, FormStateInterface $form_state) {
    $local_number = $this->mobileNumberUtil
      ->getLocalNumber($this->mobileNumber);
    $numberClue = str_pad(substr($local_number, -3, 3), strlen($local_number), 'X', STR_PAD_LEFT);
    $numberClue = substr_replace($numberClue, '-', 3, 0);
    $form['code'] = [
      '#type' => 'textfield',
      '#title' => t('Verification Code'),
      '#required' => TRUE,
      '#description' => t('A verification code was sent to %clue. Enter the @length-character code sent to your device.', [
        '@length' => $this->codeLength,
        '%clue' => $numberClue,
      ]),
    ];
    $form['actions']['#type'] = 'actions';
    $form['actions']['login'] = [
      '#type' => 'submit',
      '#value' => t('Verify'),
    ];
    $form['actions']['resend'] = [
      '#type' => 'submit',
      '#value' => t('Resend'),
      '#submit' => [
        'tfa_form_submit',
      ],
      '#limit_validation_errors' => [],
    ];
    return $form;
  }

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

    // If operation is resend then do not attempt to validate code.
    if ($form_state['values']['op'] === $form_state['values']['resend']) {
      return TRUE;
    }
    elseif (!$this
      ->verifyCode($form_state['values']['code'])) {
      $this->errorMessages['code'] = t('Invalid code.');
      return FALSE;
    }
    else {
      return TRUE;
    }
  }

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

    // Resend code if pushed.
    if ($form_state['values']['op'] === $form_state['values']['resend']) {
      if (!$this->mobileNumberUtil
        ->checkFlood($this->mobileNumber, 'sms')) {
        $this->messenger
          ->addError(t('Too many verification code requests, please try again shortly.'));
      }
      elseif (!$this
        ->sendCode()) {
        $this->messenger
          ->addError(t('Unable to deliver the code. Please contact support.'));
      }
      else {
        $this->messenger
          ->addMessage(t('Hello world'));
      }
      return FALSE;
    }
    else {
      return parent::submitForm($form, $form_state);
    }
  }

  /**
   * Return context for this plugin.
   */
  public function getPluginContext() {
    return [
      'code' => $this->code,
      'verification_token' => !empty($this->verificationToken) ? $this->verificationToken : '',
    ];
  }

  /**
   * Send the code via the client.
   *
   * @return bool
   *   Where sending sms was successful.
   */
  public function sendCode() {
    $user = \Drupal::entityTypeManager()
      ->getStorage('user')
      ->load($this->context['uid']);
    $this->code = $this->mobileNumberUtil
      ->generateVerificationCode($this->codeLength);
    try {
      $message = \Drupal::configFactory()
        ->getEditable('mobile_number.settings')
        ->get('tfa_message');
      $message = $message ? $message : $this->mobileNumberUtil->MOBILE_NUMBER_DEFAULT_SMS_MESSAGE;
      if (!($this->verificationToken = $this->mobileNumberUtil
        ->sendVerification($this->mobileNumber, $message, $this->code, [
        'user' => $user,
      ]))) {
        return FALSE;
      }

      // @todo Consider storing date_sent or date_updated to inform user.
      \Drupal::logger('mobile_number_tfa')
        ->info('TFA validation code sent to user @uid', [
        '@uid' => $this->context['uid'],
      ]);
      return TRUE;
    } catch (Exception $e) {
      \Drupal::logger('mobile_number_tfa')
        ->error('Send message error to user @uid. Status code: @code, message: @message', [
        '@uid' => $this->context['uid'],
        '@code' => $e
          ->getCode(),
        '@message' => $e
          ->getMessage(),
      ]);
      return FALSE;
    }
  }

  /**
   * Verifies the given code with this session's verification token.
   *
   * @param string $code
   *   Code.
   *
   * @return bool
   *   Verification status.
   */
  public function verifyCode($code) {
    return $this->isValid = $this->mobileNumberUtil
      ->verifyCode($this->mobileNumber, $code, $this->verificationToken);
  }

  /**
   * {@inheritdoc}
   */
  public function getFallbacks() {
    return $this->pluginDefinition['fallbacks'] ?: '';
  }

  /**
   * {@inheritdoc}
   */
  public function purge() {
  }

}

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
MobileNumberTfa::$messenger protected property The Messenger service.
MobileNumberTfa::$mobileNumber public property Libphonenumber phone number object.
MobileNumberTfa::$mobileNumberUtil public property Libphonenumber Utility object.
MobileNumberTfa::begin public function TFA process begin. Overrides TfaSendInterface::begin
MobileNumberTfa::getFallbacks public function
MobileNumberTfa::getForm public function Get TFA process form from plugin. Overrides TfaValidationInterface::getForm
MobileNumberTfa::getPluginContext public function Return context for this plugin.
MobileNumberTfa::purge public function
MobileNumberTfa::ready public function Determine if the plugin can run for the current TFA context. Overrides TfaBasePlugin::ready
MobileNumberTfa::sendCode public function Send the code via the client.
MobileNumberTfa::submitForm public function Submit form. Overrides TfaBasePlugin::submitForm
MobileNumberTfa::validateForm public function Validate form. Overrides TfaValidationInterface::validateForm
MobileNumberTfa::verifyCode public function Verifies the given code with this session's verification token.
MobileNumberTfa::__construct public function Constructs a new Tfa plugin object. Overrides TfaBasePlugin::__construct
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.
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.