mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-09-16 17:04:40 +02:00
#850 Add setting specifying which password hashes should be checked
This commit is contained in:
parent
0a9afbe457
commit
bb89a59a8a
@ -1,5 +1,5 @@
|
|||||||
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
|
||||||
<!-- File auto-generated on Sun Oct 30 12:57:15 CET 2016. See docs/config/config.tpl.md -->
|
<!-- File auto-generated on Sun Nov 13 10:33:55 CET 2016. See docs/config/config.tpl.md -->
|
||||||
|
|
||||||
## AuthMe Configuration
|
## AuthMe Configuration
|
||||||
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
|
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
|
||||||
@ -245,10 +245,12 @@ settings:
|
|||||||
passwordHash: 'SHA256'
|
passwordHash: 'SHA256'
|
||||||
# Salt length for the SALTED2MD5 MD5(MD5(password)+salt)
|
# Salt length for the SALTED2MD5 MD5(MD5(password)+salt)
|
||||||
doubleMD5SaltLength: 8
|
doubleMD5SaltLength: 8
|
||||||
# If password checking return false, do we need to check with all
|
# If a password check fails, AuthMe will also try to check with the following hash methods.
|
||||||
# other password algorithm to check an old password?
|
# Use this setting when you change from one hash method to another.
|
||||||
# AuthMe will update the password to the new password hash
|
# AuthMe will update the password to the new hash. Example:
|
||||||
supportOldPasswordHash: false
|
# legacyHashes:
|
||||||
|
# - 'SHA1'
|
||||||
|
legacyHashes: []
|
||||||
# Prevent unsafe passwords from being used; put them in lowercase!
|
# Prevent unsafe passwords from being used; put them in lowercase!
|
||||||
# You should always set 'help' as unsafePassword due to possible conflicts.
|
# You should always set 'help' as unsafePassword due to possible conflicts.
|
||||||
# unsafePasswords:
|
# unsafePasswords:
|
||||||
@ -461,4 +463,4 @@ To change settings on a running server, save your changes to config.yml and use
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 30 12:57:15 CET 2016
|
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Nov 13 10:33:55 CET 2016
|
||||||
|
@ -12,6 +12,7 @@ import org.bukkit.plugin.PluginManager;
|
|||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager class for password-related operations.
|
* Manager class for password-related operations.
|
||||||
@ -31,7 +32,7 @@ public class PasswordSecurity implements Reloadable {
|
|||||||
private Injector injector;
|
private Injector injector;
|
||||||
|
|
||||||
private HashAlgorithm algorithm;
|
private HashAlgorithm algorithm;
|
||||||
private boolean supportOldAlgorithm;
|
private Collection<HashAlgorithm> legacyAlgorithms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load or reload the configuration.
|
* Load or reload the configuration.
|
||||||
@ -40,7 +41,7 @@ public class PasswordSecurity implements Reloadable {
|
|||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
|
this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
|
||||||
this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH);
|
this.legacyAlgorithms = settings.getProperty(SecuritySettings.LEGACY_HASHES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +84,7 @@ public class PasswordSecurity implements Reloadable {
|
|||||||
EncryptionMethod method = initializeEncryptionMethodWithEvent(algorithm, playerName);
|
EncryptionMethod method = initializeEncryptionMethodWithEvent(algorithm, playerName);
|
||||||
String playerLowerCase = playerName.toLowerCase();
|
String playerLowerCase = playerName.toLowerCase();
|
||||||
return methodMatches(method, password, hashedPassword, playerLowerCase)
|
return methodMatches(method, password, hashedPassword, playerLowerCase)
|
||||||
|| supportOldAlgorithm && compareWithAllEncryptionMethods(password, hashedPassword, playerLowerCase);
|
|| compareWithLegacyHashes(password, hashedPassword, playerLowerCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,14 +98,12 @@ public class PasswordSecurity implements Reloadable {
|
|||||||
*
|
*
|
||||||
* @return True if there was a password match with another encryption method, false otherwise
|
* @return True if there was a password match with another encryption method, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean compareWithAllEncryptionMethods(String password, HashedPassword hashedPassword, String playerName) {
|
private boolean compareWithLegacyHashes(String password, HashedPassword hashedPassword, String playerName) {
|
||||||
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
|
for (HashAlgorithm algorithm : legacyAlgorithms) {
|
||||||
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
|
EncryptionMethod method = initializeEncryptionMethod(algorithm);
|
||||||
EncryptionMethod method = initializeEncryptionMethod(algorithm);
|
if (methodMatches(method, password, hashedPassword, playerName)) {
|
||||||
if (methodMatches(method, password, hashedPassword, playerName)) {
|
hashPasswordForNewAlgorithm(password, playerName);
|
||||||
hashPasswordForNewAlgorithm(password, playerName);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
43
src/main/java/fr/xephi/authme/settings/EnumSetProperty.java
Normal file
43
src/main/java/fr/xephi/authme/settings/EnumSetProperty.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package fr.xephi.authme.settings;
|
||||||
|
|
||||||
|
import com.github.authme.configme.properties.Property;
|
||||||
|
import com.github.authme.configme.resource.PropertyResource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property whose value is a set of entries of a given enum.
|
||||||
|
*/
|
||||||
|
// TODO https://github.com/AuthMe/ConfigMe/issues/27: Should be a Property of Set<E> type
|
||||||
|
public class EnumSetProperty<E extends Enum<E>> extends Property<List<E>> {
|
||||||
|
|
||||||
|
private final Class<E> enumClass;
|
||||||
|
|
||||||
|
public EnumSetProperty(Class<E> enumClass, String path, List<E> defaultValue) {
|
||||||
|
super(path, defaultValue);
|
||||||
|
this.enumClass = enumClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<E> getFromResource(PropertyResource resource) {
|
||||||
|
List<?> elements = resource.getList(getPath());
|
||||||
|
if (elements != null) {
|
||||||
|
return elements.stream()
|
||||||
|
.map(val -> toEnum(String.valueOf(val)))
|
||||||
|
.filter(e -> e != null)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private E toEnum(String str) {
|
||||||
|
for (E e : enumClass.getEnumConstants()) {
|
||||||
|
if (str.equalsIgnoreCase(e.name())) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import com.google.common.base.Objects;
|
|||||||
import fr.xephi.authme.ConsoleLogger;
|
import fr.xephi.authme.ConsoleLogger;
|
||||||
import fr.xephi.authme.output.LogLevel;
|
import fr.xephi.authme.output.LogLevel;
|
||||||
import fr.xephi.authme.settings.properties.PluginSettings;
|
import fr.xephi.authme.settings.properties.PluginSettings;
|
||||||
|
import fr.xephi.authme.settings.properties.SecuritySettings;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
@ -49,6 +50,7 @@ public class SettingsMigrationService extends PlainMigrationService {
|
|||||||
| migrateForceSpawnSettings(resource)
|
| migrateForceSpawnSettings(resource)
|
||||||
| changeBooleanSettingToLogLevelProperty(resource)
|
| changeBooleanSettingToLogLevelProperty(resource)
|
||||||
| hasOldHelpHeaderProperty(resource)
|
| hasOldHelpHeaderProperty(resource)
|
||||||
|
| hasSupportOldPasswordProperty(resource)
|
||||||
|| hasDeprecatedProperties(resource);
|
|| hasDeprecatedProperties(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +165,16 @@ public class SettingsMigrationService extends PlainMigrationService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasSupportOldPasswordProperty(PropertyResource resource) {
|
||||||
|
String path = "settings.security.supportOldPasswordHash";
|
||||||
|
if (resource.contains(path)) {
|
||||||
|
ConsoleLogger.warning("Property '" + path + "' is no longer supported. "
|
||||||
|
+ "Use '" + SecuritySettings.LEGACY_HASHES.getPath() + "' instead.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for an old property path and moves it to a new path if present.
|
* Checks for an old property path and moves it to a new path if present.
|
||||||
*
|
*
|
||||||
|
@ -4,7 +4,9 @@ import com.github.authme.configme.Comment;
|
|||||||
import com.github.authme.configme.SettingsHolder;
|
import com.github.authme.configme.SettingsHolder;
|
||||||
import com.github.authme.configme.properties.Property;
|
import com.github.authme.configme.properties.Property;
|
||||||
import fr.xephi.authme.security.HashAlgorithm;
|
import fr.xephi.authme.security.HashAlgorithm;
|
||||||
|
import fr.xephi.authme.settings.EnumSetProperty;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.github.authme.configme.properties.PropertyInitializer.newLowercaseListProperty;
|
import static com.github.authme.configme.properties.PropertyInitializer.newLowercaseListProperty;
|
||||||
@ -74,11 +76,15 @@ public class SecuritySettings implements SettingsHolder {
|
|||||||
public static final Property<Integer> DOUBLE_MD5_SALT_LENGTH =
|
public static final Property<Integer> DOUBLE_MD5_SALT_LENGTH =
|
||||||
newProperty("settings.security.doubleMD5SaltLength", 8);
|
newProperty("settings.security.doubleMD5SaltLength", 8);
|
||||||
|
|
||||||
@Comment({"If password checking return false, do we need to check with all",
|
@Comment({
|
||||||
"other password algorithm to check an old password?",
|
"If a password check fails, AuthMe will also try to check with the following hash methods.",
|
||||||
"AuthMe will update the password to the new password hash"})
|
"Use this setting when you change from one hash method to another.",
|
||||||
public static final Property<Boolean> SUPPORT_OLD_PASSWORD_HASH =
|
"AuthMe will update the password to the new hash. Example:",
|
||||||
newProperty("settings.security.supportOldPasswordHash", false);
|
"legacyHashes:",
|
||||||
|
"- 'SHA1'"
|
||||||
|
})
|
||||||
|
public static final Property<List<HashAlgorithm>> LEGACY_HASHES =
|
||||||
|
new EnumSetProperty<>(HashAlgorithm.class, "settings.security.legacyHashes", Collections.emptyList());
|
||||||
|
|
||||||
@Comment({"Prevent unsafe passwords from being used; put them in lowercase!",
|
@Comment({"Prevent unsafe passwords from being used; put them in lowercase!",
|
||||||
"You should always set 'help' as unsafePassword due to possible conflicts.",
|
"You should always set 'help' as unsafePassword due to possible conflicts.",
|
||||||
|
@ -24,6 +24,10 @@ import org.mockito.invocation.InvocationOnMock;
|
|||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Lists.newArrayList;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.equalToIgnoringCase;
|
import static org.hamcrest.Matchers.equalToIgnoringCase;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
@ -97,7 +101,7 @@ public class PasswordSecurityTest {
|
|||||||
|
|
||||||
given(dataSource.getPassword(playerName)).willReturn(password);
|
given(dataSource.getPassword(playerName)).willReturn(password);
|
||||||
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true);
|
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true);
|
||||||
initSettings(HashAlgorithm.BCRYPT, false);
|
initSettings(HashAlgorithm.BCRYPT);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -120,7 +124,7 @@ public class PasswordSecurityTest {
|
|||||||
|
|
||||||
given(dataSource.getPassword(playerName)).willReturn(password);
|
given(dataSource.getPassword(playerName)).willReturn(password);
|
||||||
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
|
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
|
||||||
initSettings(HashAlgorithm.CUSTOM, false);
|
initSettings(HashAlgorithm.CUSTOM);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -140,7 +144,7 @@ public class PasswordSecurityTest {
|
|||||||
String clearTextPass = "tables";
|
String clearTextPass = "tables";
|
||||||
|
|
||||||
given(dataSource.getPassword(playerName)).willReturn(null);
|
given(dataSource.getPassword(playerName)).willReturn(null);
|
||||||
initSettings(HashAlgorithm.MD5, false);
|
initSettings(HashAlgorithm.MD5);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -168,7 +172,8 @@ public class PasswordSecurityTest {
|
|||||||
given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password);
|
given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password);
|
||||||
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
|
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
|
||||||
given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword);
|
given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword);
|
||||||
initSettings(HashAlgorithm.MD5, true);
|
initSettings(HashAlgorithm.MD5);
|
||||||
|
given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(newArrayList(HashAlgorithm.BCRYPT));
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -193,7 +198,7 @@ public class PasswordSecurityTest {
|
|||||||
String clearTextPass = "someInvalidPassword";
|
String clearTextPass = "someInvalidPassword";
|
||||||
given(dataSource.getPassword(playerName)).willReturn(password);
|
given(dataSource.getPassword(playerName)).willReturn(password);
|
||||||
given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false);
|
given(method.comparePassword(clearTextPass, password, playerName)).willReturn(false);
|
||||||
initSettings(HashAlgorithm.MD5, true);
|
initSettings(HashAlgorithm.MD5);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -212,7 +217,7 @@ public class PasswordSecurityTest {
|
|||||||
String usernameLowerCase = username.toLowerCase();
|
String usernameLowerCase = username.toLowerCase();
|
||||||
HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__");
|
HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__");
|
||||||
given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword);
|
given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword);
|
||||||
initSettings(HashAlgorithm.JOOMLA, true);
|
initSettings(HashAlgorithm.JOOMLA);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -234,7 +239,7 @@ public class PasswordSecurityTest {
|
|||||||
String username = "someone12";
|
String username = "someone12";
|
||||||
HashedPassword hashedPassword = new HashedPassword("~T!est#Hash");
|
HashedPassword hashedPassword = new HashedPassword("~T!est#Hash");
|
||||||
given(method.hasSeparateSalt()).willReturn(true);
|
given(method.hasSeparateSalt()).willReturn(true);
|
||||||
initSettings(HashAlgorithm.XAUTH, false);
|
initSettings(HashAlgorithm.XAUTH);
|
||||||
PasswordSecurity security = newPasswordSecurity();
|
PasswordSecurity security = newPasswordSecurity();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -250,19 +255,20 @@ public class PasswordSecurityTest {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldReloadSettings() {
|
public void shouldReloadSettings() {
|
||||||
// given
|
// given
|
||||||
initSettings(HashAlgorithm.BCRYPT, false);
|
initSettings(HashAlgorithm.BCRYPT);
|
||||||
PasswordSecurity passwordSecurity = newPasswordSecurity();
|
PasswordSecurity passwordSecurity = newPasswordSecurity();
|
||||||
given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.MD5);
|
given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.MD5);
|
||||||
given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(true);
|
List<HashAlgorithm> legacyHashes = newArrayList(HashAlgorithm.CUSTOM, HashAlgorithm.BCRYPT);
|
||||||
|
given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(legacyHashes);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
passwordSecurity.reload();
|
passwordSecurity.reload();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "algorithm"),
|
assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "algorithm"),
|
||||||
equalTo((Object) HashAlgorithm.MD5));
|
equalTo(HashAlgorithm.MD5));
|
||||||
assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "supportOldAlgorithm"),
|
assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "legacyAlgorithms"),
|
||||||
equalTo((Object) Boolean.TRUE));
|
equalTo(legacyHashes));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PasswordSecurity newPasswordSecurity() {
|
private PasswordSecurity newPasswordSecurity() {
|
||||||
@ -275,11 +281,10 @@ public class PasswordSecurityTest {
|
|||||||
return passwordSecurity;
|
return passwordSecurity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSettings(HashAlgorithm algorithm, boolean supportOldPassword) {
|
private void initSettings(HashAlgorithm algorithm) {
|
||||||
given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm);
|
given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm);
|
||||||
given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(supportOldPassword);
|
given(settings.getProperty(SecuritySettings.LEGACY_HASHES)).willReturn(Collections.emptyList());
|
||||||
given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8);
|
given(settings.getProperty(HooksSettings.BCRYPT_LOG2_ROUND)).willReturn(8);
|
||||||
given(settings.getProperty(SecuritySettings.DOUBLE_MD5_SALT_LENGTH)).willReturn(16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user