diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index c5a26048c..73dfd4bd7 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -13,6 +13,7 @@ import java.util.logging.Logger; import com.google.common.base.Charsets; import com.google.common.io.Resources; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.settings.SettingsMigrationService; import org.apache.logging.log4j.LogManager; @@ -534,7 +535,7 @@ public class AuthMe extends JavaPlugin { }); } - if (Settings.getDataSource == DataSource.DataSourceType.FILE) { + 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); diff --git a/src/main/java/fr/xephi/authme/PerformBackup.java b/src/main/java/fr/xephi/authme/PerformBackup.java index 36b75d7d1..677ade986 100644 --- a/src/main/java/fr/xephi/authme/PerformBackup.java +++ b/src/main/java/fr/xephi/authme/PerformBackup.java @@ -1,6 +1,6 @@ package fr.xephi.authme; -import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.BackupSettings; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -80,7 +80,7 @@ public class PerformBackup { } public boolean doBackup() { - DataSource.DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); + DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); switch (dataSourceType) { case FILE: return fileBackup("auths.db"); @@ -112,14 +112,10 @@ public class PerformBackup { ConsoleLogger.info("Backup created successfully."); return true; } else { - ConsoleLogger.showError("Could not create the backup!"); + ConsoleLogger.showError("Could not create the backup! (Windows)"); } - } catch (IOException e) { - ConsoleLogger.showError("Error during backup: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - } catch (InterruptedException e) { - ConsoleLogger.showError("Backup was interrupted: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); + } catch (IOException | InterruptedException e) { + ConsoleLogger.logException("Error during Windows backup:", e); } } else { String executeCmd = "mysqldump -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; @@ -133,12 +129,8 @@ public class PerformBackup { } else { ConsoleLogger.showError("Could not create the backup!"); } - } catch (IOException e) { - ConsoleLogger.showError("Error during backup: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - } catch (InterruptedException e) { - ConsoleLogger.showError("Backup was interrupted: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); + } catch (IOException | InterruptedException e) { + ConsoleLogger.logException("Error during backup:", e); } } return false; diff --git a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java index d873aa506..0b0dc2580 100644 --- a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java +++ b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java @@ -3,6 +3,7 @@ package fr.xephi.authme.converter; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.DatabaseSettings; @@ -30,7 +31,7 @@ public class ForceFlatToSqlite { auth.setRealName("Player"); sqlite.saveAuth(auth); } - settings.setProperty(DatabaseSettings.BACKEND, DataSource.DataSourceType.SQLITE); + settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE); settings.save(); ConsoleLogger.info("Database successfully converted to sqlite!"); return sqlite; diff --git a/src/main/java/fr/xephi/authme/converter/SqliteToSql.java b/src/main/java/fr/xephi/authme/converter/SqliteToSql.java index 37df161a3..821875e09 100644 --- a/src/main/java/fr/xephi/authme/converter/SqliteToSql.java +++ b/src/main/java/fr/xephi/authme/converter/SqliteToSql.java @@ -5,7 +5,7 @@ import org.bukkit.command.CommandSender; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource.DataSourceType; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.output.MessageKey; diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 2d7dc76f8..e1ece18aa 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -41,6 +41,7 @@ public class CacheDataSource implements DataSource { }) .build( new CacheLoader>() { + @Override public Optional load(String key) { return Optional.fromNullable(source.getAuth(key)); } @@ -62,15 +63,6 @@ public class CacheDataSource implements DataSource { return source.getPassword(user); } - /** - * Method getAuth. - * - * @param user String - * - * @return PlayerAuth - * - * @see fr.xephi.authme.datasource.DataSource#getAuth(String) - */ @Override public synchronized PlayerAuth getAuth(String user) { user = user.toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index b59e1daa5..a0077ce5a 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -220,9 +220,4 @@ public interface DataSource { boolean isEmailStored(String email); - enum DataSourceType { - MYSQL, - FILE, - SQLITE - } } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSourceType.java b/src/main/java/fr/xephi/authme/datasource/DataSourceType.java new file mode 100644 index 000000000..edb72ac8c --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/DataSourceType.java @@ -0,0 +1,14 @@ +package fr.xephi.authme.datasource; + +/** + * DataSource type. + */ +public enum DataSourceType { + + MYSQL, + + FILE, + + SQLITE + +} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index a97974fb8..6cd0128c3 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -18,6 +18,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; +import java.sql.Types; import java.util.ArrayList; import java.util.List; @@ -149,7 +151,7 @@ public class MySQL implements DataSource { + columnRealName + " VARCHAR(255) NOT NULL," + columnPassword + " VARCHAR(255) NOT NULL," + columnIp + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1'," - + columnLastLogin + " BIGINT NOT NULL DEFAULT '" + System.currentTimeMillis() + "'," + + columnLastLogin + " TIMESTAMP NOT NULL DEFAULT current_timestamp," + lastlocX + " DOUBLE NOT NULL DEFAULT '0.0'," + lastlocY + " DOUBLE NOT NULL DEFAULT '0.0'," + lastlocZ + " DOUBLE NOT NULL DEFAULT '0.0'," @@ -200,7 +202,9 @@ public class MySQL implements DataSource { rs = md.getColumns(null, null, tableName, columnLastLogin); if (!rs.next()) { st.executeUpdate("ALTER TABLE " + tableName - + " ADD COLUMN " + columnLastLogin + " BIGINT;"); + + " ADD COLUMN " + columnLastLogin + " TIMESTAMP NOT NULL DEFAULT current_timestamp;"); + } else { + migrateLastLoginColumnToTimestamp(con, rs); } rs.close(); @@ -245,7 +249,7 @@ public class MySQL implements DataSource { st.close(); } - ConsoleLogger.info("MySQL Setup finished"); + ConsoleLogger.info("MySQL setup finished"); } @Override @@ -291,22 +295,8 @@ public class MySQL implements DataSource { if (!rs.next()) { return null; } - String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null; - int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1; int id = rs.getInt(columnID); - pAuth = PlayerAuth.builder() - .name(rs.getString(columnName)) - .realName(rs.getString(columnRealName)) - .password(rs.getString(columnPassword), salt) - .lastLogin(rs.getLong(columnLastLogin)) - .ip(rs.getString(columnIp)) - .locWorld(rs.getString(lastlocWorld)) - .locX(rs.getDouble(lastlocX)) - .locY(rs.getDouble(lastlocY)) - .locZ(rs.getDouble(lastlocZ)) - .email(rs.getString(columnEmail)) - .groupId(group) - .build(); + pAuth = buildAuthFromResultSet(rs); rs.close(); pst.close(); if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) { @@ -344,7 +334,7 @@ public class MySQL implements DataSource { pst.setString(1, auth.getNickname()); pst.setString(2, auth.getPassword().getHash()); pst.setString(3, auth.getIp()); - pst.setLong(4, auth.getLastLogin()); + pst.setTimestamp(4, new Timestamp(auth.getLastLogin())); pst.setString(5, auth.getRealName()); pst.setString(6, auth.getEmail()); if (useSalt) { @@ -923,22 +913,7 @@ public class MySQL implements DataSource { ResultSet rs = st.executeQuery("SELECT * FROM " + tableName); PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;"); while (rs.next()) { - String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null; - int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1; - PlayerAuth pAuth = PlayerAuth.builder() - .name(rs.getString(columnName)) - .realName(rs.getString(columnRealName)) - .password(rs.getString(columnPassword), salt) - .lastLogin(rs.getLong(columnLastLogin)) - .ip(rs.getString(columnIp)) - .locWorld(rs.getString(lastlocWorld)) - .locX(rs.getDouble(lastlocX)) - .locY(rs.getDouble(lastlocY)) - .locZ(rs.getDouble(lastlocZ)) - .email(rs.getString(columnEmail)) - .groupId(group) - .build(); - + PlayerAuth pAuth = buildAuthFromResultSet(rs); if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) { int id = rs.getInt(columnID); pst.setInt(1, id); @@ -969,22 +944,7 @@ public class MySQL implements DataSource { ResultSet rs = st.executeQuery("SELECT * FROM " + tableName + " WHERE " + columnLogged + "=1;"); PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;"); while (rs.next()) { - String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null; - int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1; - PlayerAuth pAuth = PlayerAuth.builder() - .name(rs.getString(columnName)) - .realName(rs.getString(columnRealName)) - .password(rs.getString(columnPassword), salt) - .lastLogin(rs.getLong(columnLastLogin)) - .ip(rs.getString(columnIp)) - .locWorld(rs.getString(lastlocWorld)) - .locX(rs.getDouble(lastlocX)) - .locY(rs.getDouble(lastlocY)) - .locZ(rs.getDouble(lastlocZ)) - .email(rs.getString(columnEmail)) - .groupId(group) - .build(); - + PlayerAuth pAuth = buildAuthFromResultSet(rs); if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) { int id = rs.getInt(columnID); pst.setInt(1, id); @@ -1018,6 +978,55 @@ public class MySQL implements DataSource { return false; } + private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { + String salt = columnSalt.isEmpty() ? null : row.getString(columnSalt); + int group = columnGroup.isEmpty() ? -1 : row.getInt(columnGroup); + return PlayerAuth.builder() + .name(row.getString(columnName)) + .realName(row.getString(columnRealName)) + .password(row.getString(columnPassword), salt) + .lastLogin(row.getTimestamp(columnLastLogin).getTime()) + .ip(row.getString(columnIp)) + .locWorld(row.getString(lastlocWorld)) + .locX(row.getDouble(lastlocX)) + .locY(row.getDouble(lastlocY)) + .locZ(row.getDouble(lastlocZ)) + .email(row.getString(columnEmail)) + .groupId(group) + .build(); + } + + private void migrateLastLoginColumnToTimestamp(Connection con, ResultSet rs) throws SQLException { + final int columnType = rs.getInt("DATA_TYPE"); + if (columnType == Types.BIGINT) { + ConsoleLogger.info("Migrating lastlogin column from bigint to timestamp"); + final String lastLoginOld = columnLastLogin + "_old"; + + // Rename lastlogin to lastlogin_old + String sql = String.format("ALTER TABLE %s CHANGE COLUMN %s %s BIGINT", + tableName, columnLastLogin, lastLoginOld); + PreparedStatement pst = con.prepareStatement(sql); + pst.execute(); + + // Create lastlogin column + sql = String.format("ALTER TABLE %s ADD COLUMN %s " + + "TIMESTAMP NOT NULL DEFAULT current_timestamp AFTER %s", + tableName, columnLastLogin, columnIp); + con.prepareStatement(sql).execute(); + + // Set values of lastlogin based on lastlogin_old + sql = String.format("UPDATE %s SET %s = FROM_UNIXTIME(%s)", + tableName, columnLastLogin, lastLoginOld); + con.prepareStatement(sql).execute(); + + // Drop lastlogin_old + sql = String.format("ALTER TABLE %s DROP COLUMN %s", + tableName, lastLoginOld); + con.prepareStatement(sql).execute(); + ConsoleLogger.info("Finished migration of lastlogin (bigint to timestamp)"); + } + } + private static void logSqlException(SQLException e) { ConsoleLogger.logException("Error during SQL operation:", e); } diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index 7e17de907..406a5f193 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -64,9 +64,9 @@ public class SQLite implements DataSource { try { this.connect(); this.setup(); - } catch (ClassNotFoundException | SQLException cnf) { - ConsoleLogger.showError("Can't use SQLITE... !"); - throw cnf; + } catch (ClassNotFoundException | SQLException ex) { + ConsoleLogger.logException("Error during SQLite initialization:", ex); + throw ex; } } @@ -102,7 +102,7 @@ public class SQLite implements DataSource { rs.close(); rs = con.getMetaData().getColumns(null, null, tableName, columnLastLogin); if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnLastLogin + " BIGINT DEFAULT '0';"); + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnLastLogin + " TIMESTAMP DEFAULT current_timestamp;"); } rs.close(); rs = con.getMetaData().getColumns(null, null, tableName, lastlocX); @@ -124,7 +124,7 @@ public class SQLite implements DataSource { rs.close(); rs = con.getMetaData().getColumns(null, null, tableName, columnLogged); if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnLogged + " BIGINT DEFAULT '0';"); + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + columnLogged + " INT DEFAULT '0';"); } rs.close(); rs = con.getMetaData().getColumns(null, null, tableName, columnRealName); @@ -178,13 +178,6 @@ public class SQLite implements DataSource { return null; } - /** - * Method getAuth. - * - * @param user String - * - * @return PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAuth(String) - */ @Override public synchronized PlayerAuth getAuth(String user) { PreparedStatement pst = null; diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index f2c3660e1..a068263ad 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -63,7 +63,7 @@ public class AsynchronousLogin { this.settings = settings; } - protected boolean needsCaptcha() { + private boolean needsCaptcha() { if (Settings.useCaptcha) { if (!plugin.captcha.containsKey(name)) { plugin.captcha.putIfAbsent(name, 1); @@ -87,7 +87,7 @@ public class AsynchronousLogin { * * @return PlayerAuth */ - protected PlayerAuth preAuth() { + private PlayerAuth preAuth() { if (PlayerCache.getInstance().isAuthenticated(name)) { m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return null; @@ -153,7 +153,6 @@ public class AsynchronousLogin { .name(name) .realName(realName) .ip(ip) - .lastLogin(new Date().getTime()) .email(email) .password(pAuth.getPassword()) .build(); @@ -221,14 +220,12 @@ public class AsynchronousLogin { } public void displayOtherAccounts(PlayerAuth auth) { - if (!Settings.displayOtherAccounts) { - return; - } - if (auth == null) { + if (!Settings.displayOtherAccounts || auth == null) { return; } + List auths = this.database.getAllAuthsByName(auth); - if (auths.isEmpty() || auths.size() == 1) { + if (auths.size() < 2) { return; } String message = "[AuthMe] " + StringUtils.join(", ", auths) + "."; diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java index 0f06e22e5..9b85d605b 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -43,7 +43,7 @@ public class AsyncRegister { this.settings = settings; } - private boolean preRegisterCheck() throws Exception { + private boolean preRegisterCheck() { String passLow = password.toLowerCase(); if (PlayerCache.getInstance().isAuthenticated(name)) { m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); @@ -86,18 +86,12 @@ public class AsyncRegister { } public void process() { - try { - if (!preRegisterCheck()) { - return; - } + if (preRegisterCheck()) { if (email != null && !email.isEmpty()) { emailRegister(); } else { passwordRegister(); } - } catch (Exception e) { - ConsoleLogger.logException("Error during async register process", e); - m.send(player, MessageKey.ERROR); } } @@ -130,7 +124,7 @@ public class AsyncRegister { } - private void passwordRegister() throws Exception { + private void passwordRegister() { final HashedPassword hashedPassword = plugin.getPasswordSecurity().computeHash(password, name); PlayerAuth auth = PlayerAuth.builder() .name(name) diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index ff7116213..1513c7f65 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -2,8 +2,7 @@ package fr.xephi.authme.settings; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSource.DataSourceType; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.util.Wrapper; import org.bukkit.configuration.file.FileConfiguration; @@ -281,10 +280,10 @@ public final class Settings { private static DataSourceType getDataSource() { String key = "DataSource.backend"; try { - return DataSource.DataSourceType.valueOf(configFile.getString(key, "sqlite").toUpperCase()); + return DataSourceType.valueOf(configFile.getString(key, "sqlite").toUpperCase()); } catch (IllegalArgumentException ex) { ConsoleLogger.showError("Unknown database backend; defaulting to SQLite database"); - return DataSource.DataSourceType.SQLITE; + return DataSourceType.SQLITE; } } diff --git a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java index 49ddc597b..87a8ef037 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java @@ -1,6 +1,6 @@ package fr.xephi.authme.settings.properties; -import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.SettingsClass; @@ -11,8 +11,8 @@ public class DatabaseSettings implements SettingsClass { @Comment({"What type of database do you want to use?", "Valid values: sqlite, mysql"}) - public static final Property BACKEND = - newProperty(DataSource.DataSourceType.class, "DataSource.backend", DataSource.DataSourceType.SQLITE); + public static final Property BACKEND = + newProperty(DataSourceType.class, "DataSource.backend", DataSourceType.SQLITE); @Comment("Enable database caching, should improve database performance") public static final Property USE_CACHING =