diff --git a/src/Sentry/Laravel/EventHandler.php b/src/Sentry/Laravel/EventHandler.php index 3aeaa843..6d5f12ea 100644 --- a/src/Sentry/Laravel/EventHandler.php +++ b/src/Sentry/Laravel/EventHandler.php @@ -274,14 +274,24 @@ private function configureUserScopeFromModel($authUser): void // If the user is a Laravel Eloquent model we try to extract some common fields from it if ($authUser instanceof Model) { - $username = $authUser->getAttribute('username'); + $email = null; + + if ($this->modelHasAttribute($authUser, 'email')) { + $email = $authUser->getAttribute('email'); + } elseif ($this->modelHasAttribute($authUser, 'mail')) { + $email = $authUser->getAttribute('mail'); + } + + $username = $this->modelHasAttribute($authUser, 'username') + ? (string)$authUser->getAttribute('username') + : null; $userData = [ 'id' => $authUser instanceof Authenticatable ? $authUser->getAuthIdentifier() : $authUser->getKey(), - 'email' => $authUser->getAttribute('email') ?? $authUser->getAttribute('mail'), - 'username' => $username === null ? $username : (string)$username, + 'email' => $email, + 'username' => $username, ]; } @@ -305,6 +315,15 @@ private function configureUserScopeFromModel($authUser): void }); } + private function modelHasAttribute(Model $model, string $key): bool + { + // Taken from: https://github.com/laravel/framework/blob/v11.41.3/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L445 + // Laravel 11 introduced the `hasAttribute` method we are (almost) mirroring here since it's not available in earlier Laravel versions we support + return array_key_exists($key, $model->getAttributes()) || + $model->hasGetMutator($key) || + (method_exists($model, 'hasAttributeMutator') && $model->hasAttributeMutator($key)); + } + protected function octaneRequestReceivedHandler(Octane\RequestReceived $event): void { $this->prepareScopeForOctane(); diff --git a/test/Sentry/EventHandler/AuthEventsTest.php b/test/Sentry/EventHandler/AuthEventsTest.php index 6921f53f..d3e5dec0 100644 --- a/test/Sentry/EventHandler/AuthEventsTest.php +++ b/test/Sentry/EventHandler/AuthEventsTest.php @@ -15,11 +15,13 @@ class AuthEventsTest extends TestCase public function testAuthenticatedEventFillsUserOnScope(): void { - $user = new AuthEventsTestUserModel(); + $user = new AuthEventsTestUserModel; - $user->id = 123; - $user->username = 'username'; - $user->email = 'foo@example.com'; + $user->forceFill([ + 'id' => 123, + 'username' => 'username', + 'email' => 'foo@example.com', + ]); $scope = $this->getCurrentSentryScope(); @@ -29,17 +31,19 @@ public function testAuthenticatedEventFillsUserOnScope(): void $this->assertNotNull($scope->getUser()); - $this->assertEquals($scope->getUser()->getId(), 123); - $this->assertEquals($scope->getUser()->getUsername(), 'username'); - $this->assertEquals($scope->getUser()->getEmail(), 'foo@example.com'); + $this->assertEquals(123, $scope->getUser()->getId()); + $this->assertEquals('username', $scope->getUser()->getUsername()); + $this->assertEquals('foo@example.com', $scope->getUser()->getEmail()); } public function testAuthenticatedEventFillsUserOnScopeWhenUsernameIsNotAString(): void { $user = new AuthEventsTestUserModel(); - $user->id = 123; - $user->username = 456; + $user->forceFill([ + 'id' => 123, + 'username' => 456, + ]); $scope = $this->getCurrentSentryScope(); @@ -49,8 +53,8 @@ public function testAuthenticatedEventFillsUserOnScopeWhenUsernameIsNotAString() $this->assertNotNull($scope->getUser()); - $this->assertEquals($scope->getUser()->getId(), 123); - $this->assertEquals($scope->getUser()->getUsername(), '456'); + $this->assertEquals(123, $scope->getUser()->getId()); + $this->assertEquals('456', $scope->getUser()->getUsername()); } public function testAuthenticatedEventDoesNotFillUserOnScopeWhenPIIShouldNotBeSent(): void