Skip to content

Commit b784b79

Browse files
eliecharraJean85
authored andcommitted
Retrieve user IP from request context (#132)
This will fix ivalid client IP retrieval when app is behind a reverse proxy as we retrieve IP through framework logic. Backport of #131 to 1.x
1 parent d68d99e commit b784b79

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
@@ -7,6 +7,8 @@
77
use Symfony\Component\Console\Event\ConsoleCommandEvent;
88
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
99
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
10+
use Symfony\Component\HttpFoundation\Request;
11+
use Symfony\Component\HttpFoundation\RequestStack;
1012
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1113
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
1214
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -33,6 +35,9 @@ class ExceptionListener implements SentryExceptionListenerInterface
3335
/** @var EventDispatcherInterface */
3436
protected $eventDispatcher;
3537

38+
/** @var RequestStack */
39+
private $requestStack;
40+
3641
/** @var string[] */
3742
protected $skipCapture;
3843

@@ -47,12 +52,14 @@ class ExceptionListener implements SentryExceptionListenerInterface
4752
public function __construct(
4853
\Raven_Client $client,
4954
EventDispatcherInterface $dispatcher,
55+
RequestStack $requestStack,
5056
array $skipCapture,
5157
TokenStorageInterface $tokenStorage = null,
5258
AuthorizationCheckerInterface $authorizationChecker = null
5359
) {
5460
$this->client = $client;
5561
$this->eventDispatcher = $dispatcher;
62+
$this->requestStack = $requestStack;
5663
$this->skipCapture = $skipCapture;
5764
$this->tokenStorage = $tokenStorage;
5865
$this->authorizationChecker = $authorizationChecker;
@@ -158,20 +165,27 @@ protected function shouldExceptionCaptureBeSkipped(\Exception $exception)
158165
*/
159166
private function setUserValue($user)
160167
{
168+
$data = [];
169+
170+
$request = $this->requestStack->getCurrentRequest();
171+
if ($request instanceof Request) {
172+
$data['ip_address'] = $request->getClientIp();
173+
}
174+
161175
if ($user instanceof UserInterface) {
162-
$this->client->set_user_data($user->getUsername());
176+
$this->client->set_user_data($user->getUsername(), null, $data);
163177

164178
return;
165179
}
166180

167181
if (is_string($user)) {
168-
$this->client->set_user_data($user);
182+
$this->client->set_user_data($user, null, $data);
169183

170184
return;
171185
}
172186

173187
if (is_object($user) && method_exists($user, '__toString')) {
174-
$this->client->set_user_data((string)$user);
188+
$this->client->set_user_data((string)$user, null, $data);
175189
}
176190
}
177191
}

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
@@ -10,6 +10,7 @@
1010
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1111
use Symfony\Component\DependencyInjection\ContainerBuilder;
1212
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13+
use Symfony\Component\HttpFoundation\RequestStack;
1314
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
1415

1516
class SentryExtensionTest extends TestCase
@@ -373,6 +374,10 @@ private function getContainer(array $configuration = [])
373374
$mockEventDispatcher = $this
374375
->createMock(EventDispatcherInterface::class);
375376

377+
$mockRequestStack = $this
378+
->createMock(RequestStack::class);
379+
380+
$containerBuilder->set('request_stack', $mockRequestStack);
376381
$containerBuilder->set('event_dispatcher', $mockEventDispatcher);
377382

378383
$extension = new SentryExtension();

test/EventListener/ExceptionListenerTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
1212
use Symfony\Component\DependencyInjection\ContainerBuilder;
1313
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\RequestStack;
1416
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1517
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
1618
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -38,17 +40,21 @@ class ExceptionListenerTest extends TestCase
3840
/** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */
3941
private $mockEventDispatcher;
4042

43+
private $mockRequestStack;
44+
4145
public function setUp()
4246
{
4347
$this->mockTokenStorage = $this->createMock(TokenStorageInterface::class);
4448
$this->mockAuthorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
4549
$this->mockSentryClient = $this->createMock(\Raven_Client::class);
4650
$this->mockEventDispatcher = $this->createMock(EventDispatcherInterface::class);
51+
$this->mockRequestStack = $this->createMock(RequestStack::class);
4752

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

57+
$containerBuilder->set('request_stack', $this->mockRequestStack);
5258
$containerBuilder->set('security.token_storage', $this->mockTokenStorage);
5359
$containerBuilder->set('security.authorization_checker', $this->mockAuthorizationChecker);
5460
$containerBuilder->set('sentry.client', $this->mockSentryClient);
@@ -424,6 +430,54 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
424430
$listener->onKernelRequest($mockEvent);
425431
}
426432

433+
public function test_that_ip_is_set_from_request_stack()
434+
{
435+
$mockToken = $this->createMock(TokenInterface::class);
436+
437+
$mockToken
438+
->method('getUser')
439+
->willReturn('some_user');
440+
441+
$mockToken
442+
->method('isAuthenticated')
443+
->willReturn(true);
444+
445+
$mockEvent = $this->createMock(GetResponseEvent::class);
446+
447+
$mockRequest = $this->createMock(Request::class);
448+
449+
$mockRequest
450+
->method('getClientIp')
451+
->willReturn('1.2.3.4');
452+
453+
$this->mockRequestStack
454+
->method('getCurrentRequest')
455+
->willReturn($mockRequest);
456+
457+
$mockEvent
458+
->expects($this->once())
459+
->method('getRequestType')
460+
->willReturn(HttpKernelInterface::MASTER_REQUEST);
461+
462+
$this->mockAuthorizationChecker
463+
->method('isGranted')
464+
->with($this->identicalTo(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED))
465+
->willReturn(true);
466+
467+
$this->mockTokenStorage
468+
->method('getToken')
469+
->willReturn($mockToken);
470+
471+
$this->mockSentryClient
472+
->expects($this->once())
473+
->method('set_user_data')
474+
->with($this->identicalTo('some_user'), null, ['ip_address' => '1.2.3.4']);
475+
476+
$this->containerBuilder->compile();
477+
$listener = $this->containerBuilder->get('sentry.exception_listener');
478+
$listener->onKernelRequest($mockEvent);
479+
}
480+
427481
public function test_regression_with_unauthenticated_user_token_PR_78()
428482
{
429483
$mockToken = $this->createMock(TokenInterface::class);

0 commit comments

Comments
 (0)