GeoBlockingKernelRequestListener   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 212
Duplicated Lines 5.66 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 8
dl 12
loc 212
ccs 100
cts 100
cp 1
rs 9
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
D onKernelRequest() 12 106 21
A blockAccess() 0 16 2
B isAllowedByIpWhiteListConfig() 0 12 5
B isAllowedBecauseIpIsSearchEngingeCrawler() 0 35 6

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace Azine\GeoBlockingBundle\EventListener;
3
4
use Symfony\Component\DependencyInjection\Container;
5
6
use Psr\Log\LoggerInterface;
7
8
use Symfony\Component\HttpFoundation\Response;
9
use Symfony\Component\Security\Core\User\UserInterface;
10
11
use Symfony\Component\HttpKernel\HttpKernelInterface;
12
13
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
14
15
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
16
17
use Azine\GeoBlockingBundle\Adapter\GeoIpLookupAdapterInterface;
18
19
class GeoBlockingKernelRequestListener
20
{
21
    private $configParams;
22
    private $lookUpAdapter;
23
    private $templating;
24
    private $logger;
25
    private $container;
26
27
    /**
28
     * @param EngineInterface             $templating
29
     * @param GeoIpLookupAdapterInterface $lookupAdapter
30
     * @param LoggerInterface             $logger
31
     * @param Container                   $container
32
     * @param array                       $parameters
33
     */
34 24
    public function __construct(EngineInterface $templating, GeoIpLookupAdapterInterface $lookupAdapter, LoggerInterface $logger, Container $container,  array $parameters)
35
    {
36 24
        $this->configParams = $parameters;
37 24
        $this->lookUpAdapter 	= $lookupAdapter;
38 24
        $this->templating = $templating;
39 24
        $this->logger = $logger;
40 24
        $this->container = $container;
41 24
    }
42
43
    /**
44
     * @param GetResponseEvent $event
45
     */
46 24
    public function onKernelRequest(GetResponseEvent $event)
47
    {
48
        // ignore sub-requests
49 24
        if ($event->getRequestType() == HttpKernelInterface::SUB_REQUEST) {
50 1
            return;
51
        }
52
53
        // check if the blocking is enabled at all
54 23
        if (!$this->configParams['enabled']) {
55 1
            $this->logger->info("azine_geoblocking_bundle: blocking not enabled");
56
57 1
            return;
58
        }
59
60 22
        $request = $event->getRequest();
61
        // check if blocking authenticated users is enabled
62 22
        $authenticated = $this->container->get('security.token_storage')->getToken()->getUser() instanceof UserInterface;
63 22
        if ($this->configParams['blockAnonOnly'] && $authenticated) {
64 1
            $this->logger->info("azine_geoblocking_bundle: allowed logged-in user");
65
66 1
            return;
67
        }
68
69
        // allow access it the "geoblocking_allow_cookie" is set to true
70 21
        $alloweByCookie = $this->configParams['allow_by_cookie'];
71
72 21
        if($alloweByCookie && $request->cookies->get($this->configParams['allow_by_cookie_name'], false)){
73 1
        	return;
74
        }
75
76 20
        $visitorAddress = $request->getClientIp();
77
78
        // check if the visitors IP is a private IP => the request comes from the same subnet as the server or the server it self.
79 20
        if ($this->configParams['allowPrivateIPs']) {
80 20
            $patternForPrivateIPs = "#(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)#";
81 20
            if (preg_match($patternForPrivateIPs, $visitorAddress) == 1) {
82 2
                $this->logger->info("azine_geoblocking_bundle: allowed private network");
83
84 2
                return;
85
            }
86 18
        }
87
88
        // check if the route is allowed from the current visitors country via whitelists
89 18
        $routeName = $request->get('_route');
90 18
        $allowedByRouteWhiteList = array_search($routeName, $this->configParams['routeWhitelist'], true) === false;
91 18
        if (!$allowedByRouteWhiteList) {
92 1
            $this->logger->info("azine_geoblocking_bundle: allowed by routeWhiteList");
93
94 1
            return;
95
        }
96
97 17
        $country = $this->lookUpAdapter->getCountry($visitorAddress);
98 17
        $allowedByCountryWhiteList = array_search($country, $this->configParams['countryWhitelist'], true) === false;
99 17
        if (!$allowedByCountryWhiteList) {
100 2
            $this->logger->info("azine_geoblocking_bundle: allowed by countryWhiteList");
101
102 2
            return;
103
        }
104
105
        // check if the vistitor is a whitelisted IP
106 15
        if ($this->isAllowedByIpWhiteListConfig($visitorAddress)) {
107 2
            $this->logger->info("azine_geoblocking_bundle: allowed by ipWhiteList");
108
109 2
            return;
110
        }
111
112
        // check if the visitor is allowed because it's a search-engine crawler of google or msn
113 13
        if ($this->isAllowedBecauseIpIsSearchEngingeCrawler($visitorAddress)) {
114 2
            $this->logger->info("azine_geoblocking_bundle: allowed by searchEngineConfig");
115
116 2
            return;
117
        }
118
119
120
        // until here everything that is allowed has been filtered out.
121 11
        $useRouteBL = array_key_exists('routeBlacklist', $this->configParams) && !empty($this->configParams['routeBlacklist']);
122 11
        $useCountryBL = array_key_exists('countryBlacklist', $this->configParams) && !empty($this->configParams['countryBlacklist']);
123
124
        // if neither of the blackLists should be used, deny all remaining requests
125 11
        if (!$useRouteBL && !$useCountryBL) {
126 7
            $this->logger->warning("azine_geoblocking_bundle: no blackLists defined and the request (Route: $routeName, Country: $country, IP: $visitorAddress) was not allowed by any of the whiteList/positive filters.");
127 7
            $this->blockAccess($event, $country);
128
129 7
            return;
130
        }
131
132
        // check if one of the blacklists denies access
133 4 View Code Duplication
        if ($useRouteBL) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134 2
            if (array_search($routeName, $this->configParams['routeBlacklist'], true) !== false) {
135 1
                $this->logger->warning("azine_geoblocking_bundle: blocked by routeBL.\n".print_r($this->configParams['routeBlacklist'], true));
136 1
                $this->blockAccess($event, $country);
137 1
            }
138 2
        }
139
140 4 View Code Duplication
        if ($useCountryBL) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141 3
            if (array_search($country, $this->configParams['countryBlacklist'], true) !== false) {
142 2
                $this->logger->warning("azine_geoblocking_bundle: blocked by countryBL\n".print_r($this->configParams['countryBlacklist'], true));
143 2
                $this->blockAccess($event, $country);
144 2
            }
145 3
        }
146
147
        // one or both blacklists were defined to be used, but the request was not filtered out => allow the request
148 4
        $this->logger->info("azine_geoblocking_bundle: allowed, no denial-rule triggered");
149
150 4
        return;
151
    }
152
153
    /**
154
     * @param GetResponseEvent $event
155
     * @param string           $country
156
     */
157 10
    private function blockAccess(GetResponseEvent $event, $country)
158
    {
159
        // render the "sorry you are not allowed here"-page
160 10
        $parameters = array('loginRoute' => $this->configParams['loginRoute'], 'country' => $country);
161 10
        $event->setResponse($this->templating->renderResponse($this->configParams['blockedPageView'], $parameters, new Response('', Response::HTTP_FORBIDDEN)));
162 10
        $event->stopPropagation();
163
164 10
        if ($this->configParams['logBlockedRequests']) {
165 1
            $request = $event->getRequest();
166 1
            $routeName = $request->get('_route');
167 1
            $ip = $request->getClientIp();
168 1
            $uagent = $_SERVER['HTTP_USER_AGENT'];
169 1
            $hostName = gethostbyaddr($ip);
170 1
            $this->logger->warning("azine_geoblocking_bundle: Route $routeName was blocked for a user from $country (IP: $ip , HostName $hostName, UAgent: '$uagent'");
171 1
        }
172 10
    }
173
174
    /**
175
     * @param  string  $ip
176
     * @return boolean
177
     */
178 15
    private function isAllowedByIpWhiteListConfig($ip)
179
    {
180 15
        if ($this->configParams['ip_whitelist']) {
181 3
            foreach ($this->configParams['ip_whitelist'] as $pattern) {
182 3
                if ($ip == $pattern || @preg_match($pattern, $ip) === 1) {
183 2
                    return true;
184
                }
185 1
            }
186 1
        }
187
188 13
        return false;
189
    }
190
191
    /**
192
     * @param  string  $ip
193
     * @return boolean
194
     */
195 13
    private function isAllowedBecauseIpIsSearchEngingeCrawler($ip)
196
    {
197 13
        if ($this->configParams['allow_search_bots']) {
198
199
            // resolve host name
200 3
            $hostName = gethostbyaddr($ip);
201
            // reverse resolve IP
202 3
            $reverseIP = gethostbyname($hostName);
203
204
205
            // chekc if the hostname matches any of the search-engine names.
206 3
            $searchEngineDomains = $this->configParams['search_bot_domains'];
207
208 3
            $isSearchEngineDomain = false;
209
210 3
            foreach ($searchEngineDomains as $domain) {
211
212
                // if the hostname ends with any of the search-engine-domain names
213 3
                if (substr($hostName, - strlen($domain)) === $domain) {
214
215
                    // set variable to true and stop the loop
216 2
                    $isSearchEngineDomain = true;
217 2
                    break;
218
                }
219 3
            }
220
221
            // if the IP and reverse resolved IP match and the ip belongs to a search-engine-domain
222 3
            if ($ip == $reverseIP && $isSearchEngineDomain) {
223
                // allow the ip
224 2
                return true;
225
            }
226 1
        }
227
228 11
        return false;
229
    }
230
}
231