134134 */
135135public class AuthenticationServiceTests extends ESTestCase {
136136
137+ private static final String SECOND_REALM_NAME = "second_realm" ;
138+ private static final String SECOND_REALM_TYPE = "second" ;
139+ private static final String FIRST_REALM_NAME = "file_realm" ;
140+ private static final String FIRST_REALM_TYPE = "file" ;
137141 private AuthenticationService service ;
138142 private TransportMessage message ;
139143 private RestRequest restRequest ;
@@ -167,11 +171,11 @@ public void init() throws Exception {
167171 threadContext = new ThreadContext (Settings .EMPTY );
168172
169173 firstRealm = mock (Realm .class );
170- when (firstRealm .type ()).thenReturn ("file" );
171- when (firstRealm .name ()).thenReturn ("file_realm" );
174+ when (firstRealm .type ()).thenReturn (FIRST_REALM_TYPE );
175+ when (firstRealm .name ()).thenReturn (FIRST_REALM_NAME );
172176 secondRealm = mock (Realm .class );
173- when (secondRealm .type ()).thenReturn ("second" );
174- when (secondRealm .name ()).thenReturn ("second_realm" );
177+ when (secondRealm .type ()).thenReturn (SECOND_REALM_TYPE );
178+ when (secondRealm .name ()).thenReturn (SECOND_REALM_NAME );
175179 Settings settings = Settings .builder ()
176180 .put ("path.home" , createTempDir ())
177181 .put ("node.name" , "authc_test" )
@@ -305,26 +309,35 @@ public void testAuthenticateSmartRealmOrdering() {
305309 when (secondRealm .token (threadContext )).thenReturn (token );
306310 final String reqId = AuditUtil .getOrGenerateRequestId (threadContext );
307311
312+ // Authenticate against the normal chain. 1st Realm will be checked (and not pass) then 2nd realm will successfully authc
308313 final AtomicBoolean completed = new AtomicBoolean (false );
309314 service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
310315 assertThat (result , notNullValue ());
311316 assertThat (result .getUser (), is (user ));
312317 assertThat (result .getLookedUpBy (), is (nullValue ()));
313318 assertThat (result .getAuthenticatedBy (), is (notNullValue ())); // TODO implement equals
319+ assertThat (result .getAuthenticatedBy ().getName (), is (SECOND_REALM_NAME ));
320+ assertThat (result .getAuthenticatedBy ().getType (), is (SECOND_REALM_TYPE ));
314321 assertThreadContextContainsAuthentication (result );
315322 setCompletedToTrue (completed );
316323 }, this ::logAndFail ));
317324 assertTrue (completed .get ());
318325
319326 completed .set (false );
327+ // Authenticate against the smart chain.
328+ // "SecondRealm" will be at the top of the list and will successfully authc.
329+ // "FirstRealm" will not be used
320330 service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
321331 assertThat (result , notNullValue ());
322332 assertThat (result .getUser (), is (user ));
323333 assertThat (result .getLookedUpBy (), is (nullValue ()));
324334 assertThat (result .getAuthenticatedBy (), is (notNullValue ())); // TODO implement equals
335+ assertThat (result .getAuthenticatedBy ().getName (), is (SECOND_REALM_NAME ));
336+ assertThat (result .getAuthenticatedBy ().getType (), is (SECOND_REALM_TYPE ));
325337 assertThreadContextContainsAuthentication (result );
326338 setCompletedToTrue (completed );
327339 }, this ::logAndFail ));
340+
328341 verify (auditTrail ).authenticationFailed (reqId , firstRealm .name (), token , "_action" , message );
329342 verify (auditTrail , times (2 )).authenticationSuccess (reqId , secondRealm .name (), user , "_action" , message );
330343 verify (firstRealm , times (2 )).name (); // used above one time
@@ -337,6 +350,30 @@ public void testAuthenticateSmartRealmOrdering() {
337350 verify (firstRealm ).authenticate (eq (token ), any (ActionListener .class ));
338351 verify (secondRealm , times (2 )).authenticate (eq (token ), any (ActionListener .class ));
339352 verifyNoMoreInteractions (auditTrail , firstRealm , secondRealm );
353+
354+ // Now assume some change in the backend system so that 2nd realm no longer has the user, but the 1st realm does.
355+ mockAuthenticate (secondRealm , token , null );
356+ mockAuthenticate (firstRealm , token , user );
357+
358+ completed .set (false );
359+ // This will authenticate against the smart chain.
360+ // "SecondRealm" will be at the top of the list but will no longer authenticate the user.
361+ // Then "FirstRealm" will be checked.
362+ service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
363+ assertThat (result , notNullValue ());
364+ assertThat (result .getUser (), is (user ));
365+ assertThat (result .getLookedUpBy (), is (nullValue ()));
366+ assertThat (result .getAuthenticatedBy (), is (notNullValue ()));
367+ assertThat (result .getAuthenticatedBy ().getName (), is (FIRST_REALM_NAME ));
368+ assertThat (result .getAuthenticatedBy ().getType (), is (FIRST_REALM_TYPE ));
369+ assertThreadContextContainsAuthentication (result );
370+ setCompletedToTrue (completed );
371+ }, this ::logAndFail ));
372+
373+ verify (auditTrail , times (1 )).authenticationFailed (reqId , SECOND_REALM_NAME , token , "_action" , message );
374+ verify (auditTrail , times (1 )).authenticationSuccess (reqId , FIRST_REALM_NAME , user , "_action" , message );
375+ verify (secondRealm , times (3 )).authenticate (eq (token ), any (ActionListener .class )); // 2 from above + 1 more
376+ verify (firstRealm , times (2 )).authenticate (eq (token ), any (ActionListener .class )); // 1 from above + 1 more
340377 }
341378
342379 public void testCacheClearOnSecurityIndexChange () {
0 commit comments