class TfaRecoveryCode in Two-factor Authentication (TFA) 8
Recovery validation class for performing recovery codes validation.
Plugin annotation
@TfaValidation(
id = "tfa_recovery_code",
label = @Translation("TFA Recovery Code"),
description = @Translation("TFA Recovery Code Validation Plugin"),
setupPluginId = "tfa_recovery_code_setup",
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\tfa\Plugin\TfaBasePlugin uses DependencySerializationTrait, TfaDataTrait
- class \Drupal\tfa\Plugin\TfaValidation\TfaRecoveryCode implements ContainerFactoryPluginInterface, TfaValidationInterface uses StringTranslationTrait, TfaRandomTrait
- class \Drupal\tfa\Plugin\TfaBasePlugin uses DependencySerializationTrait, TfaDataTrait
Expanded class hierarchy of TfaRecoveryCode
2 files declare their use of TfaRecoveryCode
- TfaRecoveryCodeSetup.php in src/
Plugin/ TfaSetup/ TfaRecoveryCodeSetup.php - TfaRecoveryCodeTest.php in tests/
src/ Unit/ Plugin/ TfaValidation/ TfaRecoveryCodeTest.php
File
- src/
Plugin/ TfaValidation/ TfaRecoveryCode.php, line 28
Namespace
Drupal\tfa\Plugin\TfaValidationView source
class TfaRecoveryCode extends TfaBasePlugin implements TfaValidationInterface, ContainerFactoryPluginInterface {
use TfaRandomTrait;
use StringTranslationTrait;
/**
* The number of recovery codes to generate.
*
* @var int
*/
protected $codeLimit = 10;
/**
* Constructs a new Tfa plugin object.
*
* @param array $configuration
* The plugin configuration.
* @param string $plugin_id
* The plugin id.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\user\UserDataInterface $user_data
* User data object to store user specific information.
* @param \Drupal\encrypt\EncryptionProfileManagerInterface $encryption_profile_manager
* Encryption profile manager.
* @param \Drupal\encrypt\EncryptServiceInterface $encrypt_service
* Encryption service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, UserDataInterface $user_data, EncryptionProfileManagerInterface $encryption_profile_manager, EncryptServiceInterface $encrypt_service, ConfigFactoryInterface $config_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $user_data, $encryption_profile_manager, $encrypt_service);
$codes_amount = $config_factory
->get('tfa.settings')
->get('validation_plugin_settings.tfa_recovery_code.recovery_codes_amount');
if (!empty($codes_amount)) {
$this->codeLimit = $codes_amount;
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('user.data'), $container
->get('encrypt.encryption_profile.manager'), $container
->get('encryption'), $container
->get('config.factory'));
}
/**
* {@inheritdoc}
*/
public function ready() {
$codes = $this
->getCodes();
return !empty($codes);
}
/**
* {@inheritdoc}
*/
public function getForm(array $form, FormStateInterface $form_state) {
$form['code'] = [
'#type' => 'textfield',
'#title' => $this
->t('Enter one of your recovery codes'),
'#required' => TRUE,
'#description' => $this
->t('Recovery codes were generated when you first set up TFA. Format: XXX XXX XXX'),
'#attributes' => [
'autocomplete' => 'off',
],
];
$form['actions']['#type'] = 'actions';
$form['actions']['login'] = [
'#type' => 'submit',
'#button_type' => 'primary',
'#value' => $this
->t('Verify'),
];
return $form;
}
/**
* Configuration form for the recovery code plugin.
*
* @param \Drupal\Core\Config\Config $config
* Config object for tfa settings.
* @param array $state
* Form state array determines if this form should be shown.
*
* @return array
* Form array specific for this validation plugin.
*/
public function buildConfigurationForm(Config $config, array $state = []) {
$settings_form['recovery_codes_amount'] = [
'#type' => 'number',
'#title' => $this
->t('Recovery Codes Amount'),
'#default_value' => $this->codeLimit,
'#description' => $this
->t('Number of Recovery Codes To Generate.'),
'#min' => 1,
'#size' => 2,
'#states' => $state,
'#required' => TRUE,
];
return $settings_form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array $form, FormStateInterface $form_state) {
$values = $form_state
->getValues();
return $this
->validate($values['code']);
}
/**
* Simple validate for web services.
*
* @param int $code
* OTP Code.
*
* @return bool
* True if validation was successful otherwise false.
*/
public function validateRequest($code) {
if ($this
->validate($code)) {
$this
->storeAcceptedCode($code);
return TRUE;
}
return FALSE;
}
/**
* Generate an array of secure recovery codes.
*
* @return array
* An array of randomly generated codes.
*
* @throws \Exception
*/
public function generateCodes() {
$codes = [];
for ($i = 0; $i < $this->codeLimit; $i++) {
$codes[] = $this
->randomCharacters(9, '1234567890');
}
return $codes;
}
/**
* Get unused recovery codes.
*
* @todo consider returning used codes so validate() can error with
* appropriate message
*
* @return array
* Array of codes indexed by ID.
*
* @throws \Drupal\encrypt\Exception\EncryptionMethodCanNotDecryptException
* @throws \Drupal\encrypt\Exception\EncryptException
*/
public function getCodes() {
$codes = $this
->getUserData('tfa', 'tfa_recovery_code', $this->uid, $this->userData) ?: [];
array_walk($codes, function (&$v, $k) {
$v = $this
->decrypt($v);
});
return $codes;
}
/**
* Save recovery codes for current account.
*
* @param array $codes
* Recovery codes for current account.
*
* @throws \Drupal\encrypt\Exception\EncryptException
*/
public function storeCodes(array $codes) {
$this
->deleteCodes();
// Encrypt code for storage.
array_walk($codes, function (&$v, $k) {
$v = $this
->encrypt($v);
});
$data = [
'tfa_recovery_code' => $codes,
];
$this
->setUserData('tfa', $data, $this->uid, $this->userData);
}
/**
* Delete existing codes.
*/
protected function deleteCodes() {
// Delete any existing codes.
$this
->deleteUserData('tfa', 'tfa_recovery_code', $this->uid, $this->userData);
}
/**
* {@inheritdoc}
*/
protected function validate($code) {
$this->isValid = FALSE;
// Get codes and compare.
$codes = $this
->getCodes();
if (empty($codes)) {
$this->errorMessages['recovery_code'] = $this
->t('You have no unused codes available.');
return FALSE;
}
// Remove empty spaces.
$code = str_replace(' ', '', $code);
foreach ($codes as $id => $stored) {
// Remove spaces from stored code.
if (hash_equals(trim(str_replace(' ', '', $stored)), $code)) {
$this->isValid = TRUE;
unset($codes[$id]);
$this
->storeCodes($codes);
return $this->isValid;
}
}
$this->errorMessages['recovery_code'] = $this
->t('Invalid recovery code.');
return $this->isValid;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TfaBasePlugin:: |
protected | property | Whether the code has been used before. | |
TfaBasePlugin:: |
protected | property | The user submitted code to be validated. | |
TfaBasePlugin:: |
protected | property | The allowed code length. | |
TfaBasePlugin:: |
protected | property | Encryption profile. | |
TfaBasePlugin:: |
protected | property | Encryption service. | |
TfaBasePlugin:: |
protected | property | The error for the current validation. | |
TfaBasePlugin:: |
protected | property | Whether the validation succeeded or not. | |
TfaBasePlugin:: |
protected | property | The user id. | |
TfaBasePlugin:: |
protected | property | Provides the user data service object. | |
TfaBasePlugin:: |
protected | function | Whether code has already been used. | |
TfaBasePlugin:: |
protected | function | Decrypt a encrypted string. | |
TfaBasePlugin:: |
protected | function | Encrypt a plaintext string. | |
TfaBasePlugin:: |
public | function | Get error messages suitable for form_set_error(). | |
TfaBasePlugin:: |
public | function | Get the plugin label. | |
TfaBasePlugin:: |
protected | function | Store validated code to prevent replay attack. | |
TfaBasePlugin:: |
public | function | Submit form. | 1 |
TfaDataTrait:: |
protected | function | Deletes data stored for the current validated user account. | |
TfaDataTrait:: |
protected | function | Returns data stored for the current validated user account. | |
TfaDataTrait:: |
protected | function | Store user specific information. | |
TfaDataTrait:: |
protected | function | Get TFA data for an account. | |
TfaDataTrait:: |
public | function | Save TFA data for an account. | |
TfaRandomTrait:: |
protected | property | Letters allowed during random string generation. | |
TfaRandomTrait:: |
protected | property | Numbers allowed during random string generation. | |
TfaRandomTrait:: |
protected | function | Generate random characters of the given length and allowable characters. | |
TfaRandomTrait:: |
public | function | Generate a random integer of the given character length. | |
TfaRandomTrait:: |
public | function | Generate a random string of the given character length. | |
TfaRecoveryCode:: |
protected | property | The number of recovery codes to generate. | |
TfaRecoveryCode:: |
public | function | Configuration form for the recovery code plugin. | |
TfaRecoveryCode:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
|
TfaRecoveryCode:: |
protected | function | Delete existing codes. | |
TfaRecoveryCode:: |
public | function | Generate an array of secure recovery codes. | |
TfaRecoveryCode:: |
public | function | Get unused recovery codes. | |
TfaRecoveryCode:: |
public | function |
Get TFA process form from plugin. Overrides TfaValidationInterface:: |
|
TfaRecoveryCode:: |
public | function |
Determine if the plugin can run for the current TFA context. Overrides TfaBasePlugin:: |
1 |
TfaRecoveryCode:: |
public | function | Save recovery codes for current account. | |
TfaRecoveryCode:: |
protected | function |
Validate code. Overrides TfaBasePlugin:: |
|
TfaRecoveryCode:: |
public | function |
Validate form. Overrides TfaValidationInterface:: |
|
TfaRecoveryCode:: |
public | function | Simple validate for web services. | |
TfaRecoveryCode:: |
public | function |
Constructs a new Tfa plugin object. Overrides TfaBasePlugin:: |