You are here

class BakeryUncrumbleForm in Bakery Single Sign-On System 8.2

Contribute form.

Hierarchy

Expanded class hierarchy of BakeryUncrumbleForm

File

src/Forms/BakeryUncrumbleForm.php, line 18

Namespace

Drupal\bakery\Forms
View source
class BakeryUncrumbleForm extends FormBase implements ContainerInjectionInterface {

  /**
   * For generating hashed password we check with database.
   *
   * @var \Drupal\Core\Password\PasswordInterface
   */
  private $passwordHasher;

  /**
   * @var \Drupal\Core\Database\Connection
   */
  private $database;

  /**
   * @var \Drupal\bakery\Kitchen
   */
  private $kitchen;

  /**
   * Class constructor.
   */
  public function __construct(PasswordInterface $password_hasher, Connection $connection, Kitchen $kitchen) {

    // TODO some sore of session storage abstraction?
    $this->passwordHasher = $password_hasher;
    $this->database = $connection;
    $this->kitchen = $kitchen;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('password'), $container
      ->get('database'), $container
      ->get('bakery.kitchen'));
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'bakery_forms_uncrumble_form';
  }

  /**
   * Form to let users repair minor problems themselves.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $cookie = $this->kitchen
      ->taste(Kitchen::CHOCOLATE_CHIP);

    // Analyze.
    $query = $this->database
      ->select('users_field_data', 'u')
      ->fields('u', [
      'uid',
      'name',
      'mail',
    ])
      ->condition('u.uid', 0, '!=')
      ->condition('u.mail', '', '!=')
      ->where("LOWER(u.mail) = LOWER(:mail)", [
      ':mail' => $cookie['mail'],
    ]);
    $result = $query
      ->execute();
    $samemail = $result
      ->fetchObject();
    $query = $this->database
      ->select('users_field_data', 'u')
      ->fields('u', [
      'uid',
      'name',
      'mail',
    ])
      ->condition('u.uid', 0, '!=')
      ->where("LOWER(u.name) = LOWER(:name)", [
      ':name' => $cookie['name'],
    ]);
    $result = $query
      ->execute();
    $samename = $result
      ->fetchObject();
    $form['name'] = [
      '#type' => 'textfield',
      '#title' => t('Username'),
      '#value' => $cookie['name'],
      '#disabled' => TRUE,
      '#required' => TRUE,
    ];
    $form['mail'] = [
      '#type' => 'item',
      '#title' => t('Email address'),
      '#value' => $cookie['mail'],
      '#required' => TRUE,
    ];
    $form['pass'] = [
      '#type' => 'password',
      '#title' => t('Password'),
      '#description' => t('Enter the password that accompanies your username.'),
      '#required' => TRUE,
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => t('Repair account'),
      '#weight' => 2,
    ];
    $help = '';
    $count = $this->database
      ->select('users_field_data', 'u')
      ->fields('u', [
      'uid',
    ])
      ->condition('init', $cookie['init'], '=')
      ->countQuery()
      ->execute()
      ->fetchField();
    if ($count > 1) {
      $this
        ->messenger()
        ->addMessage(t('Multiple accounts are associated with your master account. This must be fixed manually. <a href="@contact">Please contact the site administrator.</a>', [
        '%email' => $cookie['mail'],
        '@contact' => $this
          ->configFactory()
          ->get('bakery.settings')
          ->get('bakery_master') . 'contact',
      ]));
      $form['pass']['#disabled'] = TRUE;
      $form['submit']['#disabled'] = TRUE;
    }
    elseif ($samename && $samemail && $samename->uid != $samemail->uid) {
      $this
        ->messenger()
        ->addMessage(t('Both an account with matching name and an account with matching email address exist, but they are different accounts. This must be fixed manually. <a href="@contact">Please contact the site administrator.</a>', [
        '%email' => $cookie['mail'],
        '@contact' => $this
          ->configFactory()
          ->get('bakery.settings')
          ->get('bakery_master') . 'contact',
      ]));
      $form['pass']['#disabled'] = TRUE;
      $form['submit']['#disabled'] = TRUE;
    }
    elseif ($samename) {
      $site_name = $this
        ->configFactory()
        ->get('system.site')
        ->get('name');
      $help = t("An account with a matching username was found. Repairing it will reset the email address to match your master account. If this is the correct account, please enter your %site password.", [
        '%site' => $site_name,
      ]);

      // This is a borderline information leak.
      // $form['mail']['#value'] = $samename->mail;.
      $form['mail']['#value'] = t('<em>*hidden*</em>');
      $form['mail']['#description'] = t('Will change to %new.', [
        '%new' => $cookie['mail'],
      ]);
    }
    elseif ($samemail) {
      $site_name = $this
        ->configFactory()
        ->get('system.site')
        ->get('name');
      $help = t("An account with a matching email address was found. Repairing it will reset the username to match your master account. If this is the correct account, please enter your %site password.", [
        '%site' => $site_name,
      ]);
      $form['name']['#value'] = $samemail->name;
      $form['name']['#description'] = t('Will change to %new.', [
        '%new' => $cookie['name'],
      ]);
    }
    $form['help'] = [
      '#weight' => -10,
      '#markup' => $help,
    ];
    return $form;
  }

  /**
   * Validation for bakery_uncrumble form.
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {

    /*
     * We are ignoring blocked status on purpose.
     * The user is being repaired, not logged in.
     */

    /** @var \Drupal\user\UserInterface|false $account */
    $account = user_load_by_name($form_state
      ->getValue('name'));
    if (!($account && $account
      ->id()) || $this->passwordHasher
      ->check($form_state
      ->getValue('pass'), $account
      ->getPassword())) {
      $this
        ->logger('bakery')
        ->notice('Login attempt failed for %user while running uncrumble.', [
        '%user' => $form_state
          ->getValue('name'),
      ]);

      /*
       * Can't pretend that it was the "username or password"
       * so let's be helpful instead.
       */
      $form_state
        ->setErrorByName('pass', t('Sorry, unrecognized password. If you have forgotten your %site password, please <a href="@contact">contact the site administrator.</a>', [
        '%site' => $this
          ->configFactory()
          ->get('system.site')
          ->get('name'),
        '@contact' => $this
          ->configFactory()
          ->get('bakery.settings')
          ->get('bakery_master') . 'contact',
      ]));
    }
    else {
      $form_state
        ->setValue('bakery_uncrumble_account', $account);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $account = $form_state
      ->getValue('bakery_uncrumble_account');
    $form_state
      ->unsetValue('bakery_uncrumble_account');
    $cookie = $this->kitchen
      ->taste(Kitchen::CHOCOLATE_CHIP);
    $this->database
      ->update('users_field_data')
      ->fields([
      'init' => $cookie['init'],
    ])
      ->condition('uid', $account
      ->id(), '=')
      ->execute();
    $logger = $this
      ->logger('bakery');
    $logger
      ->notice('uncrumble changed init field for uid %uid from %oldinit to %newinit', [
      '%oldinit' => $account
        ->get('init')->value,
      '%newinit' => $cookie['init'],
      '%uid' => $account
        ->id(),
    ]);
    $account
      ->setEmail($cookie['mail']);
    $account
      ->save();
    $logger
      ->notice('uncrumble updated name %name_old to %name_new, mail %mail_old to %mail_new on uid %uid.', [
      '%name_old' => $account
        ->getAccountName(),
      '%name_new' => $cookie['name'],
      '%mail_old' => $account
        ->getEmail(),
      '%mail_new' => $cookie['mail'],
      '%uid' => $account
        ->id(),
    ]);
    $this
      ->messenger()
      ->addMessage(t('Your account has been repaired.'));
    $form_state
      ->setRedirect('user.page');
  }

  /**
   * Only let people with actual problems mess with uncrumble.
   */
  public function uncrumbleAccess(Request $request = NULL) {

    // Work around https://www.drupal.org/project/drupal/issues/2786941
    $request = $request ?? \Drupal::request();
    if ($this
      ->currentUser()
      ->isAnonymous() && $request
      ->getSession()
      ->get('BAKERY_CRUMBLED', FALSE)) {
      return AccessResult::allowed();
    }
    return AccessResult::forbidden();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
BakeryUncrumbleForm::$database private property
BakeryUncrumbleForm::$kitchen private property
BakeryUncrumbleForm::$passwordHasher private property For generating hashed password we check with database.
BakeryUncrumbleForm::buildForm public function Form to let users repair minor problems themselves. Overrides FormInterface::buildForm
BakeryUncrumbleForm::create public static function Instantiates a new instance of this class. Overrides FormBase::create
BakeryUncrumbleForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
BakeryUncrumbleForm::submitForm public function Form submission handler. Overrides FormInterface::submitForm
BakeryUncrumbleForm::uncrumbleAccess public function Only let people with actual problems mess with uncrumble.
BakeryUncrumbleForm::validateForm public function Validation for bakery_uncrumble form. Overrides FormBase::validateForm
BakeryUncrumbleForm::__construct public function Class constructor.
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
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
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.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.