You are here

ShieldMiddleware.php in Shield 8

Namespace

Drupal\shield

File

src/ShieldMiddleware.php
View source
<?php

namespace Drupal\shield;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Path\PathMatcherInterface;
use Symfony\Component\HttpFoundation\IpUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

/**
 * Middleware for the shield module.
 */
class ShieldMiddleware implements HttpKernelInterface {

  /**
   * The decorated kernel.
   *
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  protected $httpKernel;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The EntityTypeManager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The path matcher.
   *
   * @var \Drupal\Core\Path\PathMatcherInterface
   */
  protected $pathMatcher;

  /**
   * Constructs a BanMiddleware object.
   *
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
   *   The decorated kernel.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The EntityTypeManager service.
   * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
   *   The path matcher service.
   */
  public function __construct(HttpKernelInterface $http_kernel, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, PathMatcherInterface $path_matcher) {
    $this->httpKernel = $http_kernel;
    $this->configFactory = $config_factory;
    $this->entityTypeManager = $entity_type_manager;
    $this->pathMatcher = $path_matcher;
  }

  /**
   * {@inheritdoc}
   */
  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    $config = $this->configFactory
      ->get('shield.settings');
    $allow_cli = $config
      ->get('allow_cli');
    $user = NULL;
    switch ($config
      ->get('credential_provider')) {
      case 'shield':
        $user = $config
          ->get('credentials.shield.user');
        $pass = $config
          ->get('credentials.shield.pass');
        break;
      case 'key':
        $user = $config
          ->get('credentials.key.user');

        /** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
        $storage = $this->entityTypeManager
          ->getStorage('key');

        /** @var \Drupal\key\KeyInterface $pass_key */
        $pass_key = $storage
          ->load($config
          ->get('credentials.key.pass_key'));
        if ($pass_key) {
          $pass = $pass_key
            ->getKeyValue();
        }
        break;
      case 'multikey':

        /** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
        $storage = $this->entityTypeManager
          ->getStorage('key');

        /** @var \Drupal\key\KeyInterface $user_pass_key */
        $user_pass_key = $storage
          ->load($config
          ->get('credentials.multikey.user_pass_key'));
        if ($user_pass_key) {
          $values = $user_pass_key
            ->getKeyValues();
          $user = $values['username'];
          $pass = $values['password'];
        }
        break;
    }

    // Check if enabled.
    $shield_enabled = $config
      ->get('shield_enable') && !empty($user);
    if (!$shield_enabled || $type != self::MASTER_REQUEST || PHP_SAPI === 'cli' && $allow_cli) {

      // Bypass:
      // 1. Empty username or Disabled from configuration.
      // 2. Subrequests.
      // 3. CLI requests if CLI is allowed.
      return $this->httpKernel
        ->handle($request, $type, $catch);
    }
    else {

      // Check if user IP is in whitelist.
      $in_whitelist = FALSE;
      if ($whitelist = $config
        ->get('whitelist')) {
        $whitelist = array_filter(array_map('trim', explode("\n", $whitelist)));
        $in_whitelist = IpUtils::checkIp($request
          ->getClientIp(), $whitelist);
      }

      // Check if site domain is in whitelist.
      $allow_domain = FALSE;
      if ($domains = $config
        ->get('domains')) {
        if (!empty($domains)) {
          $allow_domain = $this->pathMatcher
            ->matchPath($request
            ->getHost(), $domains);
        }
      }

      // Check if user has provided credentials.
      if ($request->server
        ->has('PHP_AUTH_USER') && $request->server
        ->has('PHP_AUTH_PW')) {
        $input_user = $request->server
          ->get('PHP_AUTH_USER');
        $input_pass = $request->server
          ->get('PHP_AUTH_PW');
      }
      elseif (!empty($request->server
        ->get('HTTP_AUTHORIZATION'))) {
        list($input_user, $input_pass) = explode(':', base64_decode(substr($request->server
          ->get('HTTP_AUTHORIZATION'), 6)), 2);
      }
      elseif (!empty($request->server
        ->get('REDIRECT_HTTP_AUTHORIZATION'))) {
        list($input_user, $input_pass) = explode(':', base64_decode(substr($request->server
          ->get('REDIRECT_HTTP_AUTHORIZATION'), 6)), 2);
      }
      $authenticated = isset($input_user) && $input_user === $user && hash_equals($pass, $input_pass);
      if ($in_whitelist || $authenticated || $allow_domain) {
        return $this->httpKernel
          ->handle($request, $type, $catch);
      }
    }
    $response = new Response();
    $response->headers
      ->add([
      'WWW-Authenticate' => 'Basic realm="' . strtr($config
        ->get('print'), [
        '[user]' => $user,
        '[pass]' => $pass,
      ]) . '"',
    ]);
    $response
      ->setStatusCode(401);
    return $response;
  }

}

Classes

Namesort descending Description
ShieldMiddleware Middleware for the shield module.