diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 0d317fb7..744f2211 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -26,7 +26,6 @@ import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.listener.AuthMePlayerListener16; import fr.xephi.authme.listener.AuthMePlayerListener18; import fr.xephi.authme.listener.AuthMeServerListener; -import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.output.ConsoleFilter; import fr.xephi.authme.output.Log4JFilter; import fr.xephi.authme.output.MessageKey; @@ -80,8 +79,6 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; -import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; /** @@ -115,7 +112,6 @@ public class AuthMe extends JavaPlugin { private BukkitService bukkitService; private AuthMeServiceInitializer initializer; private GeoLiteAPI geoLiteApi; - private SendMailSSL mail; /** * Constructor. @@ -249,9 +245,6 @@ public class AuthMe extends JavaPlugin { // Set console filter setupConsoleFilter(); - // Set up the mail API - setupMailApi(); - // Do a backup on start // TODO: maybe create a backup manager? new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.START); @@ -304,16 +297,6 @@ public class AuthMe extends JavaPlugin { initializer.get(API.class); } - /** - * Set up the mail API, if enabled. - */ - private void setupMailApi() { - // Make sure the mail API is enabled - if (!newSettings.getProperty(MAIL_ACCOUNT).isEmpty() && !newSettings.getProperty(MAIL_PASSWORD).isEmpty()) { - this.mail = new SendMailSSL(this, newSettings); - } - } - /** * Show the settings warnings, for various risky settings. */ @@ -687,15 +670,6 @@ public class AuthMe extends JavaPlugin { return commandHandler.processCommand(sender, commandLabel, args); } - /** - * Get the mailing instance. - * - * @return The send mail instance. - */ - public SendMailSSL getMail() { - return this.mail; - } - // ------------- // Service getters (deprecated) // Use @Inject fields instead diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index 5efe79e8..5114d1ff 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -53,6 +53,16 @@ public class CommandService { return messages.retrieve(key); } + /** + * Retrieve a message as a single String by its message key. + * + * @param key The message to retrieve + * @return The message + */ + public String retrieveSingle(MessageKey key) { + return messages.retrieveSingle(key); + } + /** * Retrieve the given property's value. * diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index 729a4b4e..d55e84ac 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -75,7 +75,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { if (dataSource.updatePassword(auth)) { commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); - ConsoleLogger.info(playerNameLowerCase + "'s password changed"); + ConsoleLogger.info(sender.getName() + " changed password of " + playerNameLowerCase); } else { commandService.send(sender, MessageKey.ERROR); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java index e18c9dc4..fef7cfb3 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ConverterCommand.java @@ -1,6 +1,7 @@ package fr.xephi.authme.command.executable.authme; import com.google.common.annotations.VisibleForTesting; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.converter.Converter; @@ -51,7 +52,11 @@ public class ConverterCommand implements ExecutableCommand { bukkitService.runTaskAsynchronously(new Runnable() { @Override public void run() { - converter.execute(sender); + try { + converter.execute(sender); + } catch (Exception e) { + ConsoleLogger.logException("Error during conversion:", e); + } } }); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index a4931e96..deddaf9c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -79,7 +79,7 @@ public class RegisterAdminCommand implements ExecutableCommand { bukkitService.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { - player.kickPlayer("An admin just registered you, please log in again"); + player.kickPlayer(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER)); } }); } diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java index 636027bb..346b9072 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java @@ -1,12 +1,12 @@ package fr.xephi.authme.command.executable.email; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.RandomString; @@ -33,17 +33,16 @@ public class RecoverEmailCommand extends PlayerCommand { private PlayerCache playerCache; @Inject - // TODO #655: Remove injected AuthMe instance once Authme#mail is encapsulated - private AuthMe plugin; + private SendMailSSL sendMailSsl; @Override public void runCommand(Player player, List arguments) { final String playerMail = arguments.get(0); final String playerName = player.getName(); - if (plugin.getMail() == null) { + if (!sendMailSsl.hasAllInformation()) { ConsoleLogger.showError("Mail API is not set"); - commandService.send(player, MessageKey.ERROR); + commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); return; } if (dataSource.isAuthAvailable(playerName)) { @@ -76,7 +75,7 @@ public class RecoverEmailCommand extends PlayerCommand { } auth.setPassword(hashNew); dataSource.updatePassword(auth); - plugin.getMail().main(auth, thePass); + sendMailSsl.sendPasswordMail(auth, thePass); commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE); } else { commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE); diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java index ed9f9eef..be8b1edb 100644 --- a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java @@ -3,6 +3,7 @@ package fr.xephi.authme.command.executable.register; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; +import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; @@ -14,6 +15,7 @@ import org.bukkit.entity.Player; import javax.inject.Inject; import java.util.List; +import static fr.xephi.authme.output.MessageKey.INCOMPLETE_EMAIL_SETTINGS; import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH; import static fr.xephi.authme.settings.properties.RegistrationSettings.ENABLE_CONFIRM_EMAIL; import static fr.xephi.authme.settings.properties.RegistrationSettings.USE_EMAIL_REGISTRATION; @@ -27,6 +29,9 @@ public class RegisterCommand extends PlayerCommand { @Inject private CommandService commandService; + @Inject + private SendMailSSL sendMailSsl; + @Override public void runCommand(Player player, List arguments) { if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { @@ -63,11 +68,10 @@ public class RegisterCommand extends PlayerCommand { } private void handleEmailRegistration(Player player, List arguments) { - if (commandService.getProperty(EmailSettings.MAIL_ACCOUNT).isEmpty()) { - player.sendMessage("Cannot register: no email address is set for the server. " - + "Please contact an administrator"); - ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email is set " - + "to send emails from. Please add one in your config at " + EmailSettings.MAIL_ACCOUNT.getPath()); + if (!sendMailSsl.hasAllInformation()) { + commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); + ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email or password is set " + + "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath()); return; } diff --git a/src/main/java/fr/xephi/authme/converter/vAuthConverter.java b/src/main/java/fr/xephi/authme/converter/vAuthConverter.java index 7c6f3ea5..9c116f02 100644 --- a/src/main/java/fr/xephi/authme/converter/vAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/vAuthConverter.java @@ -1,28 +1,77 @@ package fr.xephi.authme.converter; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.DataFolder; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.Scanner; +import java.util.UUID; + +import static fr.xephi.authme.util.StringUtils.makePath; public class vAuthConverter implements Converter { - private final AuthMe plugin; + private final DataSource dataSource; + private final File vAuthPasswordsFile; @Inject - vAuthConverter(AuthMe plugin) { - this.plugin = plugin; + vAuthConverter(@DataFolder File dataFolder, DataSource dataSource) { + vAuthPasswordsFile = new File(dataFolder.getParent(), makePath("vAuth", "passwords.yml")); + this.dataSource = dataSource; } @Override public void execute(CommandSender sender) { - try { - new vAuthFileReader(plugin).convert(); - } catch (Exception e) { - sender.sendMessage(e.getMessage()); - ConsoleLogger.showError(e.getMessage()); + try (Scanner scanner = new Scanner(vAuthPasswordsFile)) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + String name = line.split(": ")[0]; + String password = line.split(": ")[1]; + PlayerAuth auth; + if (isUuidInstance(password)) { + String pname; + try { + pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName(); + } catch (Exception | NoSuchMethodError e) { + pname = getName(UUID.fromString(name)); + } + if (pname == null) + continue; + auth = PlayerAuth.builder() + .name(pname.toLowerCase()) + .realName(pname) + .password(password, null).build(); + } else { + auth = PlayerAuth.builder() + .name(name.toLowerCase()) + .realName(name) + .password(password, null).build(); + } + dataSource.saveAuth(auth); + } + } catch (IOException e) { + ConsoleLogger.logException("Error while trying to import some vAuth data", e); } } + private static boolean isUuidInstance(String s) { + return s.length() > 8 && s.charAt(8) == '-'; + } + + private String getName(UUID uuid) { + for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { + if (op.getUniqueId().compareTo(uuid) == 0) { + return op.getName(); + } + } + return null; + } + } diff --git a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java deleted file mode 100644 index 4c5b3b4f..00000000 --- a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java +++ /dev/null @@ -1,83 +0,0 @@ -package fr.xephi.authme.converter; - -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; - -import java.io.File; -import java.io.IOException; -import java.util.Scanner; -import java.util.UUID; - -import static fr.xephi.authme.util.StringUtils.makePath; - -class vAuthFileReader { - - private final AuthMe plugin; - private final DataSource database; - - /** - * Constructor for vAuthFileReader. - * - * @param plugin AuthMe - */ - public vAuthFileReader(AuthMe plugin) { - this.plugin = plugin; - this.database = plugin.getDataSource(); - } - - public void convert() { - final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml")); - Scanner scanner; - try { - scanner = new Scanner(file); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - String name = line.split(": ")[0]; - String password = line.split(": ")[1]; - PlayerAuth auth; - if (isUuidInstance(password)) { - String pname; - try { - pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName(); - } catch (Exception | NoSuchMethodError e) { - pname = getName(UUID.fromString(name)); - } - if (pname == null) - continue; - auth = PlayerAuth.builder() - .name(pname.toLowerCase()) - .realName(pname) - .password(password, null).build(); - } else { - auth = PlayerAuth.builder() - .name(name.toLowerCase()) - .realName(name) - .password(password, null).build(); - } - database.saveAuth(auth); - } - scanner.close(); - } catch (IOException e) { - ConsoleLogger.logException("Error while trying to import some vAuth data", e); - } - - } - - private static boolean isUuidInstance(String s) { - return s.length() > 8 && s.charAt(8) == '-'; - } - - private String getName(UUID uuid) { - for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { - if (op.getUniqueId().compareTo(uuid) == 0) { - return op.getName(); - } - } - return null; - } - -} diff --git a/src/main/java/fr/xephi/authme/converter/xAuthConverter.java b/src/main/java/fr/xephi/authme/converter/xAuthConverter.java index 6fdddbf7..37dfc9fe 100644 --- a/src/main/java/fr/xephi/authme/converter/xAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/xAuthConverter.java @@ -1,28 +1,146 @@ package fr.xephi.authme.converter; -import fr.xephi.authme.AuthMe; +import de.luricos.bukkit.xAuth.database.DatabaseTables; +import de.luricos.bukkit.xAuth.utils.xAuthLog; +import de.luricos.bukkit.xAuth.xAuth; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.util.CollectionUtils; import org.bukkit.command.CommandSender; +import org.bukkit.plugin.PluginManager; import javax.inject.Inject; +import java.io.File; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static fr.xephi.authme.util.StringUtils.makePath; public class xAuthConverter implements Converter { - private final AuthMe plugin; - @Inject - xAuthConverter(AuthMe plugin) { - this.plugin = plugin; + @DataFolder + private File dataFolder; + @Inject + private DataSource database; + @Inject + private PluginManager pluginManager; + + xAuthConverter() { } @Override public void execute(CommandSender sender) { try { Class.forName("de.luricos.bukkit.xAuth.xAuth"); - xAuthToFlat converter = new xAuthToFlat(plugin, sender); - converter.convert(); + convert(sender); } catch (ClassNotFoundException ce) { sender.sendMessage("xAuth has not been found, please put xAuth.jar in your plugin folder and restart!"); } } + private void convert(CommandSender sender) { + if (pluginManager.getPlugin("xAuth") == null) { + sender.sendMessage("[AuthMe] xAuth plugin not found"); + return; + } + // TODO ljacqu 20160702: xAuthDb is not used except for the existence check -- is this intended? + File xAuthDb = new File(dataFolder.getParent(), makePath("xAuth", "xAuth.h2.db")); + if (!xAuthDb.exists()) { + sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data..."); + } + List players = getXAuthPlayers(); + if (CollectionUtils.isEmpty(players)) { + sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players"); + return; + } + sender.sendMessage("[AuthMe] Starting import..."); + + for (int id : players) { + String pl = getIdPlayer(id); + String psw = getPassword(id); + if (psw != null && !psw.isEmpty() && pl != null) { + PlayerAuth auth = PlayerAuth.builder() + .name(pl.toLowerCase()) + .realName(pl) + .password(psw, null).build(); + database.saveAuth(auth); + } + } + sender.sendMessage("[AuthMe] Successfully converted from xAuth database"); + } + + private String getIdPlayer(int id) { + String realPass = ""; + Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + ps = conn.prepareStatement(sql); + ps.setInt(1, id); + rs = ps.executeQuery(); + if (!rs.next()) + return null; + realPass = rs.getString("playername").toLowerCase(); + } catch (SQLException e) { + xAuthLog.severe("Failed to retrieve name for account: " + id, e); + return null; + } finally { + xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); + } + return realPass; + } + + private List getXAuthPlayers() { + List xP = new ArrayList<>(); + Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = String.format("SELECT * FROM `%s`", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + ps = conn.prepareStatement(sql); + rs = ps.executeQuery(); + while (rs.next()) { + xP.add(rs.getInt("id")); + } + } catch (SQLException e) { + xAuthLog.severe("Cannot import xAuthPlayers", e); + return new ArrayList<>(); + } finally { + xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); + } + return xP; + } + + private String getPassword(int accountId) { + String realPass = ""; + Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + ps = conn.prepareStatement(sql); + ps.setInt(1, accountId); + rs = ps.executeQuery(); + if (!rs.next()) + return null; + realPass = rs.getString("password"); + } catch (SQLException e) { + xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e); + return null; + } finally { + xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); + } + return realPass; + } + } diff --git a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java b/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java deleted file mode 100644 index 34ec2a5e..00000000 --- a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java +++ /dev/null @@ -1,136 +0,0 @@ -package fr.xephi.authme.converter; - -import de.luricos.bukkit.xAuth.database.DatabaseTables; -import de.luricos.bukkit.xAuth.utils.xAuthLog; -import de.luricos.bukkit.xAuth.xAuth; -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.util.CollectionUtils; -import org.bukkit.command.CommandSender; - -import java.io.File; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -class xAuthToFlat { - - private final AuthMe instance; - private final DataSource database; - private final CommandSender sender; - - public xAuthToFlat(AuthMe instance, CommandSender sender) { - this.instance = instance; - this.database = instance.getDataSource(); - this.sender = sender; - } - - public boolean convert() { - if (instance.getServer().getPluginManager().getPlugin("xAuth") == null) { - sender.sendMessage("[AuthMe] xAuth plugin not found"); - return false; - } - File xAuthDb = new File(instance.getDataFolder().getParent(), "xAuth" + File.separator + "xAuth.h2.db"); - if (!xAuthDb.exists()) { - sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data..."); - } - List players = getXAuthPlayers(); - if (CollectionUtils.isEmpty(players)) { - sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players"); - return false; - } - sender.sendMessage("[AuthMe] Starting import..."); - try { - for (int id : players) { - String pl = getIdPlayer(id); - String psw = getPassword(id); - if (psw != null && !psw.isEmpty() && pl != null) { - PlayerAuth auth = PlayerAuth.builder() - .name(pl.toLowerCase()) - .realName(pl) - .password(psw, null).build(); - database.saveAuth(auth); - } - } - sender.sendMessage("[AuthMe] Successfully converted from xAuth database"); - } catch (Exception e) { - sender.sendMessage("[AuthMe] An error has occurred while importing the xAuth database." - + " The import may have succeeded partially."); - ConsoleLogger.logException("Error during xAuth database import", e); - } - return true; - } - - private String getIdPlayer(int id) { - String realPass = ""; - Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", - xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); - ps = conn.prepareStatement(sql); - ps.setInt(1, id); - rs = ps.executeQuery(); - if (!rs.next()) - return null; - realPass = rs.getString("playername").toLowerCase(); - } catch (SQLException e) { - xAuthLog.severe("Failed to retrieve name for account: " + id, e); - return null; - } finally { - xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); - } - return realPass; - } - - private List getXAuthPlayers() { - List xP = new ArrayList<>(); - Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = String.format("SELECT * FROM `%s`", - xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); - ps = conn.prepareStatement(sql); - rs = ps.executeQuery(); - while (rs.next()) { - xP.add(rs.getInt("id")); - } - } catch (SQLException e) { - xAuthLog.severe("Cannot import xAuthPlayers", e); - return new ArrayList<>(); - } finally { - xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); - } - return xP; - } - - private String getPassword(int accountId) { - String realPass = ""; - Connection conn = xAuth.getPlugin().getDatabaseController().getConnection(); - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", - xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); - ps = conn.prepareStatement(sql); - ps.setInt(1, accountId); - rs = ps.executeQuery(); - if (!rs.next()) - return null; - realPass = rs.getString("password"); - } catch (SQLException e) { - xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e); - return null; - } finally { - xAuth.getPlugin().getDatabaseController().close(conn, ps, rs); - } - return realPass; - } -} diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 3902e3e5..e1720f95 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -3,14 +3,12 @@ package fr.xephi.authme.datasource; import com.google.common.annotations.VisibleForTesting; import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; - import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.XFBCRYPT; import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.SecuritySettings; @@ -56,12 +54,10 @@ public class MySQL implements DataSource { if (e instanceof IllegalArgumentException) { ConsoleLogger.showError("Invalid database arguments! Please check your configuration!"); ConsoleLogger.showError("If this error persists, please report it to the developer!"); - throw e; } if (e instanceof PoolInitializationException) { ConsoleLogger.showError("Can't initialize database connection! Please check your configuration!"); ConsoleLogger.showError("If this error persists, please report it to the developer!"); - throw new PoolInitializationException(e); } ConsoleLogger.showError("Can't use the Hikari Connection Pool! Please, report this error to the developer!"); throw e; @@ -69,7 +65,7 @@ public class MySQL implements DataSource { // Initialize the database try { - setupConnection(); + checkTablesAndColumns(); } catch (SQLException e) { close(); ConsoleLogger.logException("Can't initialize the MySQL database:", e); @@ -140,24 +136,13 @@ public class MySQL implements DataSource { return ds.getConnection(); } - private void setupConnection() throws SQLException { + private void checkTablesAndColumns() throws SQLException { try (Connection con = getConnection(); Statement st = con.createStatement()) { - // Create table if not exists. + // Create table with ID column if it doesn't exist String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " (" + col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT," - + col.NAME + " VARCHAR(255) NOT NULL UNIQUE," - + col.REAL_NAME + " VARCHAR(255) NOT NULL," - + col.PASSWORD + " VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL," - + col.IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1'," - + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0," - + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," - + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," - + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," - + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," - + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com'," - + col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0'," + "PRIMARY KEY (" + col.ID + ")" - + ") CHARACTER SET = utf8"; + + ") CHARACTER SET = utf8;"; st.executeUpdate(sql); DatabaseMetaData md = con.getMetaData(); @@ -177,8 +162,7 @@ public class MySQL implements DataSource { } if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) { - st.executeUpdate("ALTER TABLE " + tableName - + " ADD COLUMN " + col.SALT + " VARCHAR(255);"); + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);"); } if (isColumnMissing(md, col.IP)) { @@ -219,8 +203,6 @@ public class MySQL implements DataSource { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL); } - - st.close(); } ConsoleLogger.info("MySQL setup finished"); } diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index d5109af2..a60dfd0d 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -5,11 +5,11 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.util.StringUtils; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -69,67 +69,73 @@ public class SQLite implements DataSource { this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db"); } - private void setup() throws SQLException { - Statement st = null; - ResultSet rs = null; - try { - st = con.createStatement(); - st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + col.ID + " INTEGER AUTO_INCREMENT," + col.NAME + " VARCHAR(255) NOT NULL UNIQUE," + col.PASSWORD + " VARCHAR(255) NOT NULL," + col.IP + " VARCHAR(40) NOT NULL," + col.LAST_LOGIN + " BIGINT," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com'," + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));"); - rs = con.getMetaData().getColumns(null, null, tableName, col.PASSWORD); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;"); + @VisibleForTesting + protected void setup() throws SQLException { + try (Statement st = con.createStatement()) { + // Note: cannot add unique fields later on in SQLite, so we add it on initialization + st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + + col.ID + " INTEGER AUTO_INCREMENT, " + + col.NAME + " VARCHAR(255) NOT NULL UNIQUE, " + + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));"); + + DatabaseMetaData md = con.getMetaData(); + + if (isColumnMissing(md, col.REAL_NAME)) { + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + + col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';"); } - rs.close(); - if (!col.SALT.isEmpty()) { - rs = con.getMetaData().getColumns(null, null, tableName, col.SALT); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);"); - } - rs.close(); + + if (isColumnMissing(md, col.PASSWORD)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL DEFAULT '';"); } - rs = con.getMetaData().getColumns(null, null, tableName, col.IP); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;"); + + if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) { + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.LAST_LOGIN); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP DEFAULT current_timestamp;"); + + if (isColumnMissing(md, col.IP)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL DEFAULT '';"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_X); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0';"); - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0';"); - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';"); + + if (isColumnMissing(md, col.LAST_LOGIN)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP;"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_WORLD); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';"); + + if (isColumnMissing(md, col.LASTLOC_X)) { + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + + " DOUBLE NOT NULL DEFAULT '0.0';"); + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y + + " DOUBLE NOT NULL DEFAULT '0.0';"); + st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z + + " DOUBLE NOT NULL DEFAULT '0.0';"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.EMAIL); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';"); + + if (isColumnMissing(md, col.LASTLOC_WORLD)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.IS_LOGGED); - if (!rs.next()) { + + if (isColumnMissing(md, col.EMAIL)) { + st.executeUpdate("ALTER TABLE " + tableName + + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';"); + } + + if (isColumnMissing(md, col.IS_LOGGED)) { st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';"); } - rs.close(); - rs = con.getMetaData().getColumns(null, null, tableName, col.REAL_NAME); - if (!rs.next()) { - st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';"); - } - } finally { - close(rs); - close(st); } ConsoleLogger.info("SQLite Setup finished"); } + private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException { + try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) { + return !rs.next(); + } + } + @Override public void reload() { close(con); @@ -146,7 +152,7 @@ public class SQLite implements DataSource { PreparedStatement pst = null; ResultSet rs = null; try { - pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"); + pst = con.prepareStatement("SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);"); pst.setString(1, user); rs = pst.executeQuery(); return rs.next(); diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/AuthMeTablistPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/protocollib/AuthMeTablistPacketAdapter.java deleted file mode 100644 index 75e06cd5..00000000 --- a/src/main/java/fr/xephi/authme/listener/protocollib/AuthMeTablistPacketAdapter.java +++ /dev/null @@ -1,120 +0,0 @@ -package fr.xephi.authme.listener.protocollib; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketAdapter; -import com.comphenix.protocol.events.PacketContainer; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.utility.MinecraftVersion; -import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode; -import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction; -import com.comphenix.protocol.wrappers.PlayerInfoData; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import com.comphenix.protocol.wrappers.WrappedGameProfile; -import com.google.common.collect.Lists; -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.cache.auth.PlayerCache; -import fr.xephi.authme.util.BukkitService; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; - -class AuthMeTablistPacketAdapter extends PacketAdapter { - - private final BukkitService bukkitService; - private boolean isRegistered; - - public AuthMeTablistPacketAdapter(AuthMe plugin, BukkitService bukkitService) { - super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); - this.bukkitService = bukkitService; - } - - @Override - public void onPacketSending(PacketEvent packetEvent) { - Player receiver = packetEvent.getPlayer(); - if (packetEvent.getPacketType() == PacketType.Play.Server.PLAYER_INFO - && !PlayerCache.getInstance().isAuthenticated(receiver.getName().toLowerCase())) { - //this hides the tablist for the new joining players. Already playing users will see the new player - try { - PacketContainer packet = packetEvent.getPacket(); - PlayerInfoAction playerInfoAction = packet.getPlayerInfoAction().read(0); - if (playerInfoAction == PlayerInfoAction.ADD_PLAYER) { - List playerInfoList = Lists.newArrayList(packet.getPlayerInfoDataLists().read(0)); - for (Iterator iterator = playerInfoList.iterator(); iterator.hasNext();) { - PlayerInfoData current = iterator.next(); - UUID uuid = current.getProfile().getUUID(); - if (Bukkit.getPlayer(uuid) == null) { - //player is not online -> a NPC - iterator.remove(); - } - } - - packet.getPlayerInfoDataLists().write(0, playerInfoList); - } - } catch (Exception ex) { - ConsoleLogger.logException("Couldn't modify outgoing tablist packet", ex); - } - } - } - - public void sendTablist(Player receiver) { - if (!isRegistered) { - return; - } - - WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(receiver); - - ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); - NativeGameMode gamemode = NativeGameMode.fromBukkit(receiver.getGameMode()); - - WrappedChatComponent displayName = WrappedChatComponent.fromText(receiver.getDisplayName()); - PlayerInfoData playerInfoData = new PlayerInfoData(gameProfile, 0, gamemode, displayName); - - //add info containing the skin data - PacketContainer addInfo = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO); - addInfo.getPlayerInfoAction().write(0, PlayerInfoAction.ADD_PLAYER); - addInfo.getPlayerInfoDataLists().write(0, Arrays.asList(playerInfoData)); - - try { - //adds the skin - protocolManager.sendServerPacket(receiver, addInfo); - } catch (InvocationTargetException ex) { - plugin.getLogger().log(Level.SEVERE, "Exception sending instant skin change packet", ex); - } - - //triggers an update for others player to see them - for (Player onlinePlayer : bukkitService.getOnlinePlayers()) { - if (onlinePlayer.equals(receiver) || !receiver.canSee(onlinePlayer)) { - continue; - } - - //removes the entity and display them - receiver.hidePlayer(onlinePlayer); - receiver.showPlayer(onlinePlayer); - } - } - - public void register() { - if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) { - ProtocolLibrary.getProtocolManager().addPacketListener(this); - isRegistered = true; - } else { - ConsoleLogger.info("The hideTablist feature is not compatible with your minecraft version"); - ConsoleLogger.info("It requires 1.8+. Disabling the hideTablist feature..."); - } - } - - public void unregister() { - ProtocolLibrary.getProtocolManager().removePacketListener(this); - isRegistered = false; - } -} diff --git a/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java b/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java index 18b13495..34630c04 100644 --- a/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java +++ b/src/main/java/fr/xephi/authme/listener/protocollib/ProtocolLibService.java @@ -15,12 +15,10 @@ public class ProtocolLibService implements SettingsDependent { /* Packet Adapters */ private AuthMeInventoryPacketAdapter inventoryPacketAdapter; private AuthMeTabCompletePacketAdapter tabCompletePacketAdapter; - private AuthMeTablistPacketAdapter tablistPacketAdapter; /* Settings */ private boolean protectInvBeforeLogin; private boolean denyTabCompleteBeforeLogin; - private boolean hideTablistBeforeLogin; /* Service */ private boolean isEnabled; @@ -44,12 +42,10 @@ public class ProtocolLibService implements SettingsDependent { if (protectInvBeforeLogin) { ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); } + if (denyTabCompleteBeforeLogin) { ConsoleLogger.showError("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it..."); } - if (hideTablistBeforeLogin) { - ConsoleLogger.showError("WARNING! The hideTablist feature requires ProtocolLib! Disabling it..."); - } this.isEnabled = false; return; @@ -70,13 +66,6 @@ public class ProtocolLibService implements SettingsDependent { tabCompletePacketAdapter.unregister(); tabCompletePacketAdapter = null; } - if (hideTablistBeforeLogin && tablistPacketAdapter == null) { - tablistPacketAdapter = new AuthMeTablistPacketAdapter(plugin, bukkitService); - tablistPacketAdapter.register(); - } else if (tablistPacketAdapter != null) { - tablistPacketAdapter.unregister(); - tablistPacketAdapter = null; - } this.isEnabled = true; } @@ -92,10 +81,6 @@ public class ProtocolLibService implements SettingsDependent { tabCompletePacketAdapter.unregister(); tabCompletePacketAdapter = null; } - if (tablistPacketAdapter != null) { - tablistPacketAdapter.unregister(); - tablistPacketAdapter = null; - } } /** @@ -109,21 +94,9 @@ public class ProtocolLibService implements SettingsDependent { } } - /** - * Send a tab list packet to a player. - * - * @param player The player to send the packet to. - */ - public void sendTabList(Player player) { - if (isEnabled && tablistPacketAdapter != null) { - tablistPacketAdapter.sendTablist(player); - } - } - @Override public void loadSettings(NewSetting settings) { this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN); this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); - this.hideTablistBeforeLogin = settings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN); } } diff --git a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java index 58b64d6b..949f3777 100644 --- a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java +++ b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java @@ -5,45 +5,72 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.StringUtils; import org.apache.commons.mail.EmailConstants; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; -import org.bukkit.Bukkit; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.imageio.ImageIO; +import javax.inject.Inject; import javax.mail.Session; import java.io.File; import java.io.IOException; import java.security.Security; import java.util.Properties; +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; + /** * @author Xephi59 */ public class SendMailSSL { - private final AuthMe plugin; - private final NewSetting settings; + @Inject + private AuthMe plugin; + @Inject + private NewSetting settings; + @Inject + private BukkitService bukkitService; - public SendMailSSL(AuthMe plugin, NewSetting settings) { - this.plugin = plugin; - this.settings = settings; + SendMailSSL() { } - public void main(final PlayerAuth auth, final String newPass) { - final String mailText = replaceMailTags(settings.getEmailMessage(), plugin, auth, newPass); - Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { + /** + * Returns whether all necessary settings are set for sending mails. + * + * @return true if the necessary email settings are set, false otherwise + */ + public boolean hasAllInformation() { + return !settings.getProperty(MAIL_ACCOUNT).isEmpty() + && !settings.getProperty(MAIL_PASSWORD).isEmpty(); + } + + /** + * Sends an email to the user with his new password. + * + * @param auth the player auth of the player + * @param newPass the new password + */ + public void sendPasswordMail(final PlayerAuth auth, final String newPass) { + if (!hasAllInformation()) { + ConsoleLogger.showError("Cannot perform email registration: not all email settings are complete"); + return; + } + + final String mailText = replaceMailTags(settings.getEmailMessage(), auth, newPass); + bukkitService.runTaskAsynchronously(new Runnable() { @Override public void run() { Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); HtmlEmail email; try { - email = initializeMail(auth, settings); + email = initializeMail(auth.getEmail()); } catch (EmailException e) { ConsoleLogger.logException("Failed to create email with the given settings:", e); return; @@ -54,7 +81,7 @@ public class SendMailSSL { File file = null; if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { try { - file = generateImage(auth, plugin, newPass); + file = generateImage(auth.getNickname(), plugin, newPass); content = embedImageIntoEmailContent(file, email, content); } catch (IOException | EmailException e) { ConsoleLogger.logException( @@ -67,13 +94,12 @@ public class SendMailSSL { file.delete(); } } - }); } - private static File generateImage(PlayerAuth auth, AuthMe plugin, String newPass) throws IOException { + private static File generateImage(String name, AuthMe plugin, String newPass) throws IOException { ImageGenerator gen = new ImageGenerator(newPass); - File file = new File(plugin.getDataFolder(), auth.getNickname() + "_new_pass.jpg"); + File file = new File(plugin.getDataFolder(), name + "_new_pass.jpg"); ImageIO.write(gen.generateImage(), "jpg", file); return file; } @@ -85,8 +111,7 @@ public class SendMailSSL { return content.replace("", ""); } - private static HtmlEmail initializeMail(PlayerAuth auth, NewSetting settings) - throws EmailException { + private HtmlEmail initializeMail(String emailAddress) throws EmailException { String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT); String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME)) ? senderMail @@ -98,12 +123,12 @@ public class SendMailSSL { email.setCharset(EmailConstants.UTF_8); email.setSmtpPort(port); email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST)); - email.addTo(auth.getEmail()); + email.addTo(emailAddress); email.setFrom(senderMail, senderName); email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT)); email.setAuthentication(senderMail, mailPassword); - setPropertiesForPort(email, port, settings); + setPropertiesForPort(email, port); return email; } @@ -124,15 +149,14 @@ public class SendMailSSL { } } - private static String replaceMailTags(String mailText, AuthMe plugin, PlayerAuth auth, String newPass) { + private String replaceMailTags(String mailText, PlayerAuth auth, String newPass) { return mailText .replace("", auth.getNickname()) .replace("", plugin.getServer().getServerName()) .replace("", newPass); } - private static void setPropertiesForPort(HtmlEmail email, int port, NewSetting settings) - throws EmailException { + private void setPropertiesForPort(HtmlEmail email, int port) throws EmailException { switch (port) { case 587: String oAuth2Token = settings.getProperty(EmailSettings.OAUTH2_TOKEN); diff --git a/src/main/java/fr/xephi/authme/output/MessageKey.java b/src/main/java/fr/xephi/authme/output/MessageKey.java index 670366f5..c445b9a2 100644 --- a/src/main/java/fr/xephi/authme/output/MessageKey.java +++ b/src/main/java/fr/xephi/authme/output/MessageKey.java @@ -143,7 +143,11 @@ public enum MessageKey { ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"), - ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"); + ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"), + + KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"), + + INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings"); private String key; private String[] tags; diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index 7f6e598c..210c57ca 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -111,10 +111,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess { restoreInventory(player); } - if (service.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) { - protocolLibService.sendTabList(player); - } - // Clean up no longer used temporary data limboCache.deleteLimboPlayer(player); } 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 4ddaff5e..056ae36c 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -4,6 +4,7 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.AsynchronousProcess; @@ -55,6 +56,9 @@ public class AsyncRegister implements AsynchronousProcess { @Inject private ValidationService validationService; + @Inject + private SendMailSSL sendMailSsl; + AsyncRegister() { } private boolean preRegisterCheck(Player player, String password) { @@ -137,7 +141,7 @@ public class AsyncRegister implements AsynchronousProcess { } database.updateEmail(auth); database.updateSession(auth); - plugin.getMail().main(auth, password); + sendMailSsl.sendPasswordMail(auth, password); syncProcessManager.processSyncEmailRegister(player); } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 9e20d49b..208752ec 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; -import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.LimboPlayerTaskManager; import fr.xephi.authme.util.BukkitService; @@ -96,10 +95,6 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess { final String name = player.getName().toLowerCase(); LimboPlayer limbo = limboCache.getLimboPlayer(name); if (limbo != null) { - if (service.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) { - protocolLibService.sendTabList(player); - } - Utils.teleportToSpawn(player); if (service.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) { diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 7c128d99..51bbb11d 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -19,15 +19,15 @@ public final class Settings { public static boolean isPermissionCheckEnabled; public static boolean isTeleportToSpawnEnabled; public static boolean isAllowRestrictedIp; + public static boolean isSaveQuitLocationEnabled; + public static boolean protectInventoryBeforeLogInEnabled; public static boolean isStopEnabled; public static boolean reloadSupport; public static boolean noTeleport; - public static boolean isRemoveSpeedEnabled; public static String getUnloggedinGroup; public static String unRegisteredGroup; public static String getRegisteredGroup; public static String defaultWorld; - public static String crazyloginFileName; public static int getNonActivatedGroup; private static FileConfiguration configFile; @@ -45,6 +45,7 @@ public final class Settings { isPermissionCheckEnabled = load(PluginSettings.ENABLE_PERMISSION_CHECK); isTeleportToSpawnEnabled = load(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN); isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS); + isSaveQuitLocationEnabled = load(RestrictionSettings.SAVE_QUIT_LOCATION); isRemoveSpeedEnabled = load(RestrictionSettings.REMOVE_SPEED); getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP); getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1); @@ -55,7 +56,6 @@ public final class Settings { reloadSupport = configFile.getBoolean("Security.ReloadCommand.useReloadCommandSupport", true); defaultWorld = configFile.getString("Purge.defaultWorld", "world"); noTeleport = load(RestrictionSettings.NO_TELEPORT); - crazyloginFileName = configFile.getString("Converter.CrazyLogin.fileName", "accounts.db"); } /** diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index fdafea6f..4a4add7b 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -92,10 +92,10 @@ public class SettingsMigrationService { final File emailFile = new File(pluginFolder, "email.html"); final String mailText = configuration.getString(oldSettingPath) - .replace("", "") - .replace("", "") - .replace("", "") - .replace("", ""); + .replace("", "").replace("%playername%", "") + .replace("", "").replace("%servername%", "") + .replace("", "").replace("%generatedpass%", "") + .replace("", "").replace("%image%", ""); if (!emailFile.exists()) { try (FileWriter fw = new FileWriter(emailFile)) { fw.write(mailText); diff --git a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java index cf0f7932..0a3722ac 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java @@ -25,7 +25,6 @@ public class PluginSettings implements SettingsClass { @Comment({ "After how many minutes should a session expire?", - "0 for unlimited time (Very dangerous, use it at your own risk!)", "Remember that sessions will end only after the timeout, and", "if the player's IP has changed but the timeout hasn't expired,", "the player will be kicked from the server due to invalid session" diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 3fd7b40b..e2b8d039 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -141,10 +141,6 @@ public class RestrictionSettings implements SettingsClass { public static final Property DENY_TABCOMPLETE_BEFORE_LOGIN = newProperty("settings.restrictions.DenyTabCompleteBeforeLogin", true); - @Comment("Should we hide the tablist before logging in? Requires ProtocolLib.") - public static final Property HIDE_TABLIST_BEFORE_LOGIN = - newProperty("settings.restrictions.HideTablistBeforeLogin", true); - @Comment({ "Should we display all other accounts from a player when he joins?", "permission: /authme.admin.accounts"}) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 99cc3a2b..8d4a0f0f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -54,7 +54,6 @@ settings: # hasn't expired, he will not need to authenticate. enabled: false # After how many minutes a session should expire? - # 0 for unlimited time (Very dangerous, use it at your own risk!) # Consider that session will end only after the timeout time, and # if the player's ip has changed but the timeout hasn't expired, # player will be kicked out of sever due to invalidSession! @@ -141,8 +140,6 @@ settings: ProtectInventoryBeforeLogIn: true # Should we deny the tabcomplete feature before logging in? Requires ProtocolLib. DenyTabCompleteBeforeLogin: true - # Should we hide the tablist before logging in? Requires ProtocolLib. - HideTablistBeforeLogin: true # Should we display all other accounts from a player when he joins? # permission: /authme.admin.accounts displayOtherAccounts: true diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index f2926eb9..b02a2c98 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -68,3 +68,5 @@ invalid_name_case: 'You should join using username %valid, not %invalid.' tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.' accounts_owned_self: 'You own %count accounts:' accounts_owned_other: 'The player %name has %count accounts:' +kicked_admin_registered: 'An admin just registered you; please log in again' +incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.' diff --git a/src/main/resources/messages/messages_fr.yml b/src/main/resources/messages/messages_fr.yml index 848090e8..e1180ef2 100644 --- a/src/main/resources/messages/messages_fr.yml +++ b/src/main/resources/messages/messages_fr.yml @@ -69,3 +69,5 @@ tempban_max_logins: '&cVous êtes temporairement banni suite à plusieurs échec accounts_owned_self: '&fVous avez %count comptes:' accounts_owned_other: '&fLe joueur %name a %count comptes:' denied_command: '&cVous devez être connecté pour pouvoir utiliser cette commande.' +kicked_admin_registered: 'Un admin vient de vous enregistrer, veuillez vous reconnecter.' +incomplete_email_settings: '&cErreur : Tous les paramètres requis ne sont pas présent pour l''envoi de mail, veuillez contacter un admin.' diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java index d6d0924e..441a38a2 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommandTest.java @@ -22,12 +22,10 @@ import org.mockito.runners.MockitoJUnitRunner; import java.util.Arrays; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -160,6 +158,8 @@ public class RegisterAdminCommandTest { given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword); Player player = mock(Player.class); given(bukkitService.getPlayerExact(user)).willReturn(player); + String kickForAdminRegister = "Admin registered you -- log in again"; + given(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); CommandSender sender = mock(CommandSender.class); // when @@ -174,7 +174,7 @@ public class RegisterAdminCommandTest { verify(dataSource).saveAuth(captor.capture()); assertAuthHasInfo(captor.getValue(), user, hashedPassword); verify(dataSource).setUnlogged(user); - verify(player).kickPlayer(argThat(containsString("please log in again"))); + verify(player).kickPlayer(kickForAdminRegister); } private void assertAuthHasInfo(PlayerAuth auth, String name, HashedPassword hashedPassword) { diff --git a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java index 217b74be..dd30d970 100644 --- a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.register; import fr.xephi.authme.TestHelper; import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.mail.SendMailSSL; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; @@ -49,6 +50,8 @@ public class RegisterCommandTest { @Mock private Management management; + @Mock + private SendMailSSL sendMailSsl; @BeforeClass public static void setup() { @@ -72,7 +75,7 @@ public class RegisterCommandTest { // then verify(sender).sendMessage(argThat(containsString("Player only!"))); - verifyZeroInteractions(management); + verifyZeroInteractions(management, sendMailSsl); } @Test @@ -86,6 +89,7 @@ public class RegisterCommandTest { // then verify(management).performRegister(player, "", "", true); + verifyZeroInteractions(sendMailSsl); } @Test @@ -98,7 +102,7 @@ public class RegisterCommandTest { // then verify(commandService).send(player, MessageKey.USAGE_REGISTER); - verifyZeroInteractions(management); + verifyZeroInteractions(management, sendMailSsl); } @Test @@ -112,7 +116,7 @@ public class RegisterCommandTest { // then verify(commandService).send(player, MessageKey.USAGE_REGISTER); - verifyZeroInteractions(management); + verifyZeroInteractions(management, sendMailSsl); } @Test @@ -127,7 +131,7 @@ public class RegisterCommandTest { // then verify(commandService).send(player, MessageKey.USAGE_REGISTER); - verifyZeroInteractions(management); + verifyZeroInteractions(management, sendMailSsl); } @Test @@ -135,14 +139,15 @@ public class RegisterCommandTest { // given given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(false); - given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn(""); + given(sendMailSsl.hasAllInformation()).willReturn(false); Player player = mock(Player.class); // when command.executeCommand(player, Collections.singletonList("myMail@example.tld")); // then - verify(player).sendMessage(argThat(containsString("no email address"))); + verify(commandService).send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS); + verify(sendMailSsl).hasAllInformation(); verifyZeroInteractions(management); } @@ -155,6 +160,7 @@ public class RegisterCommandTest { given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); + given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); // when @@ -175,6 +181,7 @@ public class RegisterCommandTest { given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); + given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); // when @@ -182,6 +189,7 @@ public class RegisterCommandTest { // then verify(commandService).send(player, MessageKey.USAGE_REGISTER); + verify(sendMailSsl).hasAllInformation(); verifyZeroInteractions(management); } @@ -196,6 +204,7 @@ public class RegisterCommandTest { given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true); given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com"); + given(sendMailSsl.hasAllInformation()).willReturn(true); Player player = mock(Player.class); // when @@ -203,6 +212,7 @@ public class RegisterCommandTest { // then verify(commandService).validateEmail(playerMail); + verify(sendMailSsl).hasAllInformation(); verify(management).performRegister(eq(player), argThat(stringWithLength(passLength)), eq(playerMail), eq(true)); } @@ -217,7 +227,7 @@ public class RegisterCommandTest { // then verify(commandService).send(player, MessageKey.PASSWORD_MATCH_ERROR); - verifyZeroInteractions(management); + verifyZeroInteractions(management, sendMailSsl); } @Test diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index e9488a40..0c0d217b 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -12,6 +12,9 @@ import java.util.Set; import static fr.xephi.authme.AuthMeMatchers.equalToHash; import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData; import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; @@ -170,6 +173,22 @@ public abstract class AbstractDataSourceIntegrationTest { assertThat(dataSource.getPassword("user"), equalToHash("new_hash")); } + @Test + public void shouldUpdatePasswordWithPlayerAuth() { + // given + DataSource dataSource = getDataSource("salt"); + PlayerAuth bobbyAuth = PlayerAuth.builder().name("bobby").password(new HashedPassword("tt", "cc")).build(); + PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").password(new HashedPassword("tt", "cc")).build(); + + // when + boolean response1 = dataSource.updatePassword(bobbyAuth); + boolean response2 = dataSource.updatePassword(invalidAuth); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getPassword("bobby"), equalToHash("tt", "cc")); + } + @Test public void shouldRemovePlayerAuth() { // given @@ -321,4 +340,49 @@ public abstract class AbstractDataSourceIntegrationTest { assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89")); } + @Test + public void shouldGetRecordsToPurge() { + // given + DataSource dataSource = getDataSource(); + // 1453242857 -> user, 1449136800 -> bobby + + // when + Set records1 = dataSource.getRecordsToPurge(1450000000); + Set records2 = dataSource.getRecordsToPurge(1460000000); + + // then + assertThat(records1, contains("bobby")); + assertThat(records2, containsInAnyOrder("bobby", "user")); + // check that the entry was not deleted because of running this command + assertThat(dataSource.isAuthAvailable("bobby"), equalTo(true)); + } + + @Test + public void shouldPerformOperationsOnIsLoggedColumnSuccessfully() { + DataSource dataSource = getDataSource(); + // on startup no one should be marked as logged + assertThat(dataSource.getLoggedPlayers(), empty()); + + // Mark user as logged + dataSource.setLogged("user"); + // non-existent user should not break database + dataSource.setLogged("does-not-exist"); + + assertThat(dataSource.isLogged("user"), equalTo(true)); + assertThat(dataSource.isLogged("bobby"), equalTo(false)); + + // Set bobby logged and unlog user + dataSource.setLogged("bobby"); + dataSource.setUnlogged("user"); + assertThat(dataSource.getLoggedPlayers(), + contains(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"))); + + // Set both as logged (even if Bobby already is logged) + dataSource.setLogged("user"); + dataSource.setLogged("bobby"); + dataSource.purgeLogged(); + assertThat(dataSource.isLogged("user"), equalTo(false)); + assertThat(dataSource.getLoggedPlayers(), empty()); + } + } diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java index 39769306..8289d837 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -6,6 +6,7 @@ import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.DatabaseSettings; +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.mockito.invocation.InvocationOnMock; @@ -60,7 +61,6 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { @Before public void initializeConnectionAndTable() throws SQLException { - silentClose(hikariSource); HikariConfig config = new HikariConfig(); config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource"); config.setConnectionTestQuery("VALUES 1"); @@ -77,6 +77,11 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { hikariSource = ds; } + @After + public void closeConnection() { + silentClose(hikariSource); + } + @Override protected DataSource getDataSource(String saltColumn) { when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn); diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java index a654fca6..e672eb49 100644 --- a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java @@ -1,11 +1,14 @@ package fr.xephi.authme.datasource; import fr.xephi.authme.TestHelper; +import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.DatabaseSettings; +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -17,6 +20,8 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -61,7 +66,6 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest { @Before public void initializeConnectionAndTable() throws SQLException { - silentClose(con); Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:"); try (Statement st = connection.createStatement()) { st.execute("DROP TABLE IF EXISTS authme"); @@ -72,6 +76,50 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest { con = connection; } + @After + public void closeConnection() { + silentClose(con); + } + + @Test + public void shouldSetUpTableIfMissing() throws SQLException { + // given + Statement st = con.createStatement(); + // table is absent + st.execute("DROP TABLE authme"); + SQLite sqLite = new SQLite(settings, con); + + // when + sqLite.setup(); + + // then + // Save some player to verify database is operational + sqLite.saveAuth(PlayerAuth.builder().name("Name").build()); + assertThat(sqLite.getAllAuths(), hasSize(1)); + } + + @Test + public void shouldCreateMissingColumns() throws SQLException { + // given + Statement st = con.createStatement(); + // drop table and create one with only some of the columns: SQLite doesn't support ALTER TABLE t DROP COLUMN c + st.execute("DROP TABLE authme"); + st.execute("CREATE TABLE authme (" + + "id bigint, " + + "username varchar(255) unique, " + + "password varchar(255) not null, " + + "primary key (id));"); + SQLite sqLite = new SQLite(settings, con); + + // when + sqLite.setup(); + + // then + // Save some player to verify database is operational + sqLite.saveAuth(PlayerAuth.builder().name("Name").build()); + assertThat(sqLite.getAllAuths(), hasSize(1)); + } + @Override protected DataSource getDataSource(String saltColumn) { when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn); diff --git a/src/test/java/fr/xephi/authme/output/MessageKeyTest.java b/src/test/java/fr/xephi/authme/output/MessageKeyTest.java index 101306bf..dda5eeb9 100644 --- a/src/test/java/fr/xephi/authme/output/MessageKeyTest.java +++ b/src/test/java/fr/xephi/authme/output/MessageKeyTest.java @@ -22,12 +22,11 @@ public class MessageKeyTest { // when / then for (MessageKey messageKey : messageKeys) { String key = messageKey.getKey(); - if (keys.contains(key)) { + if (!keys.add(key)) { fail("Found key '" + messageKey.getKey() + "' twice!"); } else if (StringUtils.isEmpty(key)) { fail("Key for message key '" + messageKey + "' is empty"); } - keys.add(key); } } } diff --git a/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java b/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java index 0c2c2b16..3cfa0e27 100644 --- a/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/AdminPermissionTest.java @@ -33,10 +33,9 @@ public class AdminPermissionTest { // when/then for (AdminPermission permission : AdminPermission.values()) { - if (nodes.contains(permission.getNode())) { + if (!nodes.add(permission.getNode())) { fail("More than one enum value defines the node '" + permission.getNode() + "'"); } - nodes.add(permission.getNode()); } } diff --git a/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java b/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java index 632a2cf3..ad8f6fe9 100644 --- a/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/PlayerPermissionTest.java @@ -33,10 +33,9 @@ public class PlayerPermissionTest { // when/then for (PlayerPermission permission : PlayerPermission.values()) { - if (nodes.contains(permission.getNode())) { + if (!nodes.add(permission.getNode())) { fail("More than one enum value defines the node '" + permission.getNode() + "'"); } - nodes.add(permission.getNode()); } } } diff --git a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java b/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java index 94b6e246..6c3e3d16 100644 --- a/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java +++ b/src/test/java/fr/xephi/authme/permission/PlayerStatePermissionTest.java @@ -37,10 +37,9 @@ public class PlayerStatePermissionTest { // when/then for (PlayerStatePermission permission : PlayerStatePermission.values()) { - if (nodes.contains(permission.getNode())) { + if (!nodes.add(permission.getNode())) { fail("More than one enum value defines the node '" + permission.getNode() + "'"); } - nodes.add(permission.getNode()); } } diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java index 79745a33..9f875a00 100644 --- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java @@ -43,10 +43,9 @@ public class HashAlgorithmIntegrationTest { // when / then for (HashAlgorithm algorithm : HashAlgorithm.values()) { if (!HashAlgorithm.CUSTOM.equals(algorithm)) { - if (classes.contains(algorithm.getClazz())) { + if (!classes.add(algorithm.getClazz())) { fail("Found class '" + algorithm.getClazz() + "' twice!"); } - classes.add(algorithm.getClazz()); } } } diff --git a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java index 75c1a56a..9e075200 100644 --- a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java @@ -74,10 +74,9 @@ public class SettingsClassConsistencyTest { if (Property.class.isAssignableFrom(field.getType())) { Property property = (Property) ReflectionTestUtils.getFieldValue(clazz, null, field.getName()); - if (paths.contains(property.getPath())) { + if (!paths.add(property.getPath())) { fail("Path '" + property.getPath() + "' should be used by only one constant"); } - paths.add(property.getPath()); } } }