package fr.xephi.authme.service; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.Sha256; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.SecuritySettings; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.Arrays; import static fr.xephi.authme.AuthMeMatchers.equalToHash; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.hamcrest.MockitoHamcrest.argThat; /** * Test for {@link MigrationService}. */ @RunWith(MockitoJUnitRunner.class) public class MigrationServiceTest { @Mock private Settings settings; @Mock private DataSource dataSource; @Mock private Sha256 sha256; @BeforeClass public static void setUpLogger() { TestHelper.setupLogger(); } @Test public void shouldMigratePlaintextHashes() { // given PlayerAuth auth1 = authWithNickAndHash("bobby", "test"); PlayerAuth auth2 = authWithNickAndHash("user", "myPassword"); PlayerAuth auth3 = authWithNickAndHash("Tester12", "$tester12_pw"); given(dataSource.getAllAuths()).willReturn(Arrays.asList(auth1, auth2, auth3)); setSha256MockToUppercase(sha256); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.PLAINTEXT); // when MigrationService.changePlainTextToSha256(settings, dataSource, sha256); // then verify(sha256, times(3)).computeHash(anyString(), anyString()); verify(dataSource).getAllAuths(); // need to verify this because we use verifyNoMoreInteractions() after verify(dataSource).updatePassword(auth1); assertThat(auth1.getPassword(), equalToHash("TEST")); verify(dataSource).updatePassword(auth2); assertThat(auth2.getPassword(), equalToHash("MYPASSWORD")); verify(dataSource).updatePassword(auth3); assertThat(auth3.getPassword(), equalToHash("$TESTER12_PW")); verifyNoMoreInteractions(dataSource); verify(settings).setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256); } @Test public void shouldNotMigrateShaHashes() { // given PlayerAuth auth1 = authWithNickAndHash("testUser", "abc1234"); PlayerAuth auth2 = authWithNickAndHash("minecraft", "$SHA$f28930ae09823eba4cd98a3"); given(dataSource.getAllAuths()).willReturn(Arrays.asList(auth1, auth2)); setSha256MockToUppercase(sha256); given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.PLAINTEXT); // when MigrationService.changePlainTextToSha256(settings, dataSource, sha256); // then verify(sha256).computeHash(eq("abc1234"), argThat(equalToIgnoringCase("testUser"))); verifyNoMoreInteractions(sha256); verify(dataSource).getAllAuths(); // need to verify this because we use verifyNoMoreInteractions() after verify(dataSource).updatePassword(auth1); assertThat(auth1.getPassword(), equalToHash("ABC1234")); verifyNoMoreInteractions(dataSource); verify(settings).setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256); } @Test public void shouldNotMigrateForHashOtherThanPlaintext() { // given given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.BCRYPT); // when MigrationService.changePlainTextToSha256(settings, dataSource, sha256); // then verify(settings).getProperty(SecuritySettings.PASSWORD_HASH); verifyNoMoreInteractions(settings, dataSource, sha256); } @Test public void shouldHaveHiddenEmptyConstructorOnly() { TestHelper.validateHasOnlyPrivateEmptyConstructor(MigrationService.class); } private static PlayerAuth authWithNickAndHash(String nick, String hash) { return PlayerAuth.builder() .name(nick) .password(hash, null) .build(); } private static void setSha256MockToUppercase(Sha256 sha256) { given(sha256.computeHash(anyString(), anyString())).willAnswer(new Answer() { @Override public HashedPassword answer(InvocationOnMock invocation) { String plainPassword = invocation.getArgument(0); return new HashedPassword(plainPassword.toUpperCase(), null); } }); } }