You are here

class SecureLoginManager in Secure Login 8

Defines the secure login service.

Hierarchy

Expanded class hierarchy of SecureLoginManager

1 string reference to 'SecureLoginManager'
securelogin.services.yml in ./securelogin.services.yml
securelogin.services.yml
1 service uses SecureLoginManager
securelogin.manager in ./securelogin.services.yml
Drupal\securelogin\SecureLoginManager

File

src/SecureLoginManager.php, line 18

Namespace

Drupal\securelogin
View source
class SecureLoginManager implements TrustedCallbackInterface {

  /**
   * Configured secure login settings.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The event dispatcher service.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The current request.
   *
   * @var \Symfony\Component\HttpFoundation\Request
   */
  protected $request;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs the secure login service.
   */
  public function __construct(ConfigFactoryInterface $config_factory, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack) {
    $this->config = $config_factory
      ->get('securelogin.settings');
    $this->eventDispatcher = $event_dispatcher;
    $this->requestStack = $request_stack;
    $this->request = $this->requestStack
      ->getCurrentRequest();
  }

  /**
   * Rewrites the form action to use the secure base URL.
   */
  public function secureForm(&$form) {

    // Rebuild this form on based on the actual URL.
    $form['#cache']['contexts'][] = 'securelogin';

    // Flag form as secure for theming purposes.
    $form['#https'] = TRUE;
    if ($this->request
      ->isSecure()) {
      return;
    }

    // Redirect to secure page, if enabled.
    if ($this->config
      ->get('secure_forms')) {

      // Disable caching, as this form must be rebuilt to set the redirect.
      $form['#cache']['max-age'] = 0;
      $this
        ->secureRedirect();
    }
    $form['#action'] = $this
      ->secureUrl($form['#action']);
  }

  /**
   * Redirects a request to the same path on the secure base URL.
   */
  public function secureRedirect() {

    // Do not redirect from HTTPS requests.
    if ($this->request
      ->isSecure()) {
      return;
    }
    $status = $this
      ->getRedirectStatus();

    // Build the redirect URL from the master request.
    $request = $this->requestStack
      ->getMasterRequest();

    // Request may be a 404 so handle as unrouted URI.
    $url = Url::fromUri("internal:{$request->getPathInfo()}");
    $url
      ->setOption('absolute', TRUE)
      ->setOption('external', FALSE)
      ->setOption('https', TRUE)
      ->setOption('query', $request->query
      ->all());

    // Create listener to set the redirect response.
    $listener = function ($event) use ($url, $status) {
      $response = new TrustedRedirectResponse($url
        ->toString(), $status);

      // Page cache has a fatal error if cached response has no Expires header.
      $response
        ->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 UTC'));

      // Add cache context for this redirect.
      $response
        ->addCacheableDependency(new SecureLoginCacheableDependency());
      $event
        ->setResponse($response);

      // Redirect URL has destination so consider this the final destination.
      $event
        ->getRequest()->query
        ->set('destination', '');
    };

    // Add listener to response event at high priority.
    $this->eventDispatcher
      ->addListener(KernelEvents::RESPONSE, $listener, 222);
  }

  /**
   * Rewrites a URL to use the secure base URL.
   */
  public function secureUrl($url) {
    global $base_path, $base_secure_url;

    // Set the form action to use secure base URL in place of base path.
    if (strpos($url, $base_path) === 0) {
      $base_url = $this->config
        ->get('base_url') ?: $base_secure_url;
      return substr_replace($url, $base_url, 0, strlen($base_path) - 1);
    }

    // Or if a different domain is being used, forcibly rewrite to HTTPS.
    return str_replace('http://', 'https://', $url);
  }

  /**
   * Lazy builder callback; renders a form action URL including destination.
   *
   * @return array
   *   A renderable array representing the form action.
   *
   * @see \Drupal\Core\Form\FormBuilder::renderPlaceholderFormAction()
   */
  public function renderPlaceholderFormAction() {
    $action = UserLoginBlock::renderPlaceholderFormAction();
    $action['#markup'] = $this
      ->secureUrl($action['#markup']);
    return $action;
  }

  /**
   * Determines proper redirect status based on request method.
   */
  public function getRedirectStatus() {

    // If necessary, use a 308 redirect to avoid losing POST data.
    return $this->request
      ->isMethodCacheable() ? RedirectResponse::HTTP_MOVED_PERMANENTLY : RedirectResponse::HTTP_PERMANENTLY_REDIRECT;
  }

  /**
   * Returns list of trusted callbacks.
   */
  public static function trustedCallbacks() {
    return [
      'renderPlaceholderFormAction',
      'userLoginBlockPreRender',
    ];
  }

  /**
   * Pre-render callback to re-secure the user login block.
   */
  public function userLoginBlockPreRender($build) {
    if (!empty($build['content']['user_login_form']['#https'])) {
      $this
        ->secureForm($build['content']['user_login_form']);

      // Handle Drupal 8.4 style action placeholder.
      $placeholder = 'form_action_p_4r8ITd22yaUvXM6SzwrSe9rnQWe48hz9k1Sxto3pBvE';
      if (isset($build['content']['user_login_form']['#attached']['placeholders'][$placeholder])) {
        $build['content']['user_login_form']['#attached']['placeholders'][$placeholder] = [
          '#lazy_builder' => [
            'securelogin.manager:renderPlaceholderFormAction',
            [],
          ],
        ];
      }
    }
    return $build;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SecureLoginManager::$config protected property Configured secure login settings.
SecureLoginManager::$eventDispatcher protected property The event dispatcher service.
SecureLoginManager::$request protected property The current request.
SecureLoginManager::$requestStack protected property The request stack.
SecureLoginManager::getRedirectStatus public function Determines proper redirect status based on request method.
SecureLoginManager::renderPlaceholderFormAction public function Lazy builder callback; renders a form action URL including destination.
SecureLoginManager::secureForm public function Rewrites the form action to use the secure base URL.
SecureLoginManager::secureRedirect public function Redirects a request to the same path on the secure base URL.
SecureLoginManager::secureUrl public function Rewrites a URL to use the secure base URL.
SecureLoginManager::trustedCallbacks public static function Returns list of trusted callbacks. Overrides TrustedCallbackInterface::trustedCallbacks
SecureLoginManager::userLoginBlockPreRender public function Pre-render callback to re-secure the user login block.
SecureLoginManager::__construct public function Constructs the secure login service.
TrustedCallbackInterface::THROW_EXCEPTION constant Untrusted callbacks throw exceptions.
TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION constant Untrusted callbacks trigger silenced E_USER_DEPRECATION errors.
TrustedCallbackInterface::TRIGGER_WARNING constant Untrusted callbacks trigger E_USER_WARNING errors.