Merge pull request #687 from acelaya-forks/feature/real-ip-geolocation

Feature/real ip geolocation
This commit is contained in:
Alejandro Celaya 2020-03-22 11:55:02 +01:00 committed by GitHub
commit 59c0d36c0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 39 additions and 19 deletions

View file

@ -15,11 +15,9 @@ vendor
docs
indocker
docker-*
php*
.php*
phpstan.neon
php*xml*
infection.json
**/test*
build*
.git*
.scrutinizer.yml
.travis.yml
**/.*

View file

@ -6,6 +6,9 @@ checks:
code_rating: true
duplication: true
build:
dependencies:
override:
- composer install --no-interaction --no-scripts --ignore-platform-reqs
nodes:
analysis:
tests:

View file

@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
* [#626](https://github.com/shlinkio/shlink/issues/626) Added support for Microsoft SQL Server.
* [#556](https://github.com/shlinkio/shlink/issues/556) Short code lengths can now be customized, both globally and on a per-short URL basis.
* [#541](https://github.com/shlinkio/shlink/issues/541) Added a request ID that is returned on `X-Request-Id` header, can be provided from outside and is set in log entries.
* [#642](https://github.com/shlinkio/shlink/issues/642) IP geolocation is now performed over the non-anonymized IP address when using swoole.
#### Changed

View file

@ -40,9 +40,10 @@ RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8
FROM base as builder
COPY . .
COPY --from=composer:1.10.1 /usr/bin/composer ./composer.phar
RUN php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \
RUN apk add --no-cache git && \
php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \
php composer.phar clear-cache && \
rm composer.* && \
rm -r docker composer.* && \
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php

View file

@ -53,7 +53,7 @@ class LocateShortUrlVisit
}
if ($this->downloadOrUpdateGeoLiteDb($visitId)) {
$this->locateVisit($visitId, $visit);
$this->locateVisit($visitId, $shortUrlVisited->originalIpAddress(), $visit);
}
$this->eventDispatcher->dispatch(new VisitLocated($visitId));
@ -80,12 +80,13 @@ class LocateShortUrlVisit
return true;
}
private function locateVisit(string $visitId, Visit $visit): void
private function locateVisit(string $visitId, ?string $originalIpAddress, Visit $visit): void
{
$isLocatable = $originalIpAddress !== null || $visit->isLocatable();
$addr = $originalIpAddress ?? $visit->getRemoteAddr();
try {
$location = $visit->isLocatable()
? $this->ipLocationResolver->resolveIpLocation($visit->getRemoteAddr())
: Location::emptyInstance();
$location = $isLocatable ? $this->ipLocationResolver->resolveIpLocation($addr) : Location::emptyInstance();
$visit->locate(new VisitLocation($location));
$this->em->flush();

View file

@ -9,10 +9,12 @@ use JsonSerializable;
final class ShortUrlVisited implements JsonSerializable
{
private string $visitId;
private ?string $originalIpAddress;
public function __construct(string $visitId)
public function __construct(string $visitId, ?string $originalIpAddress = null)
{
$this->visitId = $visitId;
$this->originalIpAddress = $originalIpAddress;
}
public function visitId(): string
@ -20,8 +22,13 @@ final class ShortUrlVisited implements JsonSerializable
return $this->visitId;
}
public function originalIpAddress(): ?string
{
return $this->originalIpAddress;
}
public function jsonSerialize(): array
{
return ['visitId' => $this->visitId];
return ['visitId' => $this->visitId, 'originalIpAddress' => $this->originalIpAddress];
}
}

View file

@ -39,7 +39,7 @@ class VisitsTracker implements VisitsTrackerInterface
$this->em->persist($visit);
$this->em->flush();
$this->eventDispatcher->dispatch(new ShortUrlVisited($visit->getId()));
$this->eventDispatcher->dispatch(new ShortUrlVisited($visit->getId(), $visitor->getRemoteAddress()));
}
/**

View file

@ -130,13 +130,16 @@ class LocateShortUrlVisitTest extends TestCase
yield 'localhost' => [new Visit($shortUrl, new Visitor('', '', IpAddress::LOCALHOST))];
}
/** @test */
public function locatableVisitsResolveToLocation(): void
/**
* @test
* @dataProvider provideIpAddresses
*/
public function locatableVisitsResolveToLocation(string $anonymizedIpAddress, ?string $originalIpAddress): void
{
$ipAddr = '1.2.3.0';
$ipAddr = $originalIpAddress ?? $anonymizedIpAddress;
$visit = new Visit(new ShortUrl(''), new Visitor('', '', $ipAddr));
$location = new Location('', '', '', '', 0.0, 0.0, '');
$event = new ShortUrlVisited('123');
$event = new ShortUrlVisited('123', $originalIpAddress);
$findVisit = $this->em->find(Visit::class, '123')->willReturn($visit);
$flush = $this->em->flush()->will(function (): void {
@ -155,6 +158,12 @@ class LocateShortUrlVisitTest extends TestCase
$dispatch->shouldHaveBeenCalledOnce();
}
public function provideIpAddresses(): iterable
{
yield 'no original IP address' => ['1.2.3.0', null];
yield 'original IP address' => ['1.2.3.0', '1.2.3.4'];
}
/** @test */
public function errorWhenUpdatingGeoLiteWithExistingCopyLogsWarning(): void
{