Skip to content

Commit

Permalink
Merge branch '2.x' into 2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanL1997 authored Feb 27, 2023
2 parents 8b1227c + 9919146 commit d69ed29
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,46 @@ public boolean exists(User user) {
if(securityRoles != null) {
user.addSecurityRoles(securityRoles);
}

user.addAttributes(attributeMap);
return true;
}

return false;
}

/**
* A helper function used to verify that both invalid and valid usernames have a hashing check during testing.
* @param hash A string hash of the stored user's password.
* @param array A char array of the provided password
* @return Whether the hash matches the provided password
*/
public boolean passwordMatchesHash(String hash, char[] array) {
return OpenBSDBCrypt.checkPassword(hash, array);
}

@Override
public User authenticate(final AuthCredentials credentials) {

boolean userExists;

if (internalUsersModel == null) {
throw new OpenSearchSecurityException("Internal authentication backend not configured. May be OpenSearch is not initialized.");
}

final byte[] password;
String hash;
if(!internalUsersModel.exists(credentials.getUsername())) {
throw new OpenSearchSecurityException(credentials.getUsername() + " not found");
userExists = false;
password = credentials.getPassword();
hash = "$2y$12$NmKhjNssNgSIj8iXT7SYxeXvMA1E95a9tCt4cySY9FrQ4fB18xEc2"; // Ensure the same cryptographic complexity for users not found and invalid password
} else {
userExists = true;
password = credentials.getPassword();
hash = internalUsersModel.getHash(credentials.getUsername());
}

final byte[] password = credentials.getPassword();

if(password == null || password.length == 0) {
if (password == null || password.length == 0) {
throw new OpenSearchSecurityException("empty passwords not supported");
}

Expand All @@ -108,24 +126,26 @@ public User authenticate(final AuthCredentials credentials) {
Arrays.fill(password, (byte)0);

try {
if (OpenBSDBCrypt.checkPassword(internalUsersModel.getHash(credentials.getUsername()), array)) {
if (passwordMatchesHash(hash, array) && userExists) {
final List<String> roles = internalUsersModel.getBackenRoles(credentials.getUsername());
final Map<String, String> customAttributes = internalUsersModel.getAttributes(credentials.getUsername());
if(customAttributes != null) {
for(Entry<String, String> attributeName: customAttributes.entrySet()) {
credentials.addAttribute("attr.internal."+attributeName.getKey(), attributeName.getValue());
}
}

final User user = new User(credentials.getUsername(), roles, credentials);

final List<String> securityRoles = internalUsersModel.getSecurityRoles(credentials.getUsername());
if(securityRoles != null) {
user.addSecurityRoles(securityRoles);
}

return user;
} else {
if (!userExists) {
throw new OpenSearchSecurityException(credentials.getUsername() + " not found");
}
throw new OpenSearchSecurityException("password does not match");
}
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.security.auth;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import org.opensearch.OpenSearchSecurityException;
import org.opensearch.security.auth.internal.InternalAuthenticationBackend;
import org.opensearch.security.securityconf.InternalUsersModel;
import org.opensearch.security.user.AuthCredentials;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;

public class InternalAuthBackendTests {

private InternalUsersModel internalUsersModel;

private InternalAuthenticationBackend internalAuthenticationBackend;

@Before
public void internalAuthBackendTestsSetup() {
internalAuthenticationBackend = spy(new InternalAuthenticationBackend());
internalUsersModel = mock(InternalUsersModel.class);
internalAuthenticationBackend.onInternalUsersModelChanged(internalUsersModel);
}

private char[] createArrayFromPasswordBytes(byte[] password) {
ByteBuffer wrap = ByteBuffer.wrap(password);
CharBuffer buf = StandardCharsets.UTF_8.decode(wrap);
char[] array = new char[buf.limit()];
buf.get(array);
Arrays.fill(password, (byte)0);
return array;
}

@Test
public void testHashActionWithValidUserValidPassword() {

// Make authentication info for valid username with valid password
final String validPassword = "admin";
final byte[] validPasswordBytes = validPassword.getBytes();

final AuthCredentials validUsernameAuth = new AuthCredentials("admin", validPasswordBytes);

final String hash = "$2y$12$NmKhjNssNgSIj8iXT7SYxeXvMA1E95a9tCt4cySY9FrQ4fB18xEc2";

char[] array = createArrayFromPasswordBytes(validPasswordBytes);


when(internalUsersModel.getHash(validUsernameAuth.getUsername())).thenReturn(hash);
when(internalUsersModel.exists(validUsernameAuth.getUsername())).thenReturn(true);
doReturn(true).when(internalAuthenticationBackend).passwordMatchesHash(Mockito.any(String.class), Mockito.any(char[].class));

//Act
internalAuthenticationBackend.authenticate(validUsernameAuth);

verify(internalAuthenticationBackend, times(1)).passwordMatchesHash(hash, array);
verify(internalUsersModel, times(1)).getBackenRoles(validUsernameAuth.getUsername());
}

@Test
public void testHashActionWithValidUserInvalidPassword() {

// Make authentication info for valid with bad password
final String gibberishPassword = "ajdhflkasdjfaklsdf";
final byte[] gibberishPasswordBytes = gibberishPassword.getBytes();
final AuthCredentials validUsernameAuth = new AuthCredentials("admin", gibberishPasswordBytes);

final String hash = "$2y$12$NmKhjNssNgSIj8iXT7SYxeXvMA1E95a9tCt4cySY9FrQ4fB18xEc2";

char[] array = createArrayFromPasswordBytes(gibberishPasswordBytes);

when(internalUsersModel.getHash("admin")).thenReturn(hash);
when(internalUsersModel.exists("admin")).thenReturn(true);

OpenSearchSecurityException ex = Assert.assertThrows(OpenSearchSecurityException.class,
() -> internalAuthenticationBackend.authenticate(validUsernameAuth));
assert(ex.getMessage().contains("password does not match"));
verify(internalAuthenticationBackend, times(1)).passwordMatchesHash(hash, array);
}

@Test
public void testHashActionWithInvalidUserValidPassword() {

// Make authentication info for valid and invalid usernames both with bad passwords
final String validPassword = "admin";
final byte[] validPasswordBytes = validPassword.getBytes();
final AuthCredentials invalidUsernameAuth = new AuthCredentials("ertyuiykgjjfguyifdghc", validPasswordBytes);

final String hash = "$2y$12$NmKhjNssNgSIj8iXT7SYxeXvMA1E95a9tCt4cySY9FrQ4fB18xEc2";

char[] array = createArrayFromPasswordBytes(validPasswordBytes);

when(internalUsersModel.exists("ertyuiykgjjfguyifdghc")).thenReturn(false);
when(internalAuthenticationBackend.passwordMatchesHash(hash, array)).thenReturn(true); //Say that the password is correct

OpenSearchSecurityException ex = Assert.assertThrows(OpenSearchSecurityException.class,
() -> internalAuthenticationBackend.authenticate(invalidUsernameAuth));
assert(ex.getMessage().contains("not found"));
verify(internalAuthenticationBackend, times(1)).passwordMatchesHash(hash, array);
}

@Test
public void testHashActionWithInvalidUserInvalidPassword() {

// Make authentication info for valid and invalid usernames both with bad passwords
final String gibberishPassword = "ajdhflkasdjfaklsdf";
final byte[] gibberishPasswordBytes = gibberishPassword.getBytes();
final AuthCredentials invalidUsernameAuth = new AuthCredentials("ertyuiykgjjfguyifdghc", gibberishPasswordBytes);

final String hash = "$2y$12$NmKhjNssNgSIj8iXT7SYxeXvMA1E95a9tCt4cySY9FrQ4fB18xEc2";

char[] array = createArrayFromPasswordBytes(gibberishPasswordBytes);

when(internalUsersModel.exists("ertyuiykgjjfguyifdghc")).thenReturn(false);


OpenSearchSecurityException ex = Assert.assertThrows(OpenSearchSecurityException.class,
() -> internalAuthenticationBackend.authenticate(invalidUsernameAuth));
verify(internalAuthenticationBackend, times(1)).passwordMatchesHash(hash, array);
assert(ex.getMessage().contains("not found"));
}
}

0 comments on commit d69ed29

Please sign in to comment.