136136 */
137137public class AuthenticationServiceTests extends ESTestCase {
138138
139+ private static final String SECOND_REALM_NAME = "second_realm" ;
140+ private static final String SECOND_REALM_TYPE = "second" ;
141+ private static final String FIRST_REALM_NAME = "file_realm" ;
142+ private static final String FIRST_REALM_TYPE = "file" ;
139143 private AuthenticationService service ;
140144 private TransportMessage message ;
141145 private RestRequest restRequest ;
@@ -170,11 +174,11 @@ public void init() throws Exception {
170174 threadContext = new ThreadContext (Settings .EMPTY );
171175
172176 firstRealm = mock (Realm .class );
173- when (firstRealm .type ()).thenReturn ("file" );
174- when (firstRealm .name ()).thenReturn ("file_realm" );
177+ when (firstRealm .type ()).thenReturn (FIRST_REALM_TYPE );
178+ when (firstRealm .name ()).thenReturn (FIRST_REALM_NAME );
175179 secondRealm = mock (Realm .class );
176- when (secondRealm .type ()).thenReturn ("second" );
177- when (secondRealm .name ()).thenReturn ("second_realm" );
180+ when (secondRealm .type ()).thenReturn (SECOND_REALM_TYPE );
181+ when (secondRealm .name ()).thenReturn (SECOND_REALM_NAME );
178182 settings = Settings .builder ()
179183 .put ("path.home" , createTempDir ())
180184 .put ("node.name" , "authc_test" )
@@ -311,26 +315,35 @@ public void testAuthenticateSmartRealmOrdering() {
311315 when (secondRealm .token (threadContext )).thenReturn (token );
312316 final String reqId = AuditUtil .getOrGenerateRequestId (threadContext );
313317
318+ // Authenticate against the normal chain. 1st Realm will be checked (and not pass) then 2nd realm will successfully authc
314319 final AtomicBoolean completed = new AtomicBoolean (false );
315320 service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
316321 assertThat (result , notNullValue ());
317322 assertThat (result .getUser (), is (user ));
318323 assertThat (result .getLookedUpBy (), is (nullValue ()));
319324 assertThat (result .getAuthenticatedBy (), is (notNullValue ())); // TODO implement equals
325+ assertThat (result .getAuthenticatedBy ().getName (), is (SECOND_REALM_NAME ));
326+ assertThat (result .getAuthenticatedBy ().getType (), is (SECOND_REALM_TYPE ));
320327 assertThreadContextContainsAuthentication (result );
321328 setCompletedToTrue (completed );
322329 }, this ::logAndFail ));
323330 assertTrue (completed .get ());
324331
325332 completed .set (false );
333+ // Authenticate against the smart chain.
334+ // "SecondRealm" will be at the top of the list and will successfully authc.
335+ // "FirstRealm" will not be used
326336 service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
327337 assertThat (result , notNullValue ());
328338 assertThat (result .getUser (), is (user ));
329339 assertThat (result .getLookedUpBy (), is (nullValue ()));
330340 assertThat (result .getAuthenticatedBy (), is (notNullValue ())); // TODO implement equals
341+ assertThat (result .getAuthenticatedBy ().getName (), is (SECOND_REALM_NAME ));
342+ assertThat (result .getAuthenticatedBy ().getType (), is (SECOND_REALM_TYPE ));
331343 assertThreadContextContainsAuthentication (result );
332344 setCompletedToTrue (completed );
333345 }, this ::logAndFail ));
346+
334347 verify (auditTrail ).authenticationFailed (reqId , firstRealm .name (), token , "_action" , message );
335348 verify (auditTrail , times (2 )).authenticationSuccess (reqId , secondRealm .name (), user , "_action" , message );
336349 verify (firstRealm , times (2 )).name (); // used above one time
@@ -343,6 +356,30 @@ public void testAuthenticateSmartRealmOrdering() {
343356 verify (firstRealm ).authenticate (eq (token ), any (ActionListener .class ));
344357 verify (secondRealm , times (2 )).authenticate (eq (token ), any (ActionListener .class ));
345358 verifyNoMoreInteractions (auditTrail , firstRealm , secondRealm );
359+
360+ // Now assume some change in the backend system so that 2nd realm no longer has the user, but the 1st realm does.
361+ mockAuthenticate (secondRealm , token , null );
362+ mockAuthenticate (firstRealm , token , user );
363+
364+ completed .set (false );
365+ // This will authenticate against the smart chain.
366+ // "SecondRealm" will be at the top of the list but will no longer authenticate the user.
367+ // Then "FirstRealm" will be checked.
368+ service .authenticate ("_action" , message , (User )null , ActionListener .wrap (result -> {
369+ assertThat (result , notNullValue ());
370+ assertThat (result .getUser (), is (user ));
371+ assertThat (result .getLookedUpBy (), is (nullValue ()));
372+ assertThat (result .getAuthenticatedBy (), is (notNullValue ()));
373+ assertThat (result .getAuthenticatedBy ().getName (), is (FIRST_REALM_NAME ));
374+ assertThat (result .getAuthenticatedBy ().getType (), is (FIRST_REALM_TYPE ));
375+ assertThreadContextContainsAuthentication (result );
376+ setCompletedToTrue (completed );
377+ }, this ::logAndFail ));
378+
379+ verify (auditTrail , times (1 )).authenticationFailed (reqId , SECOND_REALM_NAME , token , "_action" , message );
380+ verify (auditTrail , times (1 )).authenticationSuccess (reqId , FIRST_REALM_NAME , user , "_action" , message );
381+ verify (secondRealm , times (3 )).authenticate (eq (token ), any (ActionListener .class )); // 2 from above + 1 more
382+ verify (firstRealm , times (2 )).authenticate (eq (token ), any (ActionListener .class )); // 1 from above + 1 more
346383 }
347384
348385 public void testCacheClearOnSecurityIndexChange () {
0 commit comments