diff --git a/docs/hash_algorithms.md b/docs/hash_algorithms.md
index ed1a6702d..45fa65948 100644
--- a/docs/hash_algorithms.md
+++ b/docs/hash_algorithms.md
@@ -1,5 +1,5 @@
-
+
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.
@@ -10,11 +10,9 @@ Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Se
BCRYPT | Recommended | 60 | | | Text | |
BCRYPT2Y | Recommended | 60 | | | Text | 22 |
CRAZYCRYPT1 | Do not use | 128 | | | Username | |
-DOUBLEMD5 | Deprecated | 32 | | | None | |
IPB3 | Acceptable | 32 | | | Text | 5 | Y
IPB4 | Does not work | 60 | | | Text | 22 | Y
JOOMLA | Acceptable | 65 | | | Text | 32 |
-MD5 | Deprecated | 32 | | | None | |
MD5VB | Acceptable | 56 | | | Text | 16 |
MYBB | Acceptable | 32 | | | Text | 8 | Y
PBKDF2 | Recommended | 165 | | | Text | 16 |
@@ -24,14 +22,11 @@ PHPFUSION | Do not use | 64 | Y | | | | Y
ROYALAUTH | Do not use | 128 | | | None | |
SALTED2MD5 | Acceptable | 32 | | | Text | | Y
SALTEDSHA512 | Recommended | 128 | | | | | Y
-SHA1 | Deprecated | 40 | | | None | |
SHA256 | Recommended | 86 | | | Text | 16 |
-SHA512 | Deprecated | 128 | | | None | |
-SMF | Do not use | 40 | | | Username | |
+SMF | Do not use | 40 | | | Username | | Y
TWO_FACTOR | Does not work | 16 | | | None | |
WBB3 | Acceptable | 40 | | | Text | 40 | Y
WBB4 | Recommended | 60 | | | Text | 8 |
-WHIRLPOOL | Deprecated | 128 | | | None | |
WORDPRESS | Acceptable | 34 | | | Text | 9 |
XAUTH | Recommended | 140 | | | Text | 12 |
XFBCRYPT | | 60 | | | | |
@@ -83,4 +78,4 @@ or bad.
---
-This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Sep 17 11:29:07 CEST 2017
+This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Thu Oct 19 21:41:21 CEST 2017
diff --git a/pom.xml b/pom.xml
index 1405a4a36..e44421cc9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -325,7 +325,7 @@
com.google.guava
guava
- 23.0
+ 23.2-jre
compile
true
@@ -360,7 +360,7 @@
com.zaxxer
HikariCP
- 2.7.1
+ 2.7.2
compile
diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java
index 6ce2d103e..728b469b8 100644
--- a/src/main/java/fr/xephi/authme/AuthMe.java
+++ b/src/main/java/fr/xephi/authme/AuthMe.java
@@ -23,7 +23,6 @@ import fr.xephi.authme.listener.PlayerListener18;
import fr.xephi.authme.listener.PlayerListener19;
import fr.xephi.authme.listener.PlayerListener19Spigot;
import fr.xephi.authme.listener.ServerListener;
-import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.Sha256;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BukkitService;
@@ -268,13 +267,6 @@ public class AuthMe extends JavaPlugin {
&& settings.getProperty(EmailSettings.SMTP_PORT) != 25) {
ConsoleLogger.warning("Note: You have set Email.useTls to false but this only affects mail over port 25");
}
-
- // Unsalted hashes will be deprecated in 5.4 (see Github issue #1016)
- HashAlgorithm hash = settings.getProperty(SecuritySettings.PASSWORD_HASH);
- if (OnStartupTasks.isHashDeprecatedIn54(hash)) {
- ConsoleLogger.warning("You are using an unsalted hash (" + hash + "). Support for this will be removed "
- + "in 5.4 -- do you still need it? Comment on https://github.com/AuthMe/AuthMeReloaded/issues/1016");
- }
}
/**
diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/src/main/java/fr/xephi/authme/ConsoleLogger.java
index 9d18820cd..0d9332985 100644
--- a/src/main/java/fr/xephi/authme/ConsoleLogger.java
+++ b/src/main/java/fr/xephi/authme/ConsoleLogger.java
@@ -17,6 +17,7 @@ import java.util.Date;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
/**
* The plugin's static logger.
@@ -168,7 +169,7 @@ public final class ConsoleLogger {
* @param message the message
* @param param1 parameter to replace in the message
*/
- public static void debug(String message, String param1) {
+ public static void debug(String message, Object param1) {
if (logLevel.includes(LogLevel.DEBUG)) {
String debugMessage = "[DEBUG] " + message;
logger.log(Level.INFO, debugMessage, param1);
@@ -184,24 +185,9 @@ public final class ConsoleLogger {
* @param param2 second param to replace in message
*/
// Avoids array creation if DEBUG level is disabled
- public static void debug(String message, String param1, String param2) {
+ public static void debug(String message, Object param1, Object param2) {
if (logLevel.includes(LogLevel.DEBUG)) {
- debug(message, new String[]{param1, param2});
- }
- }
-
- /**
- * Log the DEBUG message.
- *
- * @param message the message
- * @param params the params to replace in the message
- */
- // Equivalent to debug(String, Object...) but avoids conversions
- public static void debug(String message, String... params) {
- if (logLevel.includes(LogLevel.DEBUG)) {
- String debugMessage = "[DEBUG] " + message;
- logger.log(Level.INFO, debugMessage, params);
- writeLog(debugMessage + " {" + String.join(", ", params) + "}");
+ debug(message, new Object[]{param1, param2});
}
}
@@ -213,7 +199,10 @@ public final class ConsoleLogger {
*/
public static void debug(String message, Object... params) {
if (logLevel.includes(LogLevel.DEBUG)) {
- debug(message, Arrays.stream(params).map(String::valueOf).toArray(String[]::new));
+ String debugMessage = "[DEBUG] " + message;
+ logger.log(Level.INFO, debugMessage, params);
+ writeLog(debugMessage + " {"
+ + Arrays.stream(params).map(String::valueOf).collect(Collectors.joining(", ")) + "}");
}
}
diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java
index 45240a1ac..06a5422b6 100644
--- a/src/main/java/fr/xephi/authme/datasource/MySQL.java
+++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java
@@ -583,7 +583,7 @@ public class MySQL implements DataSource {
public boolean hasSession(String user) {
String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
- pst.setString(1, user);
+ pst.setString(1, user.toLowerCase());
try (ResultSet rs = pst.executeQuery()) {
return rs.next() && (rs.getInt(col.HAS_SESSION) == 1);
}
diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java
index 7497858fb..dba210157 100644
--- a/src/main/java/fr/xephi/authme/datasource/SQLite.java
+++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java
@@ -509,7 +509,7 @@ public class SQLite implements DataSource {
public boolean hasSession(String user) {
String sql = "SELECT " + col.HAS_SESSION + " FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
- pst.setString(1, user);
+ pst.setString(1, user.toLowerCase());
try (ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
return rs.getInt(col.HAS_SESSION) == 1;
@@ -526,7 +526,7 @@ public class SQLite implements DataSource {
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, 1);
- pst.setString(2, user);
+ pst.setString(2, user.toLowerCase());
pst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
@@ -538,7 +538,7 @@ public class SQLite implements DataSource {
String sql = "UPDATE " + tableName + " SET " + col.HAS_SESSION + "=? WHERE LOWER(" + col.NAME + ")=?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, 0);
- pst.setString(2, user);
+ pst.setString(2, user.toLowerCase());
pst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
diff --git a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java
index e9ae353b4..77d18264a 100644
--- a/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java
+++ b/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java
@@ -7,9 +7,6 @@ import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
-import fr.xephi.authme.security.HashAlgorithm;
-import fr.xephi.authme.security.crypts.description.Recommendation;
-import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
@@ -103,23 +100,4 @@ public class OnStartupTasks {
}
}, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
}
-
- /**
- * Returns whether the hash algorithm is deprecated and won't be able
- * to be actively used anymore in 5.4.
- *
- * @param hash the hash algorithm to check
- * @return true if the hash will be deprecated, false otherwise
- * @see #1016
- */
- public static boolean isHashDeprecatedIn54(HashAlgorithm hash) {
- if (hash.getClazz() == null || hash == HashAlgorithm.PLAINTEXT) {
- // Exclude PLAINTEXT from this check because it already has a mandatory migration, which takes care of
- // sending all the necessary messages and warnings.
- return false;
- }
-
- Recommendation recommendation = hash.getClazz().getAnnotation(Recommendation.class);
- return recommendation != null && recommendation.value() == Usage.DEPRECATED;
- }
}
diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
index f12da678d..5ce3e92a5 100644
--- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
+++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
@@ -10,34 +10,34 @@ public enum HashAlgorithm {
BCRYPT(fr.xephi.authme.security.crypts.BCrypt.class),
BCRYPT2Y(fr.xephi.authme.security.crypts.BCrypt2y.class),
CRAZYCRYPT1(fr.xephi.authme.security.crypts.CrazyCrypt1.class),
- DOUBLEMD5(fr.xephi.authme.security.crypts.DoubleMd5.class),
IPB3(fr.xephi.authme.security.crypts.Ipb3.class),
IPB4(fr.xephi.authme.security.crypts.Ipb4.class),
JOOMLA(fr.xephi.authme.security.crypts.Joomla.class),
- MD5(fr.xephi.authme.security.crypts.Md5.class),
MD5VB(fr.xephi.authme.security.crypts.Md5vB.class),
MYBB(fr.xephi.authme.security.crypts.MyBB.class),
PBKDF2(fr.xephi.authme.security.crypts.Pbkdf2.class),
PBKDF2DJANGO(fr.xephi.authme.security.crypts.Pbkdf2Django.class),
PHPBB(fr.xephi.authme.security.crypts.PhpBB.class),
PHPFUSION(fr.xephi.authme.security.crypts.PhpFusion.class),
- @Deprecated
- PLAINTEXT(fr.xephi.authme.security.crypts.PlainText.class),
ROYALAUTH(fr.xephi.authme.security.crypts.RoyalAuth.class),
SALTED2MD5(fr.xephi.authme.security.crypts.Salted2Md5.class),
SALTEDSHA512(fr.xephi.authme.security.crypts.SaltedSha512.class),
- SHA1(fr.xephi.authme.security.crypts.Sha1.class),
SHA256(fr.xephi.authme.security.crypts.Sha256.class),
- SHA512(fr.xephi.authme.security.crypts.Sha512.class),
SMF(fr.xephi.authme.security.crypts.Smf.class),
TWO_FACTOR(fr.xephi.authme.security.crypts.TwoFactor.class),
WBB3(fr.xephi.authme.security.crypts.Wbb3.class),
WBB4(fr.xephi.authme.security.crypts.Wbb4.class),
- WHIRLPOOL(fr.xephi.authme.security.crypts.Whirlpool.class),
WORDPRESS(fr.xephi.authme.security.crypts.Wordpress.class),
XAUTH(fr.xephi.authme.security.crypts.XAuth.class),
XFBCRYPT(fr.xephi.authme.security.crypts.XfBCrypt.class),
- CUSTOM(null);
+ CUSTOM(null),
+
+ @Deprecated DOUBLEMD5(fr.xephi.authme.security.crypts.DoubleMd5.class),
+ @Deprecated MD5(fr.xephi.authme.security.crypts.Md5.class),
+ @Deprecated PLAINTEXT(fr.xephi.authme.security.crypts.PlainText.class),
+ @Deprecated SHA1(fr.xephi.authme.security.crypts.Sha1.class),
+ @Deprecated SHA512(fr.xephi.authme.security.crypts.Sha512.class),
+ @Deprecated WHIRLPOOL(fr.xephi.authme.security.crypts.Whirlpool.class);
private final Class extends EncryptionMethod> clazz;
diff --git a/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java b/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java
index 54158419b..12e968082 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/DoubleMd5.java
@@ -5,6 +5,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.md5;
+@Deprecated
@Recommendation(Usage.DEPRECATED)
public class DoubleMd5 extends UnsaltedMethod {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/Md5.java b/src/main/java/fr/xephi/authme/security/crypts/Md5.java
index a5f6d91d2..55e07a173 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/Md5.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/Md5.java
@@ -4,6 +4,7 @@ import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
+@Deprecated
@Recommendation(Usage.DEPRECATED)
public class Md5 extends UnsaltedMethod {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha1.java b/src/main/java/fr/xephi/authme/security/crypts/Sha1.java
index 3430752b5..6d095b4a2 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/Sha1.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/Sha1.java
@@ -4,6 +4,7 @@ import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
+@Deprecated
@Recommendation(Usage.DEPRECATED)
public class Sha1 extends UnsaltedMethod {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/Sha512.java b/src/main/java/fr/xephi/authme/security/crypts/Sha512.java
index 4e9682ffe..36dc5a342 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/Sha512.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/Sha512.java
@@ -4,6 +4,7 @@ import fr.xephi.authme.security.HashUtils;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.Usage;
+@Deprecated
@Recommendation(Usage.DEPRECATED)
public class Sha512 extends UnsaltedMethod {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/Whirlpool.java b/src/main/java/fr/xephi/authme/security/crypts/Whirlpool.java
index c1ea747a9..7c62b2619 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/Whirlpool.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/Whirlpool.java
@@ -64,6 +64,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
import java.util.Arrays;
+@Deprecated
@Recommendation(Usage.DEPRECATED)
public class Whirlpool extends UnsaltedMethod {
diff --git a/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java b/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java
index 901c9fc1e..b18944aac 100644
--- a/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java
+++ b/src/main/java/fr/xephi/authme/settings/EnumSetProperty.java
@@ -3,8 +3,8 @@ package fr.xephi.authme.settings;
import ch.jalu.configme.properties.Property;
import ch.jalu.configme.resource.PropertyResource;
+import java.util.Collection;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -27,9 +27,9 @@ public class EnumSetProperty> extends Property> {
@Override
protected Set getFromResource(PropertyResource resource) {
- List> elements = resource.getList(getPath());
- if (elements != null) {
- return elements.stream()
+ Object entry = resource.getObject(getPath());
+ if (entry instanceof Collection>) {
+ return ((Collection>) entry).stream()
.map(val -> toEnum(String.valueOf(val)))
.filter(e -> e != null)
.collect(Collectors.toCollection(LinkedHashSet::new));
diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java
index c2dd46558..9be278807 100644
--- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java
+++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java
@@ -10,6 +10,7 @@ import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
import fr.xephi.authme.process.register.RegistrationType;
+import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
@@ -20,6 +21,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import static ch.jalu.configme.properties.PropertyInitializer.newListProperty;
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
@@ -73,6 +75,7 @@ public class SettingsMigrationService extends PlainMigrationService {
| hasSupportOldPasswordProperty(resource)
| convertToRegistrationType(resource)
| mergeAndMovePermissionGroupSettings(resource)
+ | moveDeprecatedHashAlgorithmIntoLegacySection(resource)
|| hasDeprecatedProperties(resource);
}
@@ -286,6 +289,31 @@ public class SettingsMigrationService extends PlainMigrationService {
return performedChanges;
}
+ /**
+ * If a deprecated hash is used, it is added to the legacy hashes option and the active hash
+ * is changed to SHA256.
+ *
+ * @param resource The property resource
+ * @return True if the configuration has changed, false otherwise
+ */
+ private static boolean moveDeprecatedHashAlgorithmIntoLegacySection(PropertyResource resource) {
+ HashAlgorithm currentHash = SecuritySettings.PASSWORD_HASH.getValue(resource);
+ // Skip CUSTOM (has no class) and PLAINTEXT (is force-migrated later on in the startup process)
+ if (currentHash != HashAlgorithm.CUSTOM && currentHash != HashAlgorithm.PLAINTEXT) {
+ Class> encryptionClass = currentHash.getClazz();
+ if (encryptionClass.isAnnotationPresent(Deprecated.class)) {
+ resource.setValue(SecuritySettings.PASSWORD_HASH.getPath(), HashAlgorithm.SHA256);
+ Set legacyHashes = SecuritySettings.LEGACY_HASHES.getValue(resource);
+ legacyHashes.add(currentHash);
+ resource.setValue(SecuritySettings.LEGACY_HASHES.getPath(), legacyHashes);
+ ConsoleLogger.warning("The hash algorithm '" + currentHash
+ + "' is no longer supported for active use. New hashes will be in SHA256.");
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Checks for an old property path and moves it to a new path if it is present and the new path is not yet set.
*
diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
index 07d0eaf96..0a32c5584 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
@@ -53,7 +53,7 @@ public final class SecuritySettings implements SettingsHolder {
newProperty("settings.security.passwordMaxLength", 30);
@Comment({
- "Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512, WHIRLPOOL,",
+ "Possible values: SHA256, BCRYPT, BCRYPT2Y, PBKDF2, SALTEDSHA512,",
"MYBB, IPB3, PHPBB, PHPFUSION, SMF, XENFORO, XAUTH, JOOMLA, WBB3, WBB4, MD5VB,",
"PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only). See full list at",
"https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/hash_algorithms.md"
diff --git a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java b/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java
index 7d01a7db1..b8deb9349 100644
--- a/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java
+++ b/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java
@@ -19,6 +19,7 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
+import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -142,25 +143,22 @@ public class ConsoleLoggerTest {
ConsoleLogger.setLoggingOptions(newSettings(true, LogLevel.DEBUG));
// when
- ConsoleLogger.debug("Got {0} entries", "test");
- ConsoleLogger.debug("Player `{0}` is in world `{1}`", "Bobby", "world");
- ConsoleLogger.debug("The {0} is {1} the {2}", "fox", "behind", "chicken");
+ ConsoleLogger.debug("Got {0} entries", 17);
+ ConsoleLogger.debug("Player `{0}` is in world `{1}`", "Bobby", new World("world"));
ConsoleLogger.debug("{0} quick {1} jump over {2} lazy {3} (reason: {4})", 5, "foxes", 3, "dogs", null);
ConsoleLogger.debug(() -> "Too little too late");
// then
- verify(logger).log(Level.INFO, "[DEBUG] Got {0} entries", "test");
- verify(logger).log(Level.INFO, "[DEBUG] Player `{0}` is in world `{1}`", new Object[]{"Bobby", "world"});
- verify(logger).log(Level.INFO, "[DEBUG] The {0} is {1} the {2}", new Object[]{"fox", "behind", "chicken"});
+ verify(logger).log(Level.INFO, "[DEBUG] Got {0} entries", 17);
+ verify(logger).log(Level.INFO, "[DEBUG] Player `{0}` is in world `{1}`", new Object[]{"Bobby", new World("world")});
verify(logger).log(Level.INFO, "[DEBUG] {0} quick {1} jump over {2} lazy {3} (reason: {4})",
- new Object[]{"5", "foxes", "3", "dogs", "null"});
+ new Object[]{5, "foxes", 3, "dogs", null});
verify(logger).info("[DEBUG] Too little too late");
List loggedLines = Files.readAllLines(logFile.toPath(), StandardCharsets.UTF_8);
assertThat(loggedLines, contains(
- containsString("[DEBUG] Got {0} entries {test}"),
- containsString("[DEBUG] Player `{0}` is in world `{1}` {Bobby, world}"),
- containsString("[DEBUG] The {0} is {1} the {2} {fox, behind, chicken}"),
+ containsString("[DEBUG] Got {0} entries {17}"),
+ containsString("[DEBUG] Player `{0}` is in world `{1}` {Bobby, w[world]}"),
containsString("[DEBUG] {0} quick {1} jump over {2} lazy {3} (reason: {4}) {5, foxes, 3, dogs, null}"),
containsString("[DEBUG] Too little too late")));
}
@@ -176,4 +174,25 @@ public class ConsoleLoggerTest {
given(settings.getProperty(PluginSettings.LOG_LEVEL)).willReturn(logLevel);
return settings;
}
+
+ private static final class World {
+ private final String name;
+
+ World(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "w[" + name + "]";
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof World) {
+ return Objects.equals(this.name, ((World) other).name);
+ }
+ return false;
+ }
+ }
}
diff --git a/src/test/java/fr/xephi/authme/initialization/OnStartupTasksTest.java b/src/test/java/fr/xephi/authme/initialization/OnStartupTasksTest.java
deleted file mode 100644
index e3febf41f..000000000
--- a/src/test/java/fr/xephi/authme/initialization/OnStartupTasksTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package fr.xephi.authme.initialization;
-
-import fr.xephi.authme.security.HashAlgorithm;
-import org.junit.Test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertThat;
-
-/**
- * Test for {@link OnStartupTasks}.
- */
-public class OnStartupTasksTest {
-
- @Test
- public void shouldCheckIfHashIsDeprecatedIn54() {
- // given / when / then
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.CUSTOM), equalTo(false));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.IPB3), equalTo(false));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.PLAINTEXT), equalTo(false));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.SHA256), equalTo(false));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.WORDPRESS), equalTo(false));
-
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.MD5), equalTo(true));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.SHA512), equalTo(true));
- assertThat(OnStartupTasks.isHashDeprecatedIn54(HashAlgorithm.WHIRLPOOL), equalTo(true));
- }
-}
diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
index 4f3704175..bba4f968a 100644
--- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
@@ -4,6 +4,8 @@ import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.security.crypts.description.Recommendation;
+import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
@@ -12,6 +14,8 @@ import org.junit.BeforeClass;
import org.junit.Test;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo;
@@ -70,4 +74,29 @@ public class HashAlgorithmIntegrationTest {
}
}
+ @Test
+ public void shouldBeDeprecatedIfEncryptionClassIsDeprecated() throws NoSuchFieldException {
+ // given
+ List failedEntries = new LinkedList<>();
+
+ // when
+ for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) {
+ if (hashAlgorithm != HashAlgorithm.CUSTOM) {
+ boolean isEnumDeprecated = HashAlgorithm.class.getDeclaredField(hashAlgorithm.name())
+ .isAnnotationPresent(Deprecated.class);
+ boolean isDeprecatedClass = hashAlgorithm.getClazz().isAnnotationPresent(Deprecated.class);
+ Recommendation recommendation = hashAlgorithm.getClazz().getAnnotation(Recommendation.class);
+ boolean hasDeprecatedUsage = recommendation != null && recommendation.value() == Usage.DEPRECATED;
+ if (isEnumDeprecated != isDeprecatedClass || isEnumDeprecated != hasDeprecatedUsage) {
+ failedEntries.add(hashAlgorithm + ": enum @Deprecated = " + isEnumDeprecated
+ + ", @Deprecated class = " + isDeprecatedClass + ", usage Deprecated = " + hasDeprecatedUsage);
+ }
+ }
+ }
+
+ // then
+ if (!failedEntries.isEmpty()) {
+ fail("Found inconsistencies:\n" + String.join("\n", failedEntries));
+ }
+ }
}
diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java
index da7c270be..0a952bb0b 100644
--- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java
+++ b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java
@@ -1,12 +1,16 @@
package fr.xephi.authme.settings;
+import ch.jalu.configme.configurationdata.ConfigurationData;
+import ch.jalu.configme.properties.Property;
import ch.jalu.configme.resource.PropertyResource;
import ch.jalu.configme.resource.YamlFileResource;
import com.google.common.io.Files;
import fr.xephi.authme.TestHelper;
+import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
import fr.xephi.authme.process.register.RegistrationType;
+import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -16,6 +20,8 @@ import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
import static fr.xephi.authme.TestHelper.getJarFile;
import static fr.xephi.authme.settings.properties.PluginSettings.ENABLE_PERMISSION_CHECK;
@@ -28,6 +34,8 @@ import static fr.xephi.authme.settings.properties.RegistrationSettings.REGISTRAT
import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS;
import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_LOCATION_AFTER_LOGIN;
import static fr.xephi.authme.settings.properties.RestrictionSettings.FORCE_SPAWN_ON_WORLDS;
+import static fr.xephi.authme.settings.properties.SecuritySettings.LEGACY_HASHES;
+import static fr.xephi.authme.settings.properties.SecuritySettings.PASSWORD_HASH;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
@@ -47,8 +55,9 @@ public class SettingsMigrationServiceTest {
TestHelper.setupLogger();
}
+ /* When settings are loaded, test that migrations are applied and immediately available in memory. */
@Test
- public void shouldPerformMigrations() throws IOException {
+ public void shouldPerformMigrationsInMemory() throws IOException {
// given
File dataFolder = temporaryFolder.newFolder();
File configFile = new File(dataFolder, "config.yml");
@@ -61,22 +70,31 @@ public class SettingsMigrationServiceTest {
dataFolder, resource, migrationService, AuthMeSettingsRetriever.buildConfigurationData());
// then
- assertThat(settings.getProperty(ALLOWED_NICKNAME_CHARACTERS), equalTo(ALLOWED_NICKNAME_CHARACTERS.getDefaultValue()));
- assertThat(settings.getProperty(DELAY_JOIN_MESSAGE), equalTo(true));
- assertThat(settings.getProperty(FORCE_SPAWN_LOCATION_AFTER_LOGIN), equalTo(true));
- assertThat(settings.getProperty(FORCE_SPAWN_ON_WORLDS), contains("survival", "survival_nether", "creative"));
- assertThat(settings.getProperty(LOG_LEVEL), equalTo(LogLevel.INFO));
- assertThat(settings.getProperty(REGISTRATION_TYPE), equalTo(RegistrationType.EMAIL));
- assertThat(settings.getProperty(REGISTER_SECOND_ARGUMENT), equalTo(RegisterSecondaryArgument.CONFIRMATION));
- assertThat(settings.getProperty(ENABLE_PERMISSION_CHECK), equalTo(true));
- assertThat(settings.getProperty(REGISTERED_GROUP), equalTo("unLoggedinGroup"));
- assertThat(settings.getProperty(UNREGISTERED_GROUP), equalTo(""));
+ verifyHasUpToDateSettings(settings, dataFolder);
+ }
- // Check migration of old setting to email.html
- assertThat(Files.readLines(new File(dataFolder, "email.html"), StandardCharsets.UTF_8),
- contains("Dear ,
This is your new AuthMe password for the server "
- + "
:
Do not forget to "
- + "change password after login!
/changepassword newPassword"));
+ /*
+ * When settings are loaded, test that migrations are applied and persisted to disk,
+ * i.e. when the settings are loaded again from the file, no migrations should be necessary.
+ */
+ @Test
+ public void shouldPerformMigrationsAndPersistToDisk() throws IOException {
+ // given
+ File dataFolder = temporaryFolder.newFolder();
+ File configFile = new File(dataFolder, "config.yml");
+ Files.copy(getJarFile(OLD_CONFIG_FILE), configFile);
+ PropertyResource resource = new YamlFileResource(configFile);
+ TestMigrationServiceExtension migrationService = new TestMigrationServiceExtension(dataFolder);
+ ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
+
+ // when
+ new Settings(dataFolder, resource, migrationService, configurationData);
+ resource = new YamlFileResource(configFile);
+ Settings settings = new Settings(dataFolder, resource, migrationService, configurationData);
+
+ // then
+ verifyHasUpToDateSettings(settings, dataFolder);
+ assertThat(migrationService.returnedValues, contains(true, false));
}
@Test
@@ -97,4 +115,40 @@ public class SettingsMigrationServiceTest {
assertThat(migrationService.getOnRegisterCommands(), contains("me registers", "msg CONSOLE hi"));
assertThat(migrationService.getOnRegisterConsoleCommands(), contains("sethome %p:regloc"));
}
+
+ private void verifyHasUpToDateSettings(Settings settings, File dataFolder) throws IOException {
+ assertThat(settings.getProperty(ALLOWED_NICKNAME_CHARACTERS), equalTo(ALLOWED_NICKNAME_CHARACTERS.getDefaultValue()));
+ assertThat(settings.getProperty(DELAY_JOIN_MESSAGE), equalTo(true));
+ assertThat(settings.getProperty(FORCE_SPAWN_LOCATION_AFTER_LOGIN), equalTo(true));
+ assertThat(settings.getProperty(FORCE_SPAWN_ON_WORLDS), contains("survival", "survival_nether", "creative"));
+ assertThat(settings.getProperty(LOG_LEVEL), equalTo(LogLevel.INFO));
+ assertThat(settings.getProperty(REGISTRATION_TYPE), equalTo(RegistrationType.EMAIL));
+ assertThat(settings.getProperty(REGISTER_SECOND_ARGUMENT), equalTo(RegisterSecondaryArgument.CONFIRMATION));
+ assertThat(settings.getProperty(ENABLE_PERMISSION_CHECK), equalTo(true));
+ assertThat(settings.getProperty(REGISTERED_GROUP), equalTo("unLoggedinGroup"));
+ assertThat(settings.getProperty(UNREGISTERED_GROUP), equalTo(""));
+ assertThat(settings.getProperty(PASSWORD_HASH), equalTo(HashAlgorithm.SHA256));
+ assertThat(settings.getProperty(LEGACY_HASHES), contains(HashAlgorithm.PBKDF2, HashAlgorithm.WORDPRESS, HashAlgorithm.SHA512));
+
+ // Check migration of old setting to email.html
+ assertThat(Files.readLines(new File(dataFolder, "email.html"), StandardCharsets.UTF_8),
+ contains("Dear ,
This is your new AuthMe password for the server "
+ + "
:
Do not forget to "
+ + "change password after login!
/changepassword newPassword"));
+ }
+
+ private static class TestMigrationServiceExtension extends SettingsMigrationService {
+ private List returnedValues = new ArrayList<>();
+
+ TestMigrationServiceExtension(@DataFolder File pluginFolder) {
+ super(pluginFolder);
+ }
+
+ @Override
+ protected boolean performMigrations(PropertyResource resource, List> properties) {
+ boolean result = super.performMigrations(resource, properties);
+ returnedValues.add(result);
+ return result;
+ }
+ }
}
diff --git a/src/test/resources/fr/xephi/authme/settings/config-old.yml b/src/test/resources/fr/xephi/authme/settings/config-old.yml
index 65e2614ce..0d2e425c5 100644
--- a/src/test/resources/fr/xephi/authme/settings/config-old.yml
+++ b/src/test/resources/fr/xephi/authme/settings/config-old.yml
@@ -191,7 +191,10 @@ settings:
# PLAINTEXT ( unhashed password),
# MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only)
- passwordHash: SHA256
+ passwordHash: SHA512
+ legacyHashes:
+ - PBKDF2
+ - WORDPRESS
# salt length for the SALTED2MD5 MD5(MD5(password)+salt)
doubleMD5SaltLength: 8
# If password checking return false , do we need to check with all