diff --git a/.dockerignore b/.dockerignore index 4559d3e2..9a48c84c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,11 +15,9 @@ vendor docs indocker docker-* -php* -.php* +phpstan.neon +php*xml* infection.json **/test* build* -.git* -.scrutinizer.yml -.travis.yml +**/.* diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 3fa0e966..ed831706 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -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: diff --git a/CHANGELOG.md b/CHANGELOG.md index e5915375..8304e678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Dockerfile b/Dockerfile index 1eb2715b..64cd7ebe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/module/Core/src/EventDispatcher/LocateShortUrlVisit.php b/module/Core/src/EventDispatcher/LocateShortUrlVisit.php index a0f8d033..6abbe02b 100644 --- a/module/Core/src/EventDispatcher/LocateShortUrlVisit.php +++ b/module/Core/src/EventDispatcher/LocateShortUrlVisit.php @@ -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(); diff --git a/module/Core/src/EventDispatcher/ShortUrlVisited.php b/module/Core/src/EventDispatcher/ShortUrlVisited.php index 1f0b5b5c..c33f805a 100644 --- a/module/Core/src/EventDispatcher/ShortUrlVisited.php +++ b/module/Core/src/EventDispatcher/ShortUrlVisited.php @@ -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]; } } diff --git a/module/Core/src/Service/VisitsTracker.php b/module/Core/src/Service/VisitsTracker.php index 54abe319..f477681a 100644 --- a/module/Core/src/Service/VisitsTracker.php +++ b/module/Core/src/Service/VisitsTracker.php @@ -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())); } /** diff --git a/module/Core/test/EventDispatcher/LocateShortUrlVisitTest.php b/module/Core/test/EventDispatcher/LocateShortUrlVisitTest.php index 5f40bb7b..c8f6f388 100644 --- a/module/Core/test/EventDispatcher/LocateShortUrlVisitTest.php +++ b/module/Core/test/EventDispatcher/LocateShortUrlVisitTest.php @@ -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 {