sqrl-2.0.0-rc1/src/Identities.php

src/Identities.php
<?php

namespace Drupal\sqrl;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Random;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\sqrl\Entity\IdentityInterface;
use Drupal\user\UserDataInterface;
use Drupal\user\UserInterface;

/**
 * Provides services for identities.
 */
class Identities {

  use StringManipulation;

  private const DEFAULT_EMAIL_DOMAIN = '@local.localhost';

  /**
   * The IDK.
   *
   * @var string
   */
  protected string $idk;

  /**
   * The PIDK.
   *
   * @var string
   */
  protected string $pidk;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

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

  /**
   * The random service.
   *
   * @var \Drupal\Component\Utility\Random
   */
  protected Random $random;

  /**
   * The configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected ImmutableConfig $config;

  /**
   * The log channel.
   *
   * @var \Drupal\sqrl\Log
   */
  protected Log $logger;

  /**
   * Identities constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\user\UserDataInterface $user_data
   *   The user data service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\sqrl\Log $logger
   *   The log channel.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, UserDataInterface $user_data, ConfigFactoryInterface $config_factory, Log $logger) {
    $this->entityTypeManager = $entity_type_manager;
    $this->userData = $user_data;
    $this->random = new Random();
    $this->config = $config_factory->get('sqrl.settings');
    $this->logger = $logger;
  }

  /**
   * Set the IDK.
   *
   * @param string $idk
   *   The IDK.
   *
   * @return self
   *   This identities.
   */
  public function setIdk(string $idk): Identities {
    $this->idk = $idk;
    return $this;
  }

  /**
   * Sets the PIDK.
   *
   * @param string $pidk
   *   The PIDK.
   *
   * @return self
   *   This identities.
   */
  public function setPidk(string $pidk): Identities {
    $this->pidk = $pidk;
    return $this;
  }

  /**
   * Gets an identity.
   *
   * @param string $key
   *   The key.
   * @param int|null $uid
   *   The optional user ID.
   *
   * @return \Drupal\sqrl\Entity\IdentityInterface|null
   *   The identity, if found, NULL otherwise.
   */
  protected function getIdentity(string $key, ?int $uid = NULL): ?IdentityInterface {
    if (empty($key)) {
      return NULL;
    }
    $conditions = [
      'idk' => $key,
    ];
    if ($uid !== NULL) {
      $conditions['user'] = $uid;
    }
    try {
      /** @var \Drupal\sqrl\Entity\IdentityInterface[] $identities */
      $identities = $this->entityTypeManager->getStorage('sqrl_identity')->loadByProperties($conditions);
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException) {
      // Deliberately ignored.
    }
    if (!empty($identities)) {
      return reset($identities);
    }
    return NULL;
  }

  /**
   * Get all identities.
   *
   * @param int $uid
   *   The user ID.
   *
   * @return \Drupal\sqrl\Entity\IdentityInterface[]
   *   The found identities for the given user ID.
   */
  public function getIdentities(int $uid): array {
    try {
      /** @var \Drupal\sqrl\Entity\IdentityInterface[] $identities */
      $identities = $this->entityTypeManager->getStorage('sqrl_identity')->loadByProperties([
        'user' => $uid,
      ]);
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException) {
      // Deliberately ignored.
    }
    if (!empty($identities)) {
      return $identities;
    }
    return [];
  }

  /**
   * Get an identity by IDK.
   *
   * @param int|null $uid
   *   The optional user ID.
   *
   * @return \Drupal\sqrl\Entity\IdentityInterface|null
   *   The identity, if found, NULL otherwise.
   */
  public function getIdentityByIdk(?int $uid = NULL): ?IdentityInterface {
    return $this->getIdentity($this->idk, $uid);
  }

  /**
   * Get an identity by PIDK.
   *
   * @param int|null $uid
   *   The optional user ID.
   *
   * @return \Drupal\sqrl\Entity\IdentityInterface|null
   *   The identity, if found, NULL otherwise.
   */
  public function getIdentityByPidk(?int $uid = NULL): ?IdentityInterface {
    return $this->getIdentity($this->pidk, $uid);
  }

  /**
   * Removes all identities for a cancelled account.
   *
   * @param \Drupal\user\UserInterface $account
   *   The account.
   */
  public function cancel(UserInterface $account): void {
    foreach ($this->getIdentities($account->id()) as $identity) {
      try {
        if ($identity->removeUser($account)) {
          $identity->save();
        }
      }
      catch (EntityStorageException) {
      }
    }
  }

  /**
   * Updates the password of an identity.
   *
   * @param \Drupal\sqrl\Entity\IdentityInterface $identity
   *   The identity.
   * @param string $secret
   *   The secret.
   * @param bool $encrypt
   *   Flag, if the password should be encrypted.
   */
  public function updatePassword(IdentityInterface $identity, string $secret, bool $encrypt): void {
    if ($this->config->get('allow_multiple_ids_per_account') || $this->config->get('allow_multiple_accounts_per_id')) {
      // When there is NOT a 1:1-relationship between SQRL ID and user account,
      // we can't encrypt and decrypt user passwords while they should be
      // unaccessible.
      return;
    }

    $users = $identity->getUsers();
    if (count($users) !== 1) {
      // Well, something is not as it should be, let's log this as an exception.
      $this->logger->error('Identity %id should have exactly one account associated', [
        '%id' => $identity->id(),
      ]);
      return;
    }

    $ciphers = openssl_get_cipher_methods();
    $cipher = empty($ciphers) ? '' : reset($ciphers);

    $nonceSize = openssl_cipher_iv_length($cipher);
    $strong = TRUE;
    /* @noinspection CryptographicallySecureRandomnessInspection */
    $iv = openssl_random_pseudo_bytes($nonceSize, $strong);
    if (!$strong  || !$iv) {
      $this->logger->error('Your system does not produce secure randomness');
      return;
    }

    $user = reset($users);
    $name = 'enc-pw-id-' . $identity->id();

    if ($encrypt) {
      $password = openssl_encrypt(
        $user->getPassword(),
        $cipher,
        $secret,
        OPENSSL_RAW_DATA,
        $iv
      );
      $this->userData->set('sqrl', $user->id(), $name, $this->base64Encode($password));
      $user->setPassword('');
    }
    else {
      $password = openssl_decrypt(
        $this->base64Decode($this->userData->get('sqrl', $user->id(), $name)),
        $cipher,
        $secret,
        OPENSSL_RAW_DATA,
        $iv
      );
      $user->setPassword($password);
    }
    try {
      $user->save();
    }
    catch (EntityStorageException) {
      $this->logger->error('Unable to save user account when encrypting/decrypting password.');
    }
  }

  /**
   * Determines if the user has at least one enabled identity.
   *
   * @param int $uid
   *   The user ID.
   *
   * @return bool
   *   TRUE, if the user has at least one enabled identity, FALSE otherwise.
   */
  public function hasUserEnabledIdentities(int $uid): bool {
    try {
      return (bool) $this->entityTypeManager->getStorage('sqrl_identity')->getQuery()
        ->accessCheck(FALSE)
        ->condition('user', $uid)
        ->condition('status', 1)
        ->notExists('sid')
        ->count()
        ->execute();
    }
    catch (InvalidPluginDefinitionException | PluginNotFoundException) {
    }
    return FALSE;
  }

  /**
   * Gets a dummy, random email address.
   *
   * @return string
   *   The email address.
   */
  public function dummyMail(): string {
    return urlencode($this->random->string(15)) . self::DEFAULT_EMAIL_DOMAIN;
  }

  /**
   * Determines if the email address is a dummy one.
   *
   * @param string $mail
   *   The email address.
   *
   * @return bool
   *   TRUE, if the email address is a dummy one, FALSE otherwise.
   */
  public function isDummyMail(string $mail): bool {
    return str_contains($mail, self::DEFAULT_EMAIL_DOMAIN);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc