Skip to content

Commit f5ac7cb

Browse files
authored
Merge pull request #7 from BespredeL/develop
1. Changed middleware alias from geo.restrict to geo-restrict 2. Refactoring code providers and services
2 parents 75f07da + 4f12586 commit f5ac7cb

File tree

8 files changed

+152
-53
lines changed

8 files changed

+152
-53
lines changed

src/GeoRestrictServiceProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ public function boot(): void
2626

2727
$this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'geo-restrict');
2828

29-
app('router')->aliasMiddleware('geo.restrict', RestrictAccessByGeo::class);
29+
app('router')->aliasMiddleware('geo-restrict', RestrictAccessByGeo::class);
3030
}
3131
}

src/Providers/AbstractGeoProvider.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected function buildUrl(array $params = []): string
7575
{
7676
$url = ($this->baseUrl ?? '') . ($this->endpoint ?? '');
7777

78-
// We replace placeholders with actual values
78+
// Replace placeholders with actual values
7979
$url = preg_replace_callback('/:(\w+)/', function ($matches) use ($params) {
8080
$key = $matches[1];
8181
if (!isset($params[$key]) || $params[$key] === '' || $params[$key] === null) {
@@ -130,10 +130,35 @@ protected function mapByMap(array $data, array $map): array
130130
*/
131131
public function getGeoData(string $ip): ?array
132132
{
133+
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
134+
throw new \InvalidArgumentException("Invalid IP address provided.");
135+
}
136+
133137
$params = $this->buildRequestParams($ip);
134138
$url = $this->buildUrl($params);
135-
$response = Http::timeout(5)->get($url);
139+
140+
$parsed = parse_url($url);
141+
$host = $parsed['host'] ?? null;
142+
if ($host && filter_var($host, FILTER_VALIDATE_IP)) {
143+
if (!filter_var($host, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
144+
throw new GeoProviderException("Disallowed host for geo provider: {$host}");
145+
}
146+
}
147+
148+
try {
149+
$response = Http::timeout(5)->get($url);
150+
}
151+
catch (ConnectionException $e) {
152+
Log::error("GeoProvider: connection error in " . static::class . " - " . $e->getMessage());
153+
throw $e;
154+
}
155+
catch (\Throwable $e) {
156+
Log::error("GeoProvider: unexpected error in " . static::class . " - " . $e->getMessage());
157+
throw new GeoProviderException("Unexpected error occurred while requesting geo data.");
158+
}
159+
136160
$data = $response->json();
161+
137162
if (!$response->successful() || !$this->isValidResponse($data)) {
138163
throw new GeoProviderException($this->getErrorMessage($data));
139164
}
@@ -151,6 +176,7 @@ public function getGeoData(string $ip): ?array
151176
protected function buildRequestParams(string $ip): array
152177
{
153178
$params = ['ip' => $ip];
179+
154180
foreach ($this->optionalParams as $key) {
155181
if (isset($this->options[$key])) {
156182
$params[$key] = $this->options[$key];
@@ -183,4 +209,4 @@ abstract protected function isValidResponse(array $data): bool;
183209
* @return string
184210
*/
185211
abstract protected function getErrorMessage(array $data): string;
186-
}
212+
}

src/Providers/Ip2LocationIoProvider.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
class Ip2LocationIoProvider extends AbstractGeoProvider
66
{
7-
protected ?string $baseUrl = 'https://api.ip2location.io/';
8-
protected ?string $endpoint = '?key=:api_key&ip=:ip';
9-
protected array $requiredParams = ['api_key', 'ip'];
10-
protected array $optionalParams = ['lang'];
11-
protected array $responseMap = [
7+
/**
8+
* @var string|null
9+
*/
10+
protected ?string $baseUrl = 'https://api.ip2location.io/';
11+
12+
/**
13+
* @var string|null
14+
*/
15+
protected ?string $endpoint = '?key=:api_key&ip=:ip';
16+
17+
/**
18+
* @var array|string[]
19+
*/
20+
protected array $requiredParams = ['api_key', 'ip'];
21+
22+
/**
23+
* @var array|string[]
24+
*/
25+
protected array $optionalParams = ['lang'];
26+
27+
/**
28+
* @var array|string[]
29+
*/
30+
protected array $responseMap = [
1231
'country' => 'country_code',
1332
'region' => 'region_name',
1433
'city' => 'city_name',

src/Providers/IpApiCoProvider.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
class IpApiCoProvider extends AbstractGeoProvider
66
{
7-
protected ?string $baseUrl = 'https://ipapi.co/';
8-
protected ?string $endpoint = ':ip/json/';
9-
protected array $requiredParams = ['ip'];
10-
protected array $optionalParams = ['lang'];
11-
protected array $responseMap = [
7+
/**
8+
* @var string|null
9+
*/
10+
protected ?string $baseUrl = 'https://ipapi.co/';
11+
12+
/**
13+
* @var string|null
14+
*/
15+
protected ?string $endpoint = ':ip/json/';
16+
17+
/**
18+
* @var array|string[]
19+
*/
20+
protected array $requiredParams = ['ip'];
21+
22+
/**
23+
* @var array|string[]
24+
*/
25+
protected array $optionalParams = ['lang'];
26+
27+
/**
28+
* @var array|string[]
29+
*/
30+
protected array $responseMap = [
1231
'country' => 'country_code',
1332
'region' => 'region_code',
1433
'city' => 'city',

src/Providers/IpApiComProvider.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
class IpApiComProvider extends AbstractGeoProvider
66
{
7-
protected ?string $baseUrl = 'http://ip-api.com/';
8-
protected ?string $endpoint = 'json/:ip';
9-
protected array $requiredParams = ['ip'];
10-
protected array $optionalParams = ['lang'];
11-
protected array $responseMap = [
7+
/**
8+
* @var string|null
9+
*/
10+
protected ?string $baseUrl = 'http://ip-api.com/';
11+
12+
/**
13+
* @var string|null
14+
*/
15+
protected ?string $endpoint = 'json/:ip';
16+
17+
/**
18+
* @var array|string[]
19+
*/
20+
protected array $requiredParams = ['ip'];
21+
22+
/**
23+
* @var array|string[]
24+
*/
25+
protected array $optionalParams = ['lang'];
26+
27+
/**
28+
* @var array|string[]
29+
*/
30+
protected array $responseMap = [
1231
'country' => 'countryCode',
1332
'region' => 'region',
1433
'city' => 'city',

src/Providers/IpWhoIsProvider.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,30 @@
44

55
class IpWhoIsProvider extends AbstractGeoProvider
66
{
7-
protected ?string $baseUrl = 'https://ipwho.is/';
8-
protected ?string $endpoint = ':ip';
9-
protected array $requiredParams = ['ip'];
10-
protected array $optionalParams = ['api_key'];
11-
protected array $responseMap = [
7+
/**
8+
* @var string|null
9+
*/
10+
protected ?string $baseUrl = 'https://ipwho.is/';
11+
12+
/**
13+
* @var string|null
14+
*/
15+
protected ?string $endpoint = ':ip';
16+
17+
/**
18+
* @var array|string[]
19+
*/
20+
protected array $requiredParams = ['ip'];
21+
22+
/**
23+
* @var array|string[]
24+
*/
25+
protected array $optionalParams = ['api_key'];
26+
27+
/**
28+
* @var array|string[]
29+
*/
30+
protected array $responseMap = [
1231
'country' => 'country_code',
1332
'region' => 'region',
1433
'city' => 'city',

src/Services/GeoAccess.php

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,19 @@ public function isLocalIp(string $ip): bool
3838
protected function ipInCidr(string $ip, string $cidr): bool
3939
{
4040
[$subnet, $mask] = explode('/', $cidr);
41-
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
41+
42+
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
43+
&& filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
44+
) {
4245
$ipLong = ip2long($ip);
4346
$subnetLong = ip2long($subnet);
4447
$maskLong = -1 << (32 - (int)$mask);
4548
return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
46-
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
49+
}
50+
51+
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
52+
&& filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)
53+
) {
4754
$ipBin = inet_pton($ip);
4855
$subnetBin = inet_pton($subnet);
4956
$maskBin = str_repeat("f", $mask / 4);
@@ -104,10 +111,8 @@ public function passesRules(array $geo): array|bool
104111
}
105112

106113
// Callback denial
107-
if (is_callable($rules['deny']['callback'] ?? null)) {
108-
if (call_user_func($rules['deny']['callback'], $geo) === true) {
109-
return ['reason' => 'callback', 'field' => 'callback'];
110-
}
114+
if (is_callable($rules['deny']['callback'] ?? null) && call_user_func($rules['deny']['callback'], $geo) === true) {
115+
return ['reason' => 'callback', 'field' => 'callback'];
111116
}
112117

113118
// Field-based denial
@@ -122,10 +127,8 @@ public function passesRules(array $geo): array|bool
122127
}
123128

124129
// Callback allow
125-
if (is_callable($rules['allow']['callback'] ?? null)) {
126-
if (call_user_func($rules['allow']['callback'], $geo) !== true) {
127-
return ['reason' => 'callback_allow', 'field' => 'callback'];
128-
}
130+
if (is_callable($rules['allow']['callback'] ?? null) && call_user_func($rules['allow']['callback'], $geo) !== true) {
131+
return ['reason' => 'callback_allow', 'field' => 'callback'];
129132
}
130133

131134
// Field-based allow
@@ -165,24 +168,13 @@ public function denyResponse(?string $reason = null, ?array $blockInfo = null):
165168
$messageKey = 'blocked';
166169
if ($blockInfo && isset($blockInfo['reason'])) {
167170
$reasonType = $blockInfo['reason'];
168-
switch ($reasonType) {
169-
case 'time':
170-
$messageKey = 'blocked_time';
171-
break;
172-
case 'region':
173-
$messageKey = 'blocked_region';
174-
break;
175-
case 'city':
176-
$messageKey = 'blocked_city';
177-
break;
178-
case 'asn':
179-
$messageKey = 'blocked_asn';
180-
break;
181-
case 'country':
182-
default:
183-
$messageKey = 'blocked';
184-
break;
185-
}
171+
$messageKey = match ($reasonType) {
172+
'time' => 'blocked_time',
173+
'region' => 'blocked_region',
174+
'city' => 'blocked_city',
175+
'asn' => 'blocked_asn',
176+
default => 'blocked',
177+
};
186178
}
187179

188180
$message = Lang::get('geo-restrict::messages.' . $messageKey);

src/Services/GeoResolver.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ public function resolve(string $ip): ?array
7474
*/
7575
private function resolveFromService(mixed $service, string $ip): ?array
7676
{
77+
$serviceName = match (true) {
78+
is_array($service) => $service['name'] ?? 'array_service',
79+
is_string($service) => $service,
80+
default => 'unknown',
81+
};
82+
7783
try {
7884
// Array with 'provider' key (with options)
7985
if (is_array($service) && isset($service['provider'])) {
@@ -96,7 +102,6 @@ private function resolveFromService(mixed $service, string $ip): ?array
96102
Log::error("GeoProvider error: " . $e->getMessage());
97103
}
98104
catch (\Throwable $e) {
99-
$serviceName = is_array($service) ? ($service['name'] ?? 'array_service') : (is_string($service) ? $service : 'unknown');
100105
Log::debug("GeoRestrict: API {$serviceName} failed for {$ip}: {$e->getMessage()}");
101106
}
102107

0 commit comments

Comments
 (0)