Skip to content

Commit 40bf58e

Browse files
committed
Fix RealmInteg test failures
As part of the changes in #31234,the password verification logic determines the algorithm used for hashing the password from the format of the stored password hash itself. Thus, it is generally possible to validate a password even if it's associated stored hash was not created with the same algorithm than the one currently set in the settings. At the same time, we introduced a check for incoming client change password requests to make sure that the request's password is hashed with the same algorithm that is configured to be used in the node settings. In the spirit of randomizing the algorithms used, the {@code SecurityClient} used in the {@code NativeRealmIntegTests} and {@code ReservedRealmIntegTests} would send all requests dealing with user passwords by randomly selecting a hashing algorithm each time. This meant that some change password requests were using a different password hashing algorithm than the one used for the node and the request would fail. This commit changes this behavior in the two aforementioned Integ tests to use the same password hashing algorithm for the node and the clients, no matter what the request is. Resolves #31670
1 parent b7b413e commit 40bf58e

File tree

2 files changed

+56
-34
lines changed

2 files changed

+56
-34
lines changed

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java

+36-33
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.xpack.core.security.action.user.ChangePasswordResponse;
3636
import org.elasticsearch.xpack.core.security.action.user.DeleteUserResponse;
3737
import org.elasticsearch.xpack.core.security.action.user.GetUsersResponse;
38+
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
3839
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
3940
import org.elasticsearch.xpack.core.security.authz.permission.Role;
4041
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
@@ -72,22 +73,24 @@
7273
public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
7374

7475
private static boolean anonymousEnabled;
76+
private static Hasher hasher;
7577

7678
private boolean roleExists;
7779

7880
@BeforeClass
7981
public static void init() {
8082
anonymousEnabled = randomBoolean();
83+
hasher = getFastStoredHashAlgoForTests();
8184
}
8285

8386
@Override
8487
public Settings nodeSettings(int nodeOrdinal) {
88+
Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal))
89+
.put("xpack.security.authc.password_hashing.algorithm", hasher.name());
8590
if (anonymousEnabled) {
86-
return Settings.builder().put(super.nodeSettings(nodeOrdinal))
87-
.put(AnonymousUser.ROLES_SETTING.getKey(), "native_anonymous")
88-
.build();
91+
builder.put(AnonymousUser.ROLES_SETTING.getKey(), "native_anonymous");
8992
}
90-
return super.nodeSettings(nodeOrdinal);
93+
return builder.build();
9194
}
9295

9396
@Before
@@ -111,7 +114,7 @@ public void setupAnonymousRoleIfNecessary() throws Exception {
111114
public void testDeletingNonexistingUserAndRole() throws Exception {
112115
SecurityClient c = securityClient();
113116
// first create the index so it exists
114-
c.preparePutUser("joe", "s3kirt".toCharArray(), getFastStoredHashAlgoForTests(), "role1", "user").get();
117+
c.preparePutUser("joe", "s3kirt".toCharArray(), hasher, "role1", "user").get();
115118
DeleteUserResponse resp = c.prepareDeleteUser("missing").get();
116119
assertFalse("user shouldn't be found", resp.found());
117120
DeleteRoleResponse resp2 = c.prepareDeleteRole("role").get();
@@ -131,7 +134,7 @@ public void testAddAndGetUser() throws Exception {
131134
final List<User> existingUsers = Arrays.asList(c.prepareGetUsers().get().users());
132135
final int existing = existingUsers.size();
133136
logger.error("--> creating user");
134-
c.preparePutUser("joe", "s3kirt".toCharArray(), getFastStoredHashAlgoForTests(), "role1", "user").get();
137+
c.preparePutUser("joe", "s3kirt".toCharArray(), hasher, "role1", "user").get();
135138
logger.error("--> waiting for .security index");
136139
ensureGreen(SECURITY_INDEX_NAME);
137140
logger.info("--> retrieving user");
@@ -142,8 +145,8 @@ public void testAddAndGetUser() throws Exception {
142145
assertArrayEquals(joe.roles(), new String[]{"role1", "user"});
143146

144147
logger.info("--> adding two more users");
145-
c.preparePutUser("joe2", "s3kirt2".toCharArray(), getFastStoredHashAlgoForTests(), "role2", "user").get();
146-
c.preparePutUser("joe3", "s3kirt3".toCharArray(), getFastStoredHashAlgoForTests(), "role3", "user").get();
148+
c.preparePutUser("joe2", "s3kirt2".toCharArray(), hasher, "role2", "user").get();
149+
c.preparePutUser("joe3", "s3kirt3".toCharArray(), hasher, "role3", "user").get();
147150
GetUsersResponse allUsersResp = c.prepareGetUsers().get();
148151
assertTrue("users should exist", allUsersResp.hasUsers());
149152
assertEquals("should be " + (3 + existing) + " users total", 3 + existing, allUsersResp.users().length);
@@ -237,7 +240,7 @@ public void testAddUserAndRoleThenAuth() throws Exception {
237240
new BytesArray("{\"match_all\": {}}"))
238241
.get();
239242
logger.error("--> creating user");
240-
c.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), "test_role").get();
243+
c.preparePutUser("joe", "s3krit".toCharArray(), hasher, "test_role").get();
241244
logger.error("--> waiting for .security index");
242245
ensureGreen(SECURITY_INDEX_NAME);
243246
logger.info("--> retrieving user");
@@ -258,7 +261,7 @@ public void testAddUserAndRoleThenAuth() throws Exception {
258261
public void testUpdatingUserAndAuthentication() throws Exception {
259262
SecurityClient c = securityClient();
260263
logger.error("--> creating user");
261-
c.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), SecuritySettingsSource.TEST_ROLE).get();
264+
c.preparePutUser("joe", "s3krit".toCharArray(), hasher, SecuritySettingsSource.TEST_ROLE).get();
262265
logger.error("--> waiting for .security index");
263266
ensureGreen(SECURITY_INDEX_NAME);
264267
logger.info("--> retrieving user");
@@ -275,7 +278,7 @@ public void testUpdatingUserAndAuthentication() throws Exception {
275278

276279
assertEquals(1L, searchResp.getHits().getTotalHits());
277280

278-
c.preparePutUser("joe", "s3krit2".toCharArray(), getFastStoredHashAlgoForTests(), SecuritySettingsSource.TEST_ROLE).get();
281+
c.preparePutUser("joe", "s3krit2".toCharArray(), hasher, SecuritySettingsSource.TEST_ROLE).get();
279282

280283
try {
281284
client().filterWithHeader(Collections.singletonMap("Authorization", token)).prepareSearch("idx").get();
@@ -293,7 +296,7 @@ public void testUpdatingUserAndAuthentication() throws Exception {
293296
public void testCreateDeleteAuthenticate() {
294297
SecurityClient c = securityClient();
295298
logger.error("--> creating user");
296-
c.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(),
299+
c.preparePutUser("joe", "s3krit".toCharArray(), hasher,
297300
SecuritySettingsSource.TEST_ROLE).get();
298301
logger.error("--> waiting for .security index");
299302
ensureGreen(SECURITY_INDEX_NAME);
@@ -332,7 +335,7 @@ public void testCreateAndUpdateRole() {
332335
new BytesArray("{\"match_all\": {}}"))
333336
.get();
334337
logger.error("--> creating user");
335-
c.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), "test_role").get();
338+
c.preparePutUser("joe", "s3krit".toCharArray(), hasher, "test_role").get();
336339
logger.error("--> waiting for .security index");
337340
ensureGreen(SECURITY_INDEX_NAME);
338341

@@ -381,7 +384,7 @@ public void testAuthenticateWithDeletedRole() {
381384
.addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, null,
382385
new BytesArray("{\"match_all\": {}}"))
383386
.get();
384-
c.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), "test_role").get();
387+
c.preparePutUser("joe", "s3krit".toCharArray(), hasher, "test_role").get();
385388
logger.error("--> waiting for .security index");
386389
ensureGreen(SECURITY_INDEX_NAME);
387390

@@ -415,7 +418,7 @@ public void testPutUserWithoutPassword() {
415418
assertThat(client.prepareGetUsers("joes").get().hasUsers(), is(false));
416419
// check that putting a user without a password fails if the user doesn't exist
417420
try {
418-
client.preparePutUser("joe", null, getFastStoredHashAlgoForTests(), "admin_role").get();
421+
client.preparePutUser("joe", null, hasher, "admin_role").get();
419422
fail("cannot create a user without a password");
420423
} catch (IllegalArgumentException e) {
421424
assertThat(e.getMessage(), containsString("password must be specified"));
@@ -425,15 +428,15 @@ public void testPutUserWithoutPassword() {
425428

426429
// create joe with a password and verify the user works
427430
client.preparePutUser("joe", SecuritySettingsSourceField.TEST_PASSWORD.toCharArray(),
428-
getFastStoredHashAlgoForTests(), "admin_role").get();
431+
hasher, "admin_role").get();
429432
assertThat(client.prepareGetUsers("joe").get().hasUsers(), is(true));
430433
final String token = basicAuthHeaderValue("joe", SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING);
431434
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster()
432435
.prepareHealth().get();
433436
assertFalse(response.isTimedOut());
434437

435438
// modify joe without sending the password
436-
client.preparePutUser("joe", null, getFastStoredHashAlgoForTests(), "read_role").fullName("Joe Smith").get();
439+
client.preparePutUser("joe", null, hasher, "read_role").fullName("Joe Smith").get();
437440
GetUsersResponse getUsersResponse = client.prepareGetUsers("joe").get();
438441
assertThat(getUsersResponse.hasUsers(), is(true));
439442
assertThat(getUsersResponse.users().length, is(1));
@@ -454,7 +457,7 @@ public void testPutUserWithoutPassword() {
454457

455458
// update the user with password and admin role again
456459
String secondPassword = SecuritySettingsSourceField.TEST_PASSWORD + "2";
457-
client.preparePutUser("joe", secondPassword.toCharArray(), getFastStoredHashAlgoForTests(), "admin_role").
460+
client.preparePutUser("joe", secondPassword.toCharArray(), hasher, "admin_role").
458461
fullName("Joe Smith").get();
459462
getUsersResponse = client.prepareGetUsers("joe").get();
460463
assertThat(getUsersResponse.hasUsers(), is(true));
@@ -483,7 +486,7 @@ public void testPutUserWithoutPassword() {
483486
public void testCannotCreateUserWithShortPassword() throws Exception {
484487
SecurityClient client = securityClient();
485488
try {
486-
client.preparePutUser("joe", randomAlphaOfLengthBetween(0, 5).toCharArray(), getFastStoredHashAlgoForTests(),
489+
client.preparePutUser("joe", randomAlphaOfLengthBetween(0, 5).toCharArray(), hasher,
487490
"admin_role").get();
488491
fail("cannot create a user without a password < 6 characters");
489492
} catch (ValidationException v) {
@@ -494,7 +497,7 @@ public void testCannotCreateUserWithShortPassword() throws Exception {
494497
public void testCannotCreateUserWithInvalidCharactersInName() throws Exception {
495498
SecurityClient client = securityClient();
496499
ValidationException v = expectThrows(ValidationException.class,
497-
() -> client.preparePutUser("fóóbár", "my-am@zing-password".toCharArray(), getFastStoredHashAlgoForTests(),
500+
() -> client.preparePutUser("fóóbár", "my-am@zing-password".toCharArray(), hasher,
498501
"admin_role").get()
499502
);
500503
assertThat(v.getMessage(), containsString("names must be"));
@@ -505,7 +508,7 @@ public void testUsersAndRolesDoNotInterfereWithIndicesStats() throws Exception {
505508

506509
SecurityClient client = securityClient();
507510
if (randomBoolean()) {
508-
client.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(),
511+
client.preparePutUser("joe", "s3krit".toCharArray(), hasher,
509512
SecuritySettingsSource.TEST_ROLE).get();
510513
} else {
511514
client.preparePutRole("read_role")
@@ -526,7 +529,7 @@ public void testOperationsOnReservedUsers() throws Exception {
526529
final String username = randomFrom(ElasticUser.NAME, KibanaUser.NAME);
527530
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
528531
() -> securityClient().preparePutUser(username, randomBoolean() ? SecuritySettingsSourceField.TEST_PASSWORD.toCharArray()
529-
: null, getFastStoredHashAlgoForTests(), "admin").get());
532+
: null, hasher, "admin").get());
530533
assertThat(exception.getMessage(), containsString("Username [" + username + "] is reserved"));
531534

532535
exception = expectThrows(IllegalArgumentException.class,
@@ -539,21 +542,21 @@ public void testOperationsOnReservedUsers() throws Exception {
539542

540543
exception = expectThrows(IllegalArgumentException.class,
541544
() -> securityClient().prepareChangePassword(AnonymousUser.DEFAULT_ANONYMOUS_USERNAME, "foobar".toCharArray(),
542-
getFastStoredHashAlgoForTests()).get());
545+
hasher).get());
543546
assertThat(exception.getMessage(), containsString("user [" + AnonymousUser.DEFAULT_ANONYMOUS_USERNAME + "] is anonymous"));
544547

545548
exception = expectThrows(IllegalArgumentException.class,
546549
() -> securityClient().preparePutUser(AnonymousUser.DEFAULT_ANONYMOUS_USERNAME, "foobar".toCharArray(),
547-
getFastStoredHashAlgoForTests()).get());
550+
hasher).get());
548551
assertThat(exception.getMessage(), containsString("Username [" + AnonymousUser.DEFAULT_ANONYMOUS_USERNAME + "] is reserved"));
549552

550553
exception = expectThrows(IllegalArgumentException.class,
551-
() -> securityClient().preparePutUser(SystemUser.NAME, "foobar".toCharArray(), getFastStoredHashAlgoForTests()).get());
554+
() -> securityClient().preparePutUser(SystemUser.NAME, "foobar".toCharArray(), hasher).get());
552555
assertThat(exception.getMessage(), containsString("user [" + SystemUser.NAME + "] is internal"));
553556

554557
exception = expectThrows(IllegalArgumentException.class,
555558
() -> securityClient().prepareChangePassword(SystemUser.NAME, "foobar".toCharArray(),
556-
getFastStoredHashAlgoForTests()).get());
559+
hasher).get());
557560
assertThat(exception.getMessage(), containsString("user [" + SystemUser.NAME + "] is internal"));
558561

559562
exception = expectThrows(IllegalArgumentException.class,
@@ -592,7 +595,7 @@ public void testOperationsOnReservedRoles() throws Exception {
592595

593596
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/31670")
594597
public void testCreateAndChangePassword() throws Exception {
595-
securityClient().preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(),
598+
securityClient().preparePutUser("joe", "s3krit".toCharArray(), hasher,
596599
SecuritySettingsSource.TEST_ROLE).get();
597600
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
598601
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
@@ -601,7 +604,7 @@ public void testCreateAndChangePassword() throws Exception {
601604

602605
ChangePasswordResponse passwordResponse = securityClient(
603606
client().filterWithHeader(Collections.singletonMap("Authorization", token)))
604-
.prepareChangePassword("joe", SecuritySettingsSourceField.TEST_PASSWORD.toCharArray(), getFastStoredHashAlgoForTests()).get();
607+
.prepareChangePassword("joe", SecuritySettingsSourceField.TEST_PASSWORD.toCharArray(), hasher).get();
605608
assertThat(passwordResponse, notNullValue());
606609

607610

@@ -681,7 +684,7 @@ public void testRealmUsageStats() {
681684
final int numNativeUsers = scaledRandomIntBetween(1, 32);
682685
SecurityClient securityClient = new SecurityClient(client());
683686
for (int i = 0; i < numNativeUsers; i++) {
684-
securityClient.preparePutUser("joe" + i, "s3krit".toCharArray(), getFastStoredHashAlgoForTests(),
687+
securityClient.preparePutUser("joe" + i, "s3krit".toCharArray(), hasher,
685688
"superuser").get();
686689
}
687690

@@ -702,7 +705,7 @@ public void testRealmUsageStats() {
702705

703706
public void testSetEnabled() throws Exception {
704707

705-
securityClient().preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(),
708+
securityClient().preparePutUser("joe", "s3krit".toCharArray(), hasher,
706709
SecuritySettingsSource.TEST_ROLE).get();
707710
final String token = basicAuthHeaderValue("joe", new SecureString("s3krit".toCharArray()));
708711
ClusterHealthResponse response = client().filterWithHeader(Collections.singletonMap("Authorization", token))
@@ -727,7 +730,7 @@ public void testSetEnabled() throws Exception {
727730

728731
public void testNegativeLookupsThenCreateRole() throws Exception {
729732
SecurityClient securityClient = new SecurityClient(client());
730-
securityClient.preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), "unknown_role").get();
733+
securityClient.preparePutUser("joe", "s3krit".toCharArray(), hasher, "unknown_role").get();
731734

732735
final int negativeLookups = scaledRandomIntBetween(1, 10);
733736
for (int i = 0; i < negativeLookups; i++) {
@@ -763,9 +766,9 @@ public void testNegativeLookupsThenCreateRole() throws Exception {
763766
* the loader returned a null value, while the other caller(s) would get a null value unexpectedly
764767
*/
765768
public void testConcurrentRunAs() throws Exception {
766-
securityClient().preparePutUser("joe", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), SecuritySettingsSource
769+
securityClient().preparePutUser("joe", "s3krit".toCharArray(), hasher, SecuritySettingsSource
767770
.TEST_ROLE).get();
768-
securityClient().preparePutUser("executor", "s3krit".toCharArray(), getFastStoredHashAlgoForTests(), "superuser").get();
771+
securityClient().preparePutUser("executor", "s3krit".toCharArray(), hasher, "superuser").get();
769772
final String token = basicAuthHeaderValue("executor", new SecureString("s3krit".toCharArray()));
770773
final Client client = client().filterWithHeader(MapBuilder.<String, String>newMapBuilder()
771774
.put("Authorization", token)

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/ReservedRealmIntegTests.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import org.elasticsearch.ElasticsearchSecurityException;
99
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
1010
import org.elasticsearch.common.settings.SecureString;
11+
import org.elasticsearch.common.settings.Settings;
1112
import org.elasticsearch.test.NativeRealmIntegTestCase;
1213
import org.elasticsearch.xpack.core.security.action.user.ChangePasswordResponse;
14+
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
1315
import org.elasticsearch.xpack.core.security.client.SecurityClient;
1416
import org.elasticsearch.xpack.core.security.user.BeatsSystemUser;
1517
import org.elasticsearch.xpack.core.security.user.ElasticUser;
1618
import org.elasticsearch.xpack.core.security.user.KibanaUser;
1719
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
20+
import org.junit.BeforeClass;
1821

1922
import java.util.Arrays;
2023

@@ -29,6 +32,22 @@
2932
*/
3033
public class ReservedRealmIntegTests extends NativeRealmIntegTestCase {
3134

35+
private static Hasher hasher;
36+
37+
@BeforeClass
38+
public static void setHasher() {
39+
hasher = getFastStoredHashAlgoForTests();
40+
}
41+
42+
@Override
43+
public Settings nodeSettings(int nodeOrdinal) {
44+
Settings settings = Settings.builder()
45+
.put(super.nodeSettings(nodeOrdinal))
46+
.put("xpack.security.authc.password_hashing.algorithm", hasher.name())
47+
.build();
48+
return settings;
49+
}
50+
3251
public void testAuthenticate() {
3352
for (String username : Arrays.asList(ElasticUser.NAME, KibanaUser.NAME, LogstashSystemUser.NAME, BeatsSystemUser.NAME)) {
3453
ClusterHealthResponse response = client()
@@ -77,7 +96,7 @@ public void testChangingPassword() {
7796
}
7897

7998
ChangePasswordResponse response = securityClient()
80-
.prepareChangePassword(username, Arrays.copyOf(newPassword, newPassword.length), getFastStoredHashAlgoForTests())
99+
.prepareChangePassword(username, Arrays.copyOf(newPassword, newPassword.length), hasher)
81100
.get();
82101
assertThat(response, notNullValue());
83102

0 commit comments

Comments
 (0)