View source
<?php
declare (strict_types=1);
namespace Drupal\sms\Provider;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\Random;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Utility\Token;
use Drupal\sms\Direction;
use Drupal\sms\Exception\PhoneNumberSettingsException;
use Drupal\sms\Message\SmsMessage;
class PhoneNumberVerification implements PhoneNumberVerificationInterface {
protected $smsProvider;
protected $phoneNumberSettings;
protected $phoneNumberVerificationStorage;
protected $token;
protected $configFactory;
protected $time;
public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, Token $token, SmsProviderInterface $sms_provider, TimeInterface $time) {
$this->smsProvider = $sms_provider;
$this->phoneNumberSettings = $entity_type_manager
->getStorage('phone_number_settings');
$this->phoneNumberVerificationStorage = $entity_type_manager
->getStorage('sms_phone_number_verification');
$this->token = $token;
$this->configFactory = $config_factory;
$this->time = $time;
}
public function getPhoneNumberSettings($entity_type_id, $bundle) {
return $this->phoneNumberSettings
->load($entity_type_id . '.' . $bundle);
}
public function getPhoneNumberSettingsForEntity(EntityInterface $entity) {
if (!($phone_number_settings = $this
->getPhoneNumberSettings($entity
->getEntityTypeId(), $entity
->bundle()))) {
throw new PhoneNumberSettingsException(sprintf('Entity phone number config does not exist for bundle %s:%s', $entity
->getEntityTypeId(), $entity
->bundle()));
}
return $phone_number_settings;
}
public function getPhoneVerificationByCode($code) {
$entities = $this->phoneNumberVerificationStorage
->loadByProperties([
'code' => $code,
]);
return reset($entities);
}
public function getPhoneVerificationByPhoneNumber($phone_number, $verified = TRUE, $entity_type = NULL) {
$properties['phone'] = $phone_number;
if (isset($entity_type)) {
$properties['entity__target_type'] = $entity_type;
}
if (isset($verified)) {
$properties['status'] = (int) $verified;
}
return $this->phoneNumberVerificationStorage
->loadByProperties($properties);
}
public function getPhoneVerificationByEntity(EntityInterface $entity, $phone_number) {
$entities = $this->phoneNumberVerificationStorage
->loadByProperties([
'entity__target_id' => $entity
->id(),
'entity__target_type' => $entity
->getEntityTypeId(),
'phone' => $phone_number,
]);
return reset($entities);
}
public function newPhoneVerification(EntityInterface $entity, $phone_number) {
$config = $this
->getPhoneNumberSettingsForEntity($entity);
$message = $config
->getVerificationMessage() ?: '';
$random = new Random();
$code = strtoupper($random
->name(6));
$phone_verification = $this->phoneNumberVerificationStorage
->create();
$phone_verification
->setCode($code)
->setStatus(FALSE)
->setPhoneNumber($phone_number)
->setEntity($entity)
->save();
if ($phone_verification) {
$sms_message = new SmsMessage();
$sms_message
->addRecipient($phone_number)
->setOption('_is_verification_message', TRUE)
->setMessage($message)
->setDirection(Direction::OUTGOING);
$data['sms-message'] = $sms_message;
$data['sms_verification_code'] = $phone_verification
->getCode();
$sms_message
->setMessage($this->token
->replace($message, $data))
->setAutomated(FALSE);
$this->smsProvider
->queue($sms_message);
}
return $phone_verification;
}
public function updatePhoneVerificationByEntity(EntityInterface $entity) {
try {
$phone_number_settings = $this
->getPhoneNumberSettingsForEntity($entity);
$field_name = $phone_number_settings
->getFieldName('phone_number');
if (!empty($field_name)) {
$items_original = isset($entity->original) ? $entity->original->{$field_name} : NULL;
$items = $entity->{$field_name};
}
} catch (PhoneNumberSettingsException $e) {
}
if (!isset($items)) {
return;
}
$numbers = [];
assert($items instanceof FieldItemListInterface);
foreach ($items as $item) {
$phone_number = $item->value;
$numbers[] = $phone_number;
if (!$this
->getPhoneVerificationByEntity($entity, $phone_number)) {
$this
->newPhoneVerification($entity, $phone_number);
}
}
if (isset($items_original) && !$items
->equals($items_original)) {
foreach ($items_original as $item) {
$phone_number = $item->value;
if (!in_array($phone_number, $numbers)) {
if ($phone_verification = $this
->getPhoneVerificationByEntity($entity, $phone_number)) {
$phone_verification
->delete();
}
}
}
}
}
public function deletePhoneVerificationByEntity(EntityInterface $entity) {
try {
$this
->getPhoneNumberSettingsForEntity($entity);
$verification_entities = $this->phoneNumberVerificationStorage
->loadByProperties([
'entity__target_id' => $entity
->id(),
'entity__target_type' => $entity
->getEntityTypeId(),
]);
$this->phoneNumberVerificationStorage
->delete($verification_entities);
} catch (PhoneNumberSettingsException $e) {
}
}
public function purgeExpiredVerifications() {
$current_time = $this->time
->getRequestTime();
$verification_ids = [];
foreach ($this->configFactory
->listAll('sms.phone.') as $config_id) {
$config = $this->configFactory
->get($config_id);
$lifetime = $config
->get('verification_code_lifetime');
if (!empty($lifetime)) {
$verification_ids += $this->phoneNumberVerificationStorage
->getQuery()
->condition('entity__target_type', $config
->get('entity_type'))
->condition('bundle', $config
->get('bundle'))
->condition('status', 0)
->condition('created', $current_time - $lifetime, '<')
->execute();
}
}
foreach ($this->phoneNumberVerificationStorage
->loadMultiple($verification_ids) as $phone_number_verification) {
if ($entity = $phone_number_verification
->getEntity()) {
try {
$config = $this
->getPhoneNumberSettingsForEntity($entity);
$purge = $config
->getPurgeVerificationPhoneNumber();
$field_name = $config
->getFieldName('phone_number');
if (!empty($purge) && isset($entity->{$field_name})) {
$entity->{$field_name}
->filter(function ($item) use ($phone_number_verification) {
return $item->value != $phone_number_verification
->getPhoneNumber();
});
$entity
->save();
}
} catch (EntityStorageException $e) {
}
}
$this->phoneNumberVerificationStorage
->delete([
$phone_number_verification,
]);
}
}
}