You are here

class RestrictIpService in Restrict IP 8

Same name and namespace in other branches
  1. 8.2 src/Service/RestrictIpService.php \Drupal\restrict_ip\Service\RestrictIpService
  2. 3.x src/Service/RestrictIpService.php \Drupal\restrict_ip\Service\RestrictIpService

Hierarchy

Expanded class hierarchy of RestrictIpService

1 file declares its use of RestrictIpService
RestrictIpServiceTest.php in tests/src/Unit/Service/RestrictIpServiceTest.php
1 string reference to 'RestrictIpService'
restrict_ip.services.yml in ./restrict_ip.services.yml
restrict_ip.services.yml

File

src/Service/RestrictIpService.php, line 12

Namespace

Drupal\restrict_ip\Service
View source
class RestrictIpService implements RestrictIpServiceInterface {

  /**
   * Indicates whether or not the user should be blocked
   *
   * @var bool
   */
  private $blocked;

  /**
   * The current user
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The current path
   *
   * @var string
   */
  protected $currentPath;

  /**
   * The Restrict IP configuration settings
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The current user's IP address
   *
   * @var string
   */
  private $currentUserIp;

  /**
   * The Restrict IP data mapper
   *
   * @var \Drupal\restrict_ip\Mapper\RestrictIpMapperInterface
   */
  protected $mapper;

  /**
   * The Path Matcher service
   *
   * @var \Drupal\Core\Path\PathMatcherInterface
   */
  protected $pathMatcher;

  /**
   * Constructs a RestrictIpService object
   *
   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
   *   The current user
   * @param \Drupal\Core\Path\CurrentPathStack $currentPathStack
   *   The current path stack
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The Config Factory service
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
   *   The current HTTP request
   * @param \Drupal\restrict_ip\Mapper\RestrictIpMapperInterface $restrictIpMapper
   *   The Restrict IP data mapper object
   * @param \Drupal\Core\Path\PathMatcherInterface $pathMatcher
   *   The Path Matcher service
   */
  public function __construct(AccountProxyInterface $currentUser, CurrentPathStack $currentPathStack, ConfigFactoryInterface $configFactory, RequestStack $requestStack, RestrictIpMapperInterface $restrictIpMapper, PathMatcherInterface $pathMatcher) {
    $this->currentUser = $currentUser;
    $this->mapper = $restrictIpMapper;
    $this->pathMatcher = $pathMatcher;
    $this->currentPath = strtolower($currentPathStack
      ->getPath());
    $this->config = $configFactory
      ->get('restrict_ip.settings');
    $this->currentUserIp = $requestStack
      ->getCurrentRequest()
      ->getClientIp();
  }

  /**
   * {@inheritdoc}
   */
  public function userIsBlocked() {
    if ($this
      ->allowAccessByPermission()) {
      return FALSE;
    }
    return $this->blocked;
  }

  /**
   * {@inheritdoc}
   */
  public function testForBlock($runInCli = FALSE) {
    $this->blocked = FALSE;
    if ($this->config
      ->get('enable')) {
      $this->blocked = TRUE;

      // We don't want to check IP on CLI (likely drush) requests
      // unless explicitly declared to check by the $runInCli argument
      if (PHP_SAPI != 'cli' || $runInCli) {
        $access_denied = TRUE;
        if ($this
          ->allowAccessWhitelistedPath()) {
          $access_denied = FALSE;
        }
        elseif ($this
          ->allowAccessBlacklistedPath()) {
          $access_denied = FALSE;
        }
        elseif ($this
          ->allowAccessWhitelistedIp()) {
          $access_denied = FALSE;
        }

        // If the user has been denied access
        if ($access_denied) {
          if (PHP_SAPI != 'cli') {
            $_SESSION['restrict_ip'] = TRUE;
          }
        }
        else {
          $this->blocked = FALSE;
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function cleanIpAddressInput($input) {
    $ip_addresses = trim($input);
    $ip_addresses = preg_replace('/(\\/\\/|#).+/', '', $ip_addresses);
    $ip_addresses = preg_replace('~/\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/~', '', $ip_addresses);
    $addresses = explode(PHP_EOL, $ip_addresses);
    $return = [];
    foreach ($addresses as $ip_address) {
      $trimmed = trim($ip_address);
      if (strlen($trimmed)) {
        $return[] = $trimmed;
      }
    }
    return $return;
  }

  /**
   * {@inheritdoc}
   */
  public function getCurrentUserIp() {
    return $this->currentUserIp;
  }

  /**
   * {@inheritdoc}
   */
  public function getCurrentPath() {
    return $this->currentPath;
  }

  /**
   * {@inheritdoc}
   */
  public function getWhitelistedIpAddresses() {
    $ip_addresses = $this->mapper
      ->getWhitelistedIpAddresses();
    return is_array($ip_addresses) ? $ip_addresses : [];
  }

  /**
   * {@inheritdoc}
   */
  public function saveWhitelistedIpAddresses(array $ipAddresses, $overwriteExisting = TRUE) {
    $this->mapper
      ->saveWhitelistedIpAddresses($ipAddresses, $overwriteExisting);
  }

  /**
   * {@inheritdoc}
   */
  public function getWhitelistedPagePaths() {
    $whitelisted_paths = $this->mapper
      ->getWhitelistedPaths();
    return is_array($whitelisted_paths) ? $whitelisted_paths : [];
  }

  /**
   * {@inheritdoc}
   */
  public function saveWhitelistedPagePaths(array $whitelistedPaths, $overwriteExisting = TRUE) {
    $this->mapper
      ->saveWhitelistedPaths($whitelistedPaths, $overwriteExisting);
  }

  /**
   * {@inheritdoc}
   */
  public function getBlacklistedPagePaths() {
    $blacklisted_paths = $this->mapper
      ->getBlacklistedPaths();
    return is_array($blacklisted_paths) ? $blacklisted_paths : [];
  }

  /**
   * {@inheritdoc}
   */
  public function saveBlacklistedPagePaths(array $blacklistedPaths, $overwriteExisting = TRUE) {
    $this->mapper
      ->saveBlacklistedPaths($blacklistedPaths, $overwriteExisting);
  }

  /**
   * Test to see if access should be granted based on
   */
  private function allowAccessByPermission() {
    static $allow_access;
    if (is_null($allow_access)) {
      $allow_access = [];
    }
    if (!isset($allow_access[$this->currentPath])) {
      $allow_access[$this->currentPath] = FALSE;
      if ($this->config
        ->get('allow_role_bypass')) {
        $current_path = $this->currentPath;
        if ($this->currentUser
          ->hasPermission('bypass ip restriction') || in_array($current_path, array(
          '/user',
          '/user/login',
          '/user/password',
          '/user/logout',
          '/user/register',
        )) || strpos($current_path, '/user/reset/') === 0) {
          $allow_access[$this->currentPath] = TRUE;
        }
      }
    }
    return $allow_access[$this->currentPath];
  }

  /**
   * Test if the current path is allowed based on whitelist settings
   */
  private function allowAccessWhitelistedPath() {
    $allow_access = FALSE;
    if ($this->config
      ->get('white_black_list') == 1) {
      $whitelisted_pages = $this
        ->getWhitelistedPagePaths();
      $current_whitelist = FALSE;
      if (count($whitelisted_pages)) {
        foreach ($whitelisted_pages as $whitelisted_page) {
          if ($this->pathMatcher
            ->matchPath($this->currentPath, $whitelisted_page)) {
            $current_whitelist = TRUE;
          }
        }
      }
      if ($current_whitelist) {
        $allow_access = TRUE;
      }
    }
    return $allow_access;
  }

  /**
   * Test if the current path is allowed based on blacklist settings
   */
  private function allowAccessBlacklistedPath() {
    $allow_access = FALSE;
    if ($this->config
      ->get('white_black_list') == 2) {
      $blacklisted_pages = $this
        ->getBlacklistedPagePaths();
      $current_blacklist = FALSE;
      if (count($blacklisted_pages)) {
        foreach ($blacklisted_pages as $blacklisted_page) {
          if ($this->pathMatcher
            ->matchPath($this->currentPath, $blacklisted_page)) {
            $current_blacklist = TRUE;
          }
        }
      }
      if (!$current_blacklist) {
        $allow_access = TRUE;
      }
    }
    return $allow_access;
  }

  /**
   * Test to see if the current user has a whitelisted IP address
   */
  private function allowAccessWhitelistedIp() {
    $ip_whitelist = $this
      ->buildWhitelistedIpAddresses();
    if (count($ip_whitelist)) {
      foreach ($ip_whitelist as $whitelisted_address) {
        if ($this
          ->testWhitelistedIp($whitelisted_address)) {
          return TRUE;
        }
      }
    }
    return FALSE;
  }

  /**
   * Build an array of whitelisted IP addresses based on site settings
   */
  private function buildWhitelistedIpAddresses() {

    // Get the value saved to the system, and turn it into an array of IP addresses.
    $ip_addresses = $this
      ->getWhitelistedIpAddresses();

    // Add any whitelisted IPs from the settings.php file to the whitelisted array
    $ip_whitelist = $this->config
      ->get('ip_whitelist');
    if (count($ip_whitelist)) {
      $ip_addresses = array_merge($ip_addresses, $ip_whitelist);
    }
    return $ip_addresses;
  }

  /**
   * Test an ip address to see if the current user should be whitelisted based on
   * that address.
   *
   * @param $whitelisted_address
   *   The address to check
   *
   * @return bool
   *   TRUE if the user should be allowed access based on the current IP
   *   FALSE if they should not be allowed access based on the current IP
   */
  private function testWhitelistedIp($whitelisted_ip) {

    // Check if the given IP address matches the current user
    if ($whitelisted_ip == $this
      ->getCurrentUserIp()) {
      return TRUE;
    }
    $pieces = explode('-', $whitelisted_ip);

    // We only need to continue checking this IP address
    // if it is a range of addresses
    if (count($pieces) == 2) {
      $start_ip = $pieces[0];
      $end_ip = $pieces[1];
      $start_pieces = explode('.', $start_ip);

      // If there are not 4 sections to the IP then its an invalid
      // IPv4 address, and we don't need to continue checking
      if (count($start_pieces) === 4) {
        $user_pieces = explode('.', $this->currentUserIp);

        // We compare the first three chunks of the first IP address
        // With the first three chunks of the user's IP address
        // If they are not the same, then the IP address is not within
        // the range of IPs
        for ($i = 0; $i < 3; $i++) {
          if ((int) $user_pieces[$i] !== (int) $start_pieces[$i]) {

            // One of the chunks has failed, so we can stop
            // checking this range
            return FALSE;
          }
        }

        // The first three chunks have past testing, so now we check the
        // range given to see if the final chunk is in this range
        // First we get the start of the range
        $start_final_chunk = (int) array_pop($start_pieces);
        $end_pieces = explode('.', $end_ip);

        // Then we get the end of the range. This will work
        // whether the user has entered XXX.XXX.XXX.XXX - XXX.XXX.XXX.XXX
        // or XXX.XXX.XXX.XXX-XXX
        $end_final_chunk = (int) array_pop($end_pieces);

        // Now we get the user's final chunk
        $user_final_chunk = (int) array_pop($user_pieces);

        // And finally we check to see if the user's chunk lies in that range
        if ($user_final_chunk >= $start_final_chunk && $user_final_chunk <= $end_final_chunk) {

          // The user's IP lies in the range, so we don't grant access
          return TRUE;
        }
      }
    }
    return FALSE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
RestrictIpService::$blocked private property * Indicates whether or not the user should be blocked * *
RestrictIpService::$config protected property * The Restrict IP configuration settings * *
RestrictIpService::$currentPath protected property * The current path * *
RestrictIpService::$currentUser protected property * The current user * *
RestrictIpService::$currentUserIp private property * The current user's IP address * *
RestrictIpService::$mapper protected property * The Restrict IP data mapper * *
RestrictIpService::$pathMatcher protected property * The Path Matcher service * *
RestrictIpService::allowAccessBlacklistedPath private function * Test if the current path is allowed based on blacklist settings
RestrictIpService::allowAccessByPermission private function * Test to see if access should be granted based on
RestrictIpService::allowAccessWhitelistedIp private function * Test to see if the current user has a whitelisted IP address
RestrictIpService::allowAccessWhitelistedPath private function * Test if the current path is allowed based on whitelist settings
RestrictIpService::buildWhitelistedIpAddresses private function * Build an array of whitelisted IP addresses based on site settings
RestrictIpService::cleanIpAddressInput public function * Overrides RestrictIpServiceInterface::cleanIpAddressInput
RestrictIpService::getBlacklistedPagePaths public function * Overrides RestrictIpServiceInterface::getBlacklistedPagePaths
RestrictIpService::getCurrentPath public function * Overrides RestrictIpServiceInterface::getCurrentPath
RestrictIpService::getCurrentUserIp public function * Overrides RestrictIpServiceInterface::getCurrentUserIp
RestrictIpService::getWhitelistedIpAddresses public function * Overrides RestrictIpServiceInterface::getWhitelistedIpAddresses
RestrictIpService::getWhitelistedPagePaths public function * Overrides RestrictIpServiceInterface::getWhitelistedPagePaths
RestrictIpService::saveBlacklistedPagePaths public function * Overrides RestrictIpServiceInterface::saveBlacklistedPagePaths
RestrictIpService::saveWhitelistedIpAddresses public function * Overrides RestrictIpServiceInterface::saveWhitelistedIpAddresses
RestrictIpService::saveWhitelistedPagePaths public function * Overrides RestrictIpServiceInterface::saveWhitelistedPagePaths
RestrictIpService::testForBlock public function * Overrides RestrictIpServiceInterface::testForBlock
RestrictIpService::testWhitelistedIp private function * Test an ip address to see if the current user should be whitelisted based on * that address. * *
RestrictIpService::userIsBlocked public function * Overrides RestrictIpServiceInterface::userIsBlocked
RestrictIpService::__construct public function * Constructs a RestrictIpService object * *