Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix iterate-from-1 bug in smart realm order #49473

Merged
merged 1 commit into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,13 @@ private List<Realm> getRealmList(String principal) {
if (index > 0) {
final List<Realm> smartOrder = new ArrayList<>(orderedRealmList.size());
smartOrder.add(lastSuccess);
for (int i = 1; i < orderedRealmList.size(); i++) {
for (int i = 0; i < orderedRealmList.size(); i++) {
if (i != index) {
smartOrder.add(orderedRealmList.get(i));
}
}
assert smartOrder.size() == orderedRealmList.size() && smartOrder.containsAll(orderedRealmList)
: "Element mismatch between SmartOrder=" + smartOrder + " and DefaultOrder=" + orderedRealmList;
return Collections.unmodifiableList(smartOrder);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@
*/
public class AuthenticationServiceTests extends ESTestCase {

private static final String SECOND_REALM_NAME = "second_realm";
private static final String SECOND_REALM_TYPE = "second";
private static final String FIRST_REALM_NAME = "file_realm";
private static final String FIRST_REALM_TYPE = "file";
private AuthenticationService service;
private TransportMessage message;
private RestRequest restRequest;
Expand Down Expand Up @@ -167,11 +171,11 @@ public void init() throws Exception {
threadContext = new ThreadContext(Settings.EMPTY);

firstRealm = mock(Realm.class);
when(firstRealm.type()).thenReturn("file");
when(firstRealm.name()).thenReturn("file_realm");
when(firstRealm.type()).thenReturn(FIRST_REALM_TYPE);
when(firstRealm.name()).thenReturn(FIRST_REALM_NAME);
secondRealm = mock(Realm.class);
when(secondRealm.type()).thenReturn("second");
when(secondRealm.name()).thenReturn("second_realm");
when(secondRealm.type()).thenReturn(SECOND_REALM_TYPE);
when(secondRealm.name()).thenReturn(SECOND_REALM_NAME);
Settings settings = Settings.builder()
.put("path.home", createTempDir())
.put("node.name", "authc_test")
Expand Down Expand Up @@ -304,26 +308,35 @@ public void testAuthenticateSmartRealmOrdering() {
when(secondRealm.token(threadContext)).thenReturn(token);
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);

// Authenticate against the normal chain. 1st Realm will be checked (and not pass) then 2nd realm will successfully authc
final AtomicBoolean completed = new AtomicBoolean(false);
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
assertThat(result, notNullValue());
assertThat(result.getUser(), is(user));
assertThat(result.getLookedUpBy(), is(nullValue()));
assertThat(result.getAuthenticatedBy(), is(notNullValue())); // TODO implement equals
assertThat(result.getAuthenticatedBy().getName(), is(SECOND_REALM_NAME));
assertThat(result.getAuthenticatedBy().getType(), is(SECOND_REALM_TYPE));
assertThreadContextContainsAuthentication(result);
setCompletedToTrue(completed);
}, this::logAndFail));
assertTrue(completed.get());

completed.set(false);
// Authenticate against the smart chain.
// "SecondRealm" will be at the top of the list and will successfully authc.
// "FirstRealm" will not be used
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
assertThat(result, notNullValue());
assertThat(result.getUser(), is(user));
assertThat(result.getLookedUpBy(), is(nullValue()));
assertThat(result.getAuthenticatedBy(), is(notNullValue())); // TODO implement equals
assertThat(result.getAuthenticatedBy().getName(), is(SECOND_REALM_NAME));
assertThat(result.getAuthenticatedBy().getType(), is(SECOND_REALM_TYPE));
assertThreadContextContainsAuthentication(result);
setCompletedToTrue(completed);
}, this::logAndFail));

verify(auditTrail).authenticationFailed(reqId, firstRealm.name(), token, "_action", message);
verify(auditTrail, times(2)).authenticationSuccess(reqId, secondRealm.name(), user, "_action", message);
verify(firstRealm, times(2)).name(); // used above one time
Expand All @@ -336,6 +349,30 @@ public void testAuthenticateSmartRealmOrdering() {
verify(firstRealm).authenticate(eq(token), any(ActionListener.class));
verify(secondRealm, times(2)).authenticate(eq(token), any(ActionListener.class));
verifyNoMoreInteractions(auditTrail, firstRealm, secondRealm);

// Now assume some change in the backend system so that 2nd realm no longer has the user, but the 1st realm does.
mockAuthenticate(secondRealm, token, null);
mockAuthenticate(firstRealm, token, user);

completed.set(false);
// This will authenticate against the smart chain.
// "SecondRealm" will be at the top of the list but will no longer authenticate the user.
// Then "FirstRealm" will be checked.
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
assertThat(result, notNullValue());
assertThat(result.getUser(), is(user));
assertThat(result.getLookedUpBy(), is(nullValue()));
assertThat(result.getAuthenticatedBy(), is(notNullValue()));
assertThat(result.getAuthenticatedBy().getName(), is(FIRST_REALM_NAME));
assertThat(result.getAuthenticatedBy().getType(), is(FIRST_REALM_TYPE));
assertThreadContextContainsAuthentication(result);
setCompletedToTrue(completed);
}, this::logAndFail));

verify(auditTrail, times(1)).authenticationFailed(reqId, SECOND_REALM_NAME, token, "_action", message);
verify(auditTrail, times(1)).authenticationSuccess(reqId, FIRST_REALM_NAME, user, "_action", message);
verify(secondRealm, times(3)).authenticate(eq(token), any(ActionListener.class)); // 2 from above + 1 more
verify(firstRealm, times(2)).authenticate(eq(token), any(ActionListener.class)); // 1 from above + 1 more
}

public void testCacheClearOnSecurityIndexChange() {
Expand Down