Merge branch 'master' into reload-supp

This commit is contained in:
DNx5 2016-02-20 17:52:37 +07:00
commit b66c4983ec
11 changed files with 134 additions and 81 deletions

View File

@ -17,7 +17,6 @@ import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.DataSourceType;
@ -43,9 +42,8 @@ import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.OtherAccounts; import fr.xephi.authme.settings.OtherAccounts;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
@ -58,9 +56,9 @@ import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.MigrationService;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.Wrapper;
import net.minelink.ctplus.CombatTagPlus; import net.minelink.ctplus.CombatTagPlus;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -77,6 +75,7 @@ import org.bukkit.scheduler.BukkitTask;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.sql.SQLException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
@ -105,7 +104,6 @@ public class AuthMe extends JavaPlugin {
// Private Instances // Private Instances
private static AuthMe plugin; private static AuthMe plugin;
private static Server server; private static Server server;
private static Wrapper wrapper = Wrapper.getInstance();
private Management management; private Management management;
private CommandHandler commandHandler = null; private CommandHandler commandHandler = null;
private PermissionsManager permsMan = null; private PermissionsManager permsMan = null;
@ -117,9 +115,8 @@ public class AuthMe extends JavaPlugin {
/* /*
* Public Instances * Public Instances
* TODO: Encapsulation * TODO #432: Encapsulation
*/ */
public NewAPI api; public NewAPI api;
public SendMailSSL mail; public SendMailSSL mail;
public DataManager dataManager; public DataManager dataManager;
@ -246,7 +243,7 @@ public class AuthMe extends JavaPlugin {
// Connect to the database and setup tables // Connect to the database and setup tables
try { try {
setupDatabase(); setupDatabase(newSettings);
} catch (Exception e) { } catch (Exception e) {
ConsoleLogger.logException("Fatal error occurred during database connection! " ConsoleLogger.logException("Fatal error occurred during database connection! "
+ "Authme initialization aborted!", e); + "Authme initialization aborted!", e);
@ -254,6 +251,7 @@ public class AuthMe extends JavaPlugin {
return; return;
} }
MigrationService.changePlainTextToSha256(newSettings, database, new SHA256());
passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.PASSWORD_HASH), passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.PASSWORD_HASH),
Bukkit.getPluginManager(), newSettings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)); Bukkit.getPluginManager(), newSettings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH));
@ -516,25 +514,42 @@ public class AuthMe extends JavaPlugin {
} }
} }
public void setupDatabase() throws Exception { /**
if (database != null) * Sets up the data source.
database.close(); *
// Backend MYSQL - FILE - SQLITE - SQLITEHIKARI * @param settings The settings instance
boolean isSQLite = false; * @see AuthMe#database
switch (newSettings.getProperty(DatabaseSettings.BACKEND)) { */
case FILE: public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException {
database = new FlatFile(); if (this.database != null) {
break; this.database.close();
case MYSQL:
database = new MySQL(newSettings);
break;
case SQLITE:
database = new SQLite(newSettings);
isSQLite = true;
break;
} }
if (isSQLite) { DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
dataSource = new FlatFile();
break;
case MYSQL:
dataSource = new MySQL(settings);
break;
case SQLITE:
dataSource = new SQLite(settings);
break;
default:
throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'");
}
DataSource convertedSource = MigrationService.convertFlatfileToSqlite(newSettings, dataSource);
dataSource = convertedSource == null ? dataSource : convertedSource;
if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
database = dataSource;
if (DataSourceType.SQLITE == dataSourceType) {
server.getScheduler().runTaskAsynchronously(this, new Runnable() { server.getScheduler().runTaskAsynchronously(this, new Runnable() {
@Override @Override
public void run() { public void run() {
@ -546,34 +561,6 @@ public class AuthMe extends JavaPlugin {
} }
}); });
} }
if (Settings.getDataSource == DataSourceType.FILE) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated, it will be changed " +
"to SQLite... Connection will be impossible until conversion is done!");
ForceFlatToSqlite converter = new ForceFlatToSqlite(database, newSettings);
DataSource source = converter.run();
if (source != null) {
database = source;
}
}
// TODO: Move this to another place maybe ?
if (HashAlgorithm.PLAINTEXT == newSettings.getProperty(SecuritySettings.PASSWORD_HASH)) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " +
"it will be changed and hashed now to the AuthMe default hashing method");
for (PlayerAuth auth : database.getAllAuths()) {
HashedPassword hashedPassword = passwordSecurity.computeHash(
HashAlgorithm.SHA256, auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(hashedPassword);
database.updatePassword(auth);
}
newSettings.setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256);
newSettings.save();
}
if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) {
database = new CacheDataSource(database);
}
} }
/** /**
@ -916,7 +903,7 @@ public class AuthMe extends JavaPlugin {
String commandLabel, String[] args) { String commandLabel, String[] args) {
// Make sure the command handler has been initialized // Make sure the command handler has been initialized
if (commandHandler == null) { if (commandHandler == null) {
wrapper.getLogger().severe("AuthMe command handler is not available"); getLogger().severe("AuthMe command handler is not available");
return false; return false;
} }

View File

@ -20,7 +20,10 @@ public class ReloadCommand implements ExecutableCommand {
try { try {
commandService.getSettings().reload(); commandService.getSettings().reload();
commandService.reloadMessages(commandService.getSettings().getMessagesFile()); commandService.reloadMessages(commandService.getSettings().getMessagesFile());
plugin.setupDatabase(); // TODO #432: We should not reload only certain plugin entities but actually reinitialize all elements,
// i.e. here in the future we might not have setupDatabase() but Authme.onEnable(), maybe after
// a call to some destructor method
plugin.setupDatabase(commandService.getSettings());
commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting"); sender.sendMessage("Error occurred during reload of AuthMe: aborting");

View File

@ -14,7 +14,6 @@ public interface DataSource {
* Return whether there is a record for the given username. * Return whether there is a record for the given username.
* *
* @param user The username to look up * @param user The username to look up
*
* @return True if there is a record, false otherwise * @return True if there is a record, false otherwise
*/ */
boolean isAuthAvailable(String user); boolean isAuthAvailable(String user);
@ -23,7 +22,6 @@ public interface DataSource {
* Return the hashed password of the player. * Return the hashed password of the player.
* *
* @param user The user whose password should be retrieve * @param user The user whose password should be retrieve
*
* @return The password hash of the player * @return The password hash of the player
*/ */
HashedPassword getPassword(String user); HashedPassword getPassword(String user);
@ -32,7 +30,6 @@ public interface DataSource {
* Retrieve the entire PlayerAuth object associated with the username. * Retrieve the entire PlayerAuth object associated with the username.
* *
* @param user The user to retrieve * @param user The user to retrieve
*
* @return The PlayerAuth object for the given username * @return The PlayerAuth object for the given username
*/ */
PlayerAuth getAuth(String user); PlayerAuth getAuth(String user);
@ -41,7 +38,6 @@ public interface DataSource {
* Save a new PlayerAuth object. * Save a new PlayerAuth object.
* *
* @param auth The new PlayerAuth to persist * @param auth The new PlayerAuth to persist
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean saveAuth(PlayerAuth auth); boolean saveAuth(PlayerAuth auth);
@ -50,7 +46,6 @@ public interface DataSource {
* Update the session of a record (IP, last login, real name). * Update the session of a record (IP, last login, real name).
* *
* @param auth The PlayerAuth object to update in the database * @param auth The PlayerAuth object to update in the database
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateSession(PlayerAuth auth); boolean updateSession(PlayerAuth auth);
@ -59,7 +54,6 @@ public interface DataSource {
* Update the password of the given PlayerAuth object. * Update the password of the given PlayerAuth object.
* *
* @param auth The PlayerAuth whose password should be updated * @param auth The PlayerAuth whose password should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updatePassword(PlayerAuth auth); boolean updatePassword(PlayerAuth auth);
@ -69,7 +63,6 @@ public interface DataSource {
* *
* @param user The user whose password should be updated * @param user The user whose password should be updated
* @param password The new password * @param password The new password
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updatePassword(String user, HashedPassword password); boolean updatePassword(String user, HashedPassword password);
@ -79,7 +72,6 @@ public interface DataSource {
* the given time. * the given time.
* *
* @param until The minimum last login * @param until The minimum last login
*
* @return The account names that have been removed * @return The account names that have been removed
*/ */
List<String> autoPurgeDatabase(long until); List<String> autoPurgeDatabase(long until);
@ -88,7 +80,6 @@ public interface DataSource {
* Remove a user record from the database. * Remove a user record from the database.
* *
* @param user The user to remove * @param user The user to remove
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean removeAuth(String user); boolean removeAuth(String user);
@ -97,7 +88,6 @@ public interface DataSource {
* Update the quit location of a PlayerAuth. * Update the quit location of a PlayerAuth.
* *
* @param auth The entry whose quit location should be updated * @param auth The entry whose quit location should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateQuitLoc(PlayerAuth auth); boolean updateQuitLoc(PlayerAuth auth);
@ -106,7 +96,6 @@ public interface DataSource {
* Return all usernames associated with the given IP address. * Return all usernames associated with the given IP address.
* *
* @param ip The IP address to look up * @param ip The IP address to look up
*
* @return Usernames associated with the given IP address * @return Usernames associated with the given IP address
*/ */
List<String> getAllAuthsByIp(String ip); List<String> getAllAuthsByIp(String ip);
@ -115,7 +104,6 @@ public interface DataSource {
* Return all usernames associated with the given email address. * Return all usernames associated with the given email address.
* *
* @param email The email address to look up * @param email The email address to look up
*
* @return Users using the given email address * @return Users using the given email address
*/ */
List<String> getAllAuthsByEmail(String email); List<String> getAllAuthsByEmail(String email);
@ -124,7 +112,6 @@ public interface DataSource {
* Update the email of the PlayerAuth in the data source. * Update the email of the PlayerAuth in the data source.
* *
* @param auth The PlayerAuth whose email should be updated * @param auth The PlayerAuth whose email should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateEmail(PlayerAuth auth); boolean updateEmail(PlayerAuth auth);

View File

@ -7,6 +7,7 @@ public enum DataSourceType {
MYSQL, MYSQL,
@Deprecated
FILE, FILE,
SQLITE SQLITE

View File

@ -64,16 +64,17 @@ public class AsynchronousJoin {
final String ip = plugin.getIP(player); final String ip = plugin.getIP(player);
if (Settings.isAllowRestrictedIp && !isNameRestricted(name, ip, player.getAddress().getHostName())) { if (Settings.isAllowRestrictedIp && isNameRestricted(name, ip, player.getAddress().getHostName())) {
sched.scheduleSyncDelayedTask(plugin, new Runnable() { sched.scheduleSyncDelayedTask(plugin, new Runnable() {
@Override @Override
public void run() { public void run() {
AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true); AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR)); player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR));
if (Settings.banUnsafeIp) if (Settings.banUnsafeIp) {
plugin.getServer().banIP(ip); plugin.getServer().banIP(ip);
} }
}
}); });
return; return;
} }

View File

@ -220,7 +220,7 @@ public class AsynchronousLogin {
} }
} }
public void displayOtherAccounts(PlayerAuth auth) { private void displayOtherAccounts(PlayerAuth auth) {
if (!Settings.displayOtherAccounts || auth == null) { if (!Settings.displayOtherAccounts || auth == null) {
return; return;
} }

View File

@ -2,6 +2,8 @@ package fr.xephi.authme.process.login;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -92,7 +94,7 @@ public class ProcessSyncPlayerLogin implements Runnable {
} }
private void restoreSpeedEffects() { private void restoreSpeedEffects() {
if (Settings.isRemoveSpeedEnabled) { if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
player.setWalkSpeed(0.2F); player.setWalkSpeed(0.2F);
player.setFlySpeed(0.1F); player.setFlySpeed(0.1F);
} }
@ -173,19 +175,19 @@ public class ProcessSyncPlayerLogin implements Runnable {
} }
restoreSpeedEffects(); restoreSpeedEffects();
if (Settings.applyBlindEffect) { if (settings.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
player.removePotionEffect(PotionEffectType.BLINDNESS); player.removePotionEffect(PotionEffectType.BLINDNESS);
} }
// The Login event now fires (as intended) after everything is processed // The Login event now fires (as intended) after everything is processed
Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player)); Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player));
player.saveData(); player.saveData();
if (Settings.bungee) { if (settings.getProperty(HooksSettings.BUNGEECORD)) {
sendBungeeMessage(); sendBungeeMessage();
} }
// Login is finish, display welcome message if we use email registration // Login is done, display welcome message
if (Settings.useWelcomeMessage && Settings.emailRegistration) { if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) {
if (Settings.broadcastWelcomeMessage) { if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) {
for (String s : settings.getWelcomeMessage()) { for (String s : settings.getWelcomeMessage()) {
Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player));
} }

View File

@ -113,7 +113,7 @@ public class PasswordSecurity {
*/ */
private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) { private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) {
try { try {
return HashAlgorithm.CUSTOM.equals(algorithm) return HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)
? null ? null
: algorithm.getClazz().newInstance(); : algorithm.getClazz().newInstance();
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {

View File

@ -11,7 +11,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
* and store the salt with the hash itself. * and store the salt with the hash itself.
*/ */
@Recommendation(Usage.ACCEPTABLE) @Recommendation(Usage.ACCEPTABLE)
@HasSalt(SaltType.TEXT) // See saltLength() for length @HasSalt(SaltType.TEXT) // See getSaltLength() for length
public abstract class HexSaltedMethod implements EncryptionMethod { public abstract class HexSaltedMethod implements EncryptionMethod {
public abstract int getSaltLength(); public abstract int getSaltLength();

View File

@ -20,7 +20,7 @@ public class BackupSettings implements SettingsClass {
public static final Property<Boolean> ON_SERVER_STOP = public static final Property<Boolean> ON_SERVER_STOP =
newProperty("BackupSystem.OnServerStop", true); newProperty("BackupSystem.OnServerStop", true);
@Comment(" Windows only mysql installation Path") @Comment("Windows only mysql installation Path")
public static final Property<String> MYSQL_WINDOWS_PATH = public static final Property<String> MYSQL_WINDOWS_PATH =
newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");

View File

@ -0,0 +1,72 @@
package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
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.NewSetting;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import java.util.List;
/**
* Migrations to perform during the initialization of AuthMe.
*/
public final class MigrationService {
private MigrationService() {
}
/**
* Hash all passwords to SHA256 and updated the setting if the password hash is set to the deprecated PLAINTEXT.
*
* @param settings The settings instance
* @param dataSource The data source
* @param authmeSha256 Instance to the AuthMe SHA256 encryption method implementation
*/
public static void changePlainTextToSha256(NewSetting settings, DataSource dataSource,
SHA256 authmeSha256) {
if (HashAlgorithm.PLAINTEXT == settings.getProperty(SecuritySettings.PASSWORD_HASH)) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated;"
+ " it will be changed and hashed now to the AuthMe default hashing method");
List<PlayerAuth> allAuths = dataSource.getAllAuths();
for (PlayerAuth auth : allAuths) {
HashedPassword hashedPassword = authmeSha256.computeHash(
auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(hashedPassword);
dataSource.updatePassword(auth);
}
settings.setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256);
settings.save();
ConsoleLogger.info("Migrated " + allAuths.size() + " accounts from plaintext to SHA256");
}
}
/**
* Converts the data source from the deprecated FLATFILE type to SQLITE.
*
* @param settings The settings instance
* @param dataSource The data source
* @return The converted datasource (SQLite), or null if no migration was necessary
*/
public static DataSource convertFlatfileToSqlite(NewSetting settings, DataSource dataSource) {
if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated; it will be changed "
+ "to SQLite... Connection will be impossible until conversion is done!");
ForceFlatToSqlite converter = new ForceFlatToSqlite(dataSource, settings);
DataSource result = converter.run();
if (result == null) {
throw new IllegalStateException("Error during conversion from flatfile to SQLite");
} else {
return result;
}
}
return null;
}
}