Skip to content

Commit 8450f7f

Browse files
eliecharraJean85
authored andcommitted
Retrieve user IP from request context (#131)
This will fix ivalid client IP retrieval when app is behind a reverse proxy as we retrieve IP through framework logic.
1 parent 566fa53 commit 8450f7f

File tree

4 files changed

+77
-3
lines changed

4 files changed

+77
-3
lines changed

src/EventListener/ExceptionListener.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Symfony\Component\Console\Event\ConsoleEvent;
1010
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
1111
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
12+
use Symfony\Component\HttpFoundation\Request;
13+
use Symfony\Component\HttpFoundation\RequestStack;
1214
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1315
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
1416
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -35,6 +37,9 @@ class ExceptionListener implements SentryExceptionListenerInterface
3537
/** @var EventDispatcherInterface */
3638
protected $eventDispatcher;
3739

40+
/** @var RequestStack */
41+
private $requestStack;
42+
3843
/** @var string[] */
3944
protected $skipCapture;
4045

@@ -49,12 +54,14 @@ class ExceptionListener implements SentryExceptionListenerInterface
4954
public function __construct(
5055
\Raven_Client $client,
5156
EventDispatcherInterface $dispatcher,
57+
RequestStack $requestStack,
5258
array $skipCapture,
5359
TokenStorageInterface $tokenStorage = null,
5460
AuthorizationCheckerInterface $authorizationChecker = null
5561
) {
5662
$this->client = $client;
5763
$this->eventDispatcher = $dispatcher;
64+
$this->requestStack = $requestStack;
5865
$this->skipCapture = $skipCapture;
5966
$this->tokenStorage = $tokenStorage;
6067
$this->authorizationChecker = $authorizationChecker;
@@ -179,20 +186,27 @@ protected function shouldExceptionCaptureBeSkipped(\Throwable $exception): bool
179186
*/
180187
private function setUserValue($user)
181188
{
189+
$data = [];
190+
191+
$request = $this->requestStack->getCurrentRequest();
192+
if ($request instanceof Request) {
193+
$data['ip_address'] = $request->getClientIp();
194+
}
195+
182196
if ($user instanceof UserInterface) {
183-
$this->client->set_user_data($user->getUsername());
197+
$this->client->set_user_data($user->getUsername(), null, $data);
184198

185199
return;
186200
}
187201

188202
if (is_string($user)) {
189-
$this->client->set_user_data($user);
203+
$this->client->set_user_data($user, null, $data);
190204

191205
return;
192206
}
193207

194208
if (is_object($user) && method_exists($user, '__toString')) {
195-
$this->client->set_user_data((string)$user);
209+
$this->client->set_user_data((string)$user, null, $data);
196210
}
197211
}
198212
}

src/Resources/config/services.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ services:
1111
arguments:
1212
- '@sentry.client'
1313
- '@event_dispatcher'
14+
- '@request_stack'
1415
- '%sentry.skip_capture%'
1516
- '@?security.token_storage'
1617
- '@?security.authorization_checker'

test/DependencyInjection/SentryExtensionTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Symfony\Component\DependencyInjection\Container;
1313
use Symfony\Component\DependencyInjection\ContainerBuilder;
1414
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15+
use Symfony\Component\HttpFoundation\RequestStack;
1516
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
1617

1718
class SentryExtensionTest extends TestCase
@@ -376,6 +377,10 @@ private function getContainer(array $configuration = []): Container
376377
$mockEventDispatcher = $this
377378
->createMock(EventDispatcherInterface::class);
378379

380+
$mockRequestStack = $this
381+
->createMock(RequestStack::class);
382+
383+
$containerBuilder->set('request_stack', $mockRequestStack);
379384
$containerBuilder->set('event_dispatcher', $mockEventDispatcher);
380385
$containerBuilder->setAlias(self::LISTENER_TEST_PUBLIC_ALIAS, new Alias('sentry.exception_listener', true));
381386

test/EventListener/ExceptionListenerTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Component\DependencyInjection\Alias;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19+
use Symfony\Component\HttpFoundation\Request;
20+
use Symfony\Component\HttpFoundation\RequestStack;
1921
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
2022
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
2123
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -40,17 +42,21 @@ class ExceptionListenerTest extends TestCase
4042

4143
private $mockEventDispatcher;
4244

45+
private $mockRequestStack;
46+
4347
public function setUp()
4448
{
4549
$this->mockTokenStorage = $this->createMock(TokenStorageInterface::class);
4650
$this->mockAuthorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
4751
$this->mockSentryClient = $this->createMock(\Raven_Client::class);
4852
$this->mockEventDispatcher = $this->createMock(EventDispatcherInterface::class);
53+
$this->mockRequestStack = $this->createMock(RequestStack::class);
4954

5055
$containerBuilder = new ContainerBuilder();
5156
$containerBuilder->setParameter('kernel.root_dir', 'kernel/root');
5257
$containerBuilder->setParameter('kernel.environment', 'test');
5358

59+
$containerBuilder->set('request_stack', $this->mockRequestStack);
5460
$containerBuilder->set('security.token_storage', $this->mockTokenStorage);
5561
$containerBuilder->set('security.authorization_checker', $this->mockAuthorizationChecker);
5662
$containerBuilder->set('sentry.client', $this->mockSentryClient);
@@ -384,6 +390,54 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
384390
$listener->onKernelRequest($mockEvent);
385391
}
386392

393+
public function test_that_ip_is_set_from_request_stack()
394+
{
395+
$mockToken = $this->createMock(TokenInterface::class);
396+
397+
$mockToken
398+
->method('getUser')
399+
->willReturn('some_user');
400+
401+
$mockToken
402+
->method('isAuthenticated')
403+
->willReturn(true);
404+
405+
$mockEvent = $this->createMock(GetResponseEvent::class);
406+
407+
$mockRequest = $this->createMock(Request::class);
408+
409+
$mockRequest
410+
->method('getClientIp')
411+
->willReturn('1.2.3.4');
412+
413+
$this->mockRequestStack
414+
->method('getCurrentRequest')
415+
->willReturn($mockRequest);
416+
417+
$mockEvent
418+
->expects($this->once())
419+
->method('getRequestType')
420+
->willReturn(HttpKernelInterface::MASTER_REQUEST);
421+
422+
$this->mockAuthorizationChecker
423+
->method('isGranted')
424+
->with($this->identicalTo(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED))
425+
->willReturn(true);
426+
427+
$this->mockTokenStorage
428+
->method('getToken')
429+
->willReturn($mockToken);
430+
431+
$this->mockSentryClient
432+
->expects($this->once())
433+
->method('set_user_data')
434+
->with($this->identicalTo('some_user'), null, ['ip_address' => '1.2.3.4']);
435+
436+
$this->containerBuilder->compile();
437+
$listener = $this->getListener();
438+
$listener->onKernelRequest($mockEvent);
439+
}
440+
387441
public function test_regression_with_unauthenticated_user_token_PR_78()
388442
{
389443
$mockToken = $this->createMock(TokenInterface::class);

0 commit comments

Comments
 (0)