1+ package com .digitalsanctuary .spring .user .listener ;
2+
3+ import static org .mockito .ArgumentMatchers .anyString ;
4+ import static org .mockito .ArgumentMatchers .eq ;
5+ import static org .mockito .Mockito .never ;
6+ import static org .mockito .Mockito .times ;
7+ import static org .mockito .Mockito .verify ;
8+ import static org .mockito .Mockito .when ;
9+
10+ import java .util .Collection ;
11+ import java .util .HashMap ;
12+ import java .util .Map ;
13+
14+ import org .junit .jupiter .api .BeforeEach ;
15+ import org .junit .jupiter .api .Test ;
16+ import org .junit .jupiter .api .extension .ExtendWith ;
17+ import org .mockito .InjectMocks ;
18+ import org .mockito .Mock ;
19+ import org .mockito .junit .jupiter .MockitoExtension ;
20+ import org .springframework .security .authentication .UsernamePasswordAuthenticationToken ;
21+ import org .springframework .security .authentication .event .AuthenticationSuccessEvent ;
22+ import org .springframework .security .core .Authentication ;
23+ import org .springframework .security .core .GrantedAuthority ;
24+ import org .springframework .security .oauth2 .core .user .OAuth2User ;
25+
26+ import com .digitalsanctuary .spring .user .persistence .model .User ;
27+ import com .digitalsanctuary .spring .user .service .DSUserDetails ;
28+ import com .digitalsanctuary .spring .user .service .LoginAttemptService ;
29+
30+ /**
31+ * Test class for AuthenticationEventListener OAuth2 authentication handling.
32+ * Verifies that the listener correctly extracts usernames from different principal types.
33+ */
34+ @ ExtendWith (MockitoExtension .class )
35+ class AuthenticationEventListenerOAuth2Test {
36+
37+ @ Mock
38+ private LoginAttemptService loginAttemptService ;
39+
40+ @ Mock
41+ private OAuth2User oauth2User ;
42+
43+ @ Mock
44+ private DSUserDetails dsUserDetails ;
45+
46+ @ InjectMocks
47+ private AuthenticationEventListener authenticationEventListener ;
48+
49+ private User testUser ;
50+
51+ @ BeforeEach
52+ void setUp () {
53+ testUser = new User ();
54+ testUser .
setEmail (
"[email protected] " );
55+ testUser .setFirstName ("Test" );
56+ testUser .setLastName ("User" );
57+ }
58+
59+ @ Test
60+ void onSuccess_withDSUserDetailsPrincipal_extractsEmailCorrectly () {
61+ // Given
62+ when (
dsUserDetails .
getUsername ()).
thenReturn (
"[email protected] " );
63+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
64+ dsUserDetails , null , dsUserDetails .getAuthorities ());
65+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth );
66+
67+ // When
68+ authenticationEventListener .onSuccess (event );
69+
70+ // Then
71+ verify (
loginAttemptService ).
loginSucceeded (
"[email protected] " );
72+ }
73+
74+ @ Test
75+ void onSuccess_withOAuth2UserPrincipalWithEmail_extractsEmailCorrectly () {
76+ // Given
77+ when (
oauth2User .
getAttribute (
"email" )).
thenReturn (
"[email protected] " );
78+
79+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
80+ oauth2User , null , oauth2User .getAuthorities ());
81+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth );
82+
83+ // When
84+ authenticationEventListener .onSuccess (event );
85+
86+ // Then
87+ verify (
loginAttemptService ).
loginSucceeded (
"[email protected] " );
88+ }
89+
90+ @ Test
91+ void onSuccess_withOAuth2UserPrincipalWithoutEmail_fallsBackToName () {
92+ // Given
93+ when (oauth2User .getAttribute ("email" )).thenReturn (null );
94+ when (oauth2User .getName ()).thenReturn ("oauth_user_123" );
95+
96+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
97+ oauth2User , null , oauth2User .getAuthorities ());
98+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth );
99+
100+ // When
101+ authenticationEventListener .onSuccess (event );
102+
103+ // Then
104+ verify (loginAttemptService ).loginSucceeded ("oauth_user_123" );
105+ }
106+
107+ @ Test
108+ void onSuccess_withStringPrincipal_extractsUsernameCorrectly () {
109+ // Given
110+ String username =
"[email protected] " ;
111+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
112+ username , null );
113+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth );
114+
115+ // When
116+ authenticationEventListener .onSuccess (event );
117+
118+ // Then
119+ verify (loginAttemptService ).loginSucceeded (username );
120+ }
121+
122+ @ Test
123+ void onSuccess_withUnknownPrincipalType_fallsBackToGetName () {
124+ // Given
125+ Object unknownPrincipal = new Object () {
126+ @ Override
127+ public String toString () {
128+ return "unknown_principal" ;
129+ }
130+ };
131+
132+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
133+ unknownPrincipal , null );
134+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth ) {
135+ @ Override
136+ public Authentication getAuthentication () {
137+ return new Authentication () {
138+ @ Override
139+ public String getName () {
140+ 141+ }
142+
143+ @ Override
144+ public Collection <? extends GrantedAuthority > getAuthorities () {
145+ return auth .getAuthorities ();
146+ }
147+
148+ @ Override
149+ public Object getCredentials () {
150+ return auth .getCredentials ();
151+ }
152+
153+ @ Override
154+ public Object getDetails () {
155+ return auth .getDetails ();
156+ }
157+
158+ @ Override
159+ public Object getPrincipal () {
160+ return unknownPrincipal ;
161+ }
162+
163+ @ Override
164+ public boolean isAuthenticated () {
165+ return auth .isAuthenticated ();
166+ }
167+
168+ @ Override
169+ public void setAuthenticated (boolean isAuthenticated ) throws IllegalArgumentException {
170+ auth .setAuthenticated (isAuthenticated );
171+ }
172+ };
173+ }
174+ };
175+
176+ // When
177+ authenticationEventListener .onSuccess (event );
178+
179+ // Then
180+ verify (
loginAttemptService ).
loginSucceeded (
"[email protected] " );
181+ }
182+
183+ @ Test
184+ void onSuccess_withNullUsername_doesNotCallLoginAttemptService () {
185+ // Given
186+ when (oauth2User .getAttribute ("email" )).thenReturn (null );
187+ when (oauth2User .getName ()).thenReturn (null );
188+
189+ // Create an authentication with a custom implementation that returns null for getName()
190+ Authentication auth = new Authentication () {
191+ @ Override
192+ public String getName () {
193+ return null ;
194+ }
195+
196+ @ Override
197+ public Collection <? extends GrantedAuthority > getAuthorities () {
198+ return oauth2User .getAuthorities ();
199+ }
200+
201+ @ Override
202+ public Object getCredentials () {
203+ return null ;
204+ }
205+
206+ @ Override
207+ public Object getDetails () {
208+ return null ;
209+ }
210+
211+ @ Override
212+ public Object getPrincipal () {
213+ return oauth2User ;
214+ }
215+
216+ @ Override
217+ public boolean isAuthenticated () {
218+ return true ;
219+ }
220+
221+ @ Override
222+ public void setAuthenticated (boolean isAuthenticated ) throws IllegalArgumentException {
223+ }
224+ };
225+
226+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth );
227+
228+ // When
229+ authenticationEventListener .onSuccess (event );
230+
231+ // Then
232+ verify (loginAttemptService , never ()).loginSucceeded (anyString ());
233+ }
234+
235+ @ Test
236+ void onSuccess_withNullPrincipal_handlesGracefully () {
237+ // Given
238+ UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken (
239+ null , null );
240+ AuthenticationSuccessEvent event = new AuthenticationSuccessEvent (auth ) {
241+ @ Override
242+ public Authentication getAuthentication () {
243+ return new Authentication () {
244+ @ Override
245+ public String getName () {
246+ 247+ }
248+
249+ @ Override
250+ public Collection <? extends GrantedAuthority > getAuthorities () {
251+ return auth .getAuthorities ();
252+ }
253+
254+ @ Override
255+ public Object getCredentials () {
256+ return auth .getCredentials ();
257+ }
258+
259+ @ Override
260+ public Object getDetails () {
261+ return auth .getDetails ();
262+ }
263+
264+ @ Override
265+ public Object getPrincipal () {
266+ return null ;
267+ }
268+
269+ @ Override
270+ public boolean isAuthenticated () {
271+ return auth .isAuthenticated ();
272+ }
273+
274+ @ Override
275+ public void setAuthenticated (boolean isAuthenticated ) throws IllegalArgumentException {
276+ auth .setAuthenticated (isAuthenticated );
277+ }
278+ };
279+ }
280+ };
281+
282+ // When
283+ authenticationEventListener .onSuccess (event );
284+
285+ // Then
286+ verify (
loginAttemptService ).
loginSucceeded (
"[email protected] " );
287+ }
288+ }
0 commit comments