You are here

LdapSsoBootSubscriber.php in LDAP Single Sign On 8.4

Same filename and directory in other branches
  1. 8 src/LdapSsoBootSubscriber.php

Namespace

Drupal\ldap_sso

File

src/LdapSsoBootSubscriber.php
View source
<?php

declare (strict_types=1);
namespace Drupal\ldap_sso;

use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Url;
use Drupal\Core\Session\AccountInterface;
use Drupal\ldap_servers\Logger\LdapDetailLog;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;

/**
 * Provides the automated single sign-on provider.
 */
class LdapSsoBootSubscriber implements EventSubscriberInterface {

  /**
   * LDAP SSO config.
   *
   * @var \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * Front page.
   *
   * @var array|mixed|null
   */
  protected $frontpage;

  /**
   * Current path.
   *
   * @var \Drupal\Core\Path\CurrentPathStack
   */
  protected $currentPath;

  /**
   * Detail log.
   *
   * @var \Drupal\ldap_servers\Logger\LdapDetailLog
   */
  protected $detailLog;

  /**
   * The current user account.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $account;

  /**
   * The redirect destination service.
   *
   * @var \Drupal\Core\Routing\RedirectDestinationInterface
   */
  protected $redirectDestination;

  /**
   * Request Stack.
   *
   * @var \Symfony\Component\HttpFoundation\Request|null
   */
  protected $currentRequest;

  /**
   * Default paths to exclude.
   *
   * @var string[]
   */
  protected const DEFAULT_EXCLUDE_PATHS = [
    '/admin/config/search/clean-urls/check',
    '/user/login/sso',
    '/user/login',
    '/user/logout',
    '/user',
  ];

  /**
   * Fetches debugging level and logging interface.
   *
   * @param \Drupal\Core\Config\ConfigFactory $configFactory
   *   Factory for configuration for LDAP and logging level.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   Request stack.
   * @param \Drupal\Core\Path\CurrentPathStack $currentPath
   *   Adds the current path.
   * @param \Drupal\ldap_servers\Logger\LdapDetailLog $detailLog
   *   LDAP detail log.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user account.
   * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
   *   Redirect destination.
   */
  public function __construct(ConfigFactory $configFactory, RequestStack $request_stack, CurrentPathStack $currentPath, LdapDetailLog $detailLog, AccountInterface $account, RedirectDestinationInterface $redirect_destination) {
    $this->config = $configFactory
      ->get('ldap_sso.settings');
    $this->frontpage = $configFactory
      ->get('system.site')
      ->get('frontpage');
    $this->currentRequest = $request_stack
      ->getCurrentRequest();
    $this->currentPath = $currentPath;
    $this->detailLog = $detailLog;
    $this->account = $account;
    $this->redirectDestination = $redirect_destination;
  }

  /**
   * Determine if we should attempt SSO.
   *
   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
   *   Event to act upon.
   */
  public function checkSsoLoad(GetResponseEvent $event) : void {
    if (PHP_SAPI === 'cli' || $this->account
      ->isAuthenticated()) {
      $this->detailLog
        ->log('CLI or logged in user, no SSO.', [], 'ldap_sso');
      return;
    }
    if (!$this->config
      ->get('seamlessLogin')) {
      $this->detailLog
        ->log('Automated SSO not active.', [], 'ldap_sso');
      return;
    }
    if ($this
      ->checkExcludePath()) {
      $this->detailLog
        ->log('Excluded path', [], 'ldap_sso');
      return;
    }
    if ($this->currentRequest->cookies
      ->get('sso_login_running', FALSE)) {
      $this->detailLog
        ->log('SSO login running cookie present, aborting.', [], 'ldap_sso');
      exit(0);
    }
    if ($this->currentRequest->cookies
      ->get('sso_stop', FALSE)) {
      $this->detailLog
        ->log('Anonymous user with cookie to not continue SSO login', [], 'ldap_sso');
      return;
    }
    $this->detailLog
      ->log('Transferring to login controller', [], 'ldap_sso');
    $this
      ->transferSsoLoginController();
    exit(0);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() : array {
    return [
      KernelEvents::REQUEST => [
        'checkSsoLoad',
        20,
      ],
    ];
  }

  /**
   * Continue booting assuming we are doing SSO.
   */
  protected function transferSsoLoginController() : void {

    // This is set to destination() since the request uri is usually
    // system/40x already.
    $original_path = $this->redirectDestination
      ->get();
    $pathWithDestination = Url::fromRoute('ldap_sso.login_controller')
      ->toString() . '?destination=' . $original_path;
    if (method_exists(Cookie::class, 'create')) {
      $cookie = Cookie::create('sso_login_running', 'true', 0, base_path());
    }
    else {

      // Compatibility support for Drupal 8.9.
      $cookie = new Cookie('sso_login_running', 'true', 0, base_path());
    }
    $response = new RedirectResponseWithCookie($pathWithDestination, 302, [
      $cookie,
    ]);
    $response
      ->send();
  }

  /**
   * Check to exclude paths from SSO.
   *
   * @return bool
   *   Path excluded or not.
   */
  protected function checkExcludePath() : bool {
    if ($_SERVER['PHP_SELF'] === $this->currentRequest
      ->getBasePath() . '/index.php') {

      // Remove base_path from current path to match subdirectories, too.
      $path = str_replace($this->currentRequest
        ->getBasePath(), '', $this->currentPath
        ->getPath());
    }
    else {

      // cron.php, etc.
      $path = ltrim($_SERVER['PHP_SELF'], '/');
    }
    if (\in_array($path, self::DEFAULT_EXCLUDE_PATHS, TRUE)) {
      return TRUE;
    }
    if (\is_array($this->config
      ->get('ssoExcludedHosts'))) {
      $host = $_SERVER['SERVER_NAME'];
      foreach ($this->config
        ->get('ssoExcludedHosts') as $host_to_check) {
        if ($host_to_check === $host) {
          return TRUE;
        }
      }
    }
    foreach ($this->config
      ->get('ssoExcludedPaths') as $path_to_exclude) {
      if (mb_strtolower($path) === mb_strtolower($path_to_exclude) || $path_to_exclude === '<front>' && mb_strtolower($this->frontpage) === mb_strtolower($path)) {
        return TRUE;
      }
    }
    return FALSE;
  }

}

Classes

Namesort descending Description
LdapSsoBootSubscriber Provides the automated single sign-on provider.