diff --git a/README.md b/README.md index 5f82e524..934112c0 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ McStats: http://mcstats.org/plugin/AuthMe #####"The best authentication plugin for the Bukkit/Spigot API!" -

Prevent username stealing on your server! Fully compatible with UUIDs and Craftbukkit/Spigot 1.8.X!
+

Prevent username stealing on your server!
Use it to secure your Offline mode server or to increase your Online mode server's protection!

AuthMeReloaded disallows players who aren't authenticated to do actions like placing blocks, moving,
diff --git a/pom.xml b/pom.xml index 94c8b48e..90918139 100644 --- a/pom.xml +++ b/pom.xml @@ -48,22 +48,25 @@ - UTF-8 - + UTF-8 + ${projectEncoding} + ${projectEncoding} + 1.7 + 1.7 + + AuthMe ${pluginName}-${project.version} - fr.xephi.authme.AuthMe + ${project.groupId}.${project.artifactId}.${pluginName} Xephi, sgdc3, DNx5, timvisee, games647, ljacqu Unknown - - 1.7 - - 1.9-pre1-SNAPSHOT + 1.9-SNAPSHOT + jenkins @@ -84,33 +87,21 @@ src/test/java - - . - true - src/main/resources/ - - plugin.yml - - - - . - true - src/main/resources/ - - email.html - welcome.txt - - . false + . + + LICENSE + + + + . + true src/main/resources/ - *.yml + * - - plugin.yml - ./messages/ @@ -128,15 +119,28 @@ + org.apache.maven.plugins maven-compiler-plugin 3.5.1 - 1.7 - ${javaVersion} + ${jdkVersion} + ${jdkVersion} + ${testJreVersion} + ${testJreVersion} + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19.1 + + -Dfile.encoding=${projectEncoding} ${argLine} + + + @@ -161,33 +165,11 @@ - + com.google.gson fr.xephi.authme.libs.google - - org.mcstats - fr.xephi.authme - - com.zaxxer.hikari fr.xephi.authme.libs.hikari @@ -204,6 +186,11 @@ net.ricecode.similarity fr.xephi.authme.libs.similarity + + + org.mcstats + fr.xephi.authme + target/${jarName}-spigot.jar @@ -215,34 +202,12 @@ shade - + com.google fr.xephi.authme.libs.google - - org.mcstats - fr.xephi.authme - - com.zaxxer.hikari fr.xephi.authme.libs.hikari @@ -259,16 +224,22 @@ net.ricecode.similarity fr.xephi.authme.libs.similarity + + + org.mcstats + fr.xephi.authme + target/${jarName}-legacy.jar + org.jacoco jacoco-maven-plugin - 0.7.5.201505241946 + 0.7.6.201602180812 prepare-agent @@ -278,11 +249,12 @@ + org.eluder.coveralls coveralls-maven-plugin 4.1.0 - + @@ -292,7 +264,10 @@ UTF-8 UTF-8 + false true + public + false @@ -305,10 +280,10 @@ https://hub.spigotmc.org/nexus/content/repositories/snapshots - + ess-repo - http://ci.drtshock.net/plugin/repository/everything + http://repo.ess3.net/content/groups/essentials @@ -359,7 +334,7 @@ com.zaxxer HikariCP - 2.4.3 + 2.4.5-SNAPSHOT compile @@ -372,7 +347,7 @@ org.slf4j slf4j-jdk14 - 1.7.16 + 1.7.18 compile true @@ -413,7 +388,7 @@ com.google.code.gson gson - 2.6.1 + 2.6.2 compile true @@ -664,16 +639,20 @@ true - + net.ess3 - EssentialsX - 2.0.1-SNAPSHOT + Essentials + 2.13-SNAPSHOT provided - org.spigotmc - spigot-api + org.bukkit + bukkit + + + org.bukkit + craftbukkit true @@ -683,7 +662,7 @@ net.minelink CombatTagPlus - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT provided diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index bdc1b73e..571fdbbe 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -42,14 +42,15 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.process.Management; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.SHA256; import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.OtherAccounts; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.SettingsMigrationService; import fr.xephi.authme.settings.Spawn; import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -64,7 +65,6 @@ import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Server; -import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -76,6 +76,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; @@ -104,15 +105,14 @@ public class AuthMe extends JavaPlugin { // Private Instances private static AuthMe plugin; private static Server server; - private Management management; - private CommandHandler commandHandler = null; - private PermissionsManager permsMan = null; - private NewSetting newSettings; - private Messages messages; - private JsonCache playerBackup; - private PasswordSecurity passwordSecurity; - private DataSource database; - + /* + * Maps and stuff + * TODO: Clean up and Move into a manager + */ + public final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); + public final ConcurrentHashMap captcha = new ConcurrentHashMap<>(); + public final ConcurrentHashMap cap = new ConcurrentHashMap<>(); + public final ConcurrentHashMap realIp = new ConcurrentHashMap<>(); /* * Public Instances * TODO #432: Encapsulation @@ -120,9 +120,7 @@ public class AuthMe extends JavaPlugin { public NewAPI api; public SendMailSSL mail; public DataManager dataManager; - public OtherAccounts otherAccounts; public Location essentialsSpawn; - /* * Plugin Hooks * TODO: Move into modules @@ -133,15 +131,14 @@ public class AuthMe extends JavaPlugin { public AuthMeInventoryPacketAdapter inventoryProtector; public AuthMeTabCompletePacketAdapter tabComplete; public AuthMeTablistPacketAdapter tablistHider; - - /* - * Maps and stuff - * TODO: Clean up and Move into a manager - */ - public final ConcurrentHashMap sessions = new ConcurrentHashMap<>(); - public final ConcurrentHashMap captcha = new ConcurrentHashMap<>(); - public final ConcurrentHashMap cap = new ConcurrentHashMap<>(); - public final ConcurrentHashMap realIp = new ConcurrentHashMap<>(); + private Management management; + private CommandHandler commandHandler = null; + private PermissionsManager permsMan = null; + private NewSetting newSettings; + private Messages messages; + private JsonCache playerBackup; + private PasswordSecurity passwordSecurity; + private DataSource database; /** * Get the plugin's instance. @@ -259,9 +256,6 @@ public class AuthMe extends JavaPlugin { permsMan = initializePermissionsManager(); commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings); - // Setup otherAccounts file - this.otherAccounts = OtherAccounts.getInstance(); - // Set up Metrics MetricsStarter.setupMetrics(plugin, newSettings); @@ -306,7 +300,8 @@ public class AuthMe extends JavaPlugin { setupApi(); // Set up the management - management = new Management(this, newSettings); + ProcessService processService = new ProcessService(newSettings, messages, this); + management = new Management(this, processService, database, PlayerCache.getInstance()); // Set up the BungeeCord hook setupBungeeCordHook(); @@ -494,11 +489,41 @@ public class AuthMe extends JavaPlugin { if (newSettings != null) { new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP); } + new Thread(new Runnable() { + @Override + public void run() { + List pendingTasks = new ArrayList<>(); + for (BukkitTask pendingTask : getServer().getScheduler().getPendingTasks()) { + if (pendingTask.getOwner().equals(plugin) && !pendingTask.isSync()) { + pendingTasks.add(pendingTask.getTaskId()); + } + } + ConsoleLogger.info("Waiting for " + pendingTasks.size() + " tasks to finish"); + int progress = 0; + for (int taskId : pendingTasks) { + int maxTries = 5; + while (getServer().getScheduler().isCurrentlyRunning(taskId)) { + if (maxTries <= 0) { + ConsoleLogger.info("Async task " + taskId + " times out after to many tries"); + break; + } + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } + maxTries--; + } + + progress++; + ConsoleLogger.info("Progress: " + progress + " / " + pendingTasks.size()); + } + if (database != null) { + database.close(); + } + } + }, "AuthMe-DataSource#close").start(); // Close the database - if (database != null) { - database.close(); - } // Disabled correctly ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); @@ -518,7 +543,10 @@ public class AuthMe extends JavaPlugin { * Sets up the data source. * * @param settings The settings instance + * * @see AuthMe#database + * @throws ClassNotFoundException if no driver could be found for the datasource + * @throws SQLException when initialization of a SQL datasource failed */ public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException { if (this.database != null) { @@ -653,6 +681,7 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); Settings.protectInventoryBeforeLogInEnabled = false; newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false); + newSettings.save(); } return; } @@ -667,14 +696,14 @@ public class AuthMe extends JavaPlugin { if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) { tabComplete = new AuthMeTabCompletePacketAdapter(this); tabComplete.register(); - } else if (inventoryProtector != null) { + } else if (tabComplete != null) { tabComplete.unregister(); tabComplete = null; } if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) { tablistHider = new AuthMeTablistPacketAdapter(this); tablistHider.register(); - } else if (inventoryProtector != null) { + } else if (tablistHider != null) { tablistHider.unregister(); tablistHider = null; } @@ -748,42 +777,6 @@ public class AuthMe extends JavaPlugin { return Spawn.getInstance().getSpawnLocation(player); } - // Return the default spawn point of a world - private Location getDefaultSpawn(World world) { - return world.getSpawnLocation(); - } - - // Return the multiverse spawn point of a world - private Location getMultiverseSpawn(World world) { - if (multiverse != null && Settings.multiverse) { - try { - return multiverse.getMVWorldManager().getMVWorld(world).getSpawnLocation(); - } catch (Exception e) { - e.printStackTrace(); - } - } - return null; - } - - // Return the essentials spawn point - private Location getEssentialsSpawn() { - if (essentialsSpawn != null) { - return essentialsSpawn; - } - return null; - } - - // Return the AuthMe spawn point - private Location getAuthMeSpawn(Player player) { - if ((!database.isAuthAvailable(player.getName().toLowerCase()) || !player.hasPlayedBefore()) - && (Spawn.getInstance().getFirstSpawn() != null)) { - return Spawn.getInstance().getFirstSpawn(); - } else if (Spawn.getInstance().getSpawn() != null) { - return Spawn.getInstance().getSpawn(); - } - return player.getWorld().getSpawnLocation(); - } - private void scheduleRecallEmailTask() { if (!newSettings.getProperty(RECALL_PLAYERS)) { return; @@ -793,7 +786,7 @@ public class AuthMe extends JavaPlugin { public void run() { for (PlayerAuth auth : database.getLoggedPlayers()) { String email = auth.getEmail(); - if (email == null || email.isEmpty() || email.equalsIgnoreCase("your@email.com")) { + if (StringUtils.isEmpty(email) || email.equalsIgnoreCase("your@email.com")) { Player player = Utils.getPlayer(auth.getRealName()); if (player != null) { messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); @@ -801,7 +794,7 @@ public class AuthMe extends JavaPlugin { } } } - }, 1, 1200 * Settings.delayRecall); + }, 1, 1200 * newSettings.getProperty(EmailSettings.DELAY_RECALL)); } public String replaceAllInfo(String message, Player player) { @@ -863,15 +856,6 @@ public class AuthMe extends JavaPlugin { return count >= Settings.getMaxLoginPerIp; } - public boolean hasJoinedIp(String name, String ip) { - int count = 0; - for (Player player : Utils.getOnlinePlayers()) { - if (ip.equalsIgnoreCase(getIP(player)) && !player.getName().equalsIgnoreCase(name)) - count++; - } - return count >= Settings.getMaxJoinPerIp; - } - /** * Handle Bukkit commands. * diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index 03fed1a8..0c22fb13 100644 --- a/src/main/java/fr/xephi/authme/api/NewAPI.java +++ b/src/main/java/fr/xephi/authme/api/NewAPI.java @@ -13,7 +13,10 @@ import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.util.Utils; /** - * The current API of AuthMe. + * The current API of AuthMe. Recommended method of retrieving the API object: + * + * NewAPI authmeApi = NewAPI.getInstance(); + * */ public class NewAPI { @@ -23,7 +26,7 @@ public class NewAPI { /** * Constructor for NewAPI. * - * @param plugin AuthMe + * @param plugin The AuthMe plugin instance */ public NewAPI(AuthMe plugin) { this.plugin = plugin; @@ -32,16 +35,17 @@ public class NewAPI { /** * Constructor for NewAPI. * - * @param server Server + * @param server The server instance */ public NewAPI(Server server) { this.plugin = (AuthMe) server.getPluginManager().getPlugin("AuthMe"); } /** - * Hook into AuthMe + * Get the API object for AuthMe. * - * @return The API object + * @return The API object, or null if the AuthMe plugin instance could not be retrieved + * from the server environment */ public static NewAPI getInstance() { if (singleton != null) { @@ -69,7 +73,6 @@ public class NewAPI { * Return whether the given player is authenticated. * * @param player The player to verify - * * @return true if the player is authenticated */ public boolean isAuthenticated(Player player) { @@ -77,18 +80,22 @@ public class NewAPI { } /** - * @param player a Player + * Check whether the given player is an NPC. * - * @return true if player is a npc + * @param player The player to verify + * @return true if the player is an npc */ public boolean isNPC(Player player) { return Utils.isNPC(player); } /** - * @param player a Player + * Check whether the given player is unrestricted. For such players, AuthMe will not require + * them to authenticate. * + * @param player The player to verify * @return true if the player is unrestricted + * @see fr.xephi.authme.settings.properties.RestrictionSettings#UNRESTRICTED_NAMES */ public boolean isUnrestricted(Player player) { return Utils.isUnrestricted(player); @@ -97,30 +104,21 @@ public class NewAPI { /** * Get the last location of a player. * - * @param player Player The player to process - * + * @param player The player to process * @return Location The location of the player */ public Location getLastLocation(Player player) { - try { - PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName()); - - if (auth != null) { - return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); - } else { - return null; - } - - } catch (NullPointerException ex) { - return null; + PlayerAuth auth = PlayerCache.getInstance().getAuth(player.getName()); + if (auth != null) { + return new Location(Bukkit.getWorld(auth.getWorld()), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ()); } + return null; } /** * Return whether the player is registered. * * @param playerName The player name to check - * * @return true if player is registered, false otherwise */ public boolean isRegistered(String playerName) { @@ -133,7 +131,6 @@ public class NewAPI { * * @param playerName The player to check the password for * @param passwordToCheck The password to check - * * @return true if the password is correct, false otherwise */ public boolean checkPassword(String playerName, String passwordToCheck) { @@ -141,11 +138,10 @@ public class NewAPI { } /** - * Register a player. + * Register a player with the given password. * * @param playerName The player to register * @param password The password to register the player with - * * @return true if the player was registered successfully */ public boolean registerPlayer(String playerName, String password) { @@ -163,7 +159,7 @@ public class NewAPI { } /** - * Force a player to login. + * Force a player to login, i.e. the player is logged in without needing his password. * * @param player The player to log in */ @@ -181,7 +177,7 @@ public class NewAPI { } /** - * Force a player to register. + * Register a player with the given password. * * @param player The player to register * @param password The password to use @@ -191,7 +187,7 @@ public class NewAPI { } /** - * Force a player to unregister. + * Unregister a player from AuthMe. * * @param player The player to unregister */ diff --git a/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java b/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java index 91d6482d..177ccce5 100644 --- a/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java +++ b/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java @@ -8,7 +8,6 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import fr.xephi.authme.ConsoleLogger; @@ -41,14 +40,8 @@ public class JsonCache { return; } - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - - File file = new File(cacheDir, path + File.separator + "cache.json"); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); if (file.exists()) { return; } @@ -61,19 +54,13 @@ public class JsonCache { Files.touch(file); Files.write(data, file, Charsets.UTF_8); } catch (IOException e) { - e.printStackTrace(); + ConsoleLogger.writeStackTrace(e); } } public PlayerData readCache(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - - File file = new File(cacheDir, path + File.separator + "cache.json"); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); if (!file.exists()) { return null; } @@ -81,20 +68,15 @@ public class JsonCache { try { String str = Files.toString(file, Charsets.UTF_8); return gson.fromJson(str, PlayerData.class); - } catch (Exception e) { - e.printStackTrace(); + } catch (IOException e) { + ConsoleLogger.writeStackTrace(e); return null; } } public void removeCache(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - File file = new File(cacheDir, path); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name); if (file.exists()) { purgeDirectory(file); if (!file.delete()) { @@ -104,19 +86,15 @@ public class JsonCache { } public boolean doesCacheExist(Player player) { - String path; - try { - path = player.getUniqueId().toString(); - } catch (Exception | Error e) { - path = player.getName().toLowerCase(); - } - File file = new File(cacheDir, path + File.separator + "cache.json"); + String name = player.getName().toLowerCase(); + File file = new File(cacheDir, name + File.separator + "cache.json"); return file.exists(); } private class PlayerDataDeserializer implements JsonDeserializer { @Override - public PlayerData deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + public PlayerData deserialize(JsonElement jsonElement, Type type, + JsonDeserializationContext context) { JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject == null) { return null; @@ -143,7 +121,7 @@ public class JsonCache { private class PlayerDataSerializer implements JsonSerializer { @Override public JsonElement serialize(PlayerData playerData, Type type, - JsonSerializationContext jsonSerializationContext) { + JsonSerializationContext context) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("group", playerData.getGroup()); jsonObject.addProperty("operator", playerData.getOperator()); diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 0f73effa..ca89c86a 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -92,7 +92,7 @@ public final class CommandInitializer { .description("Enforce login player") .detailedDescription("Enforce the specified player to login.") .withArgument("player", "Online player name", true) - .permissions(OP_ONLY, PlayerPermission.CAN_LOGIN_BE_FORCED) + .permissions(OP_ONLY, AdminPermission.FORCE_LOGIN) .executableCommand(new ForceLoginCommand()) .build(); 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 7b929952..a33f3eb3 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 @@ -22,8 +22,8 @@ public class RegisterAdminCommand implements ExecutableCommand { public void executeCommand(final CommandSender sender, List arguments, final CommandService commandService) { // Get the player name and password - final String playerName = arguments.get(0).toLowerCase(); - final String playerPass = arguments.get(1).toLowerCase(); + final String playerName = arguments.get(0); + final String playerPass = arguments.get(1); final String playerNameLowerCase = playerName.toLowerCase(); final String playerPassLowerCase = playerPass.toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java index dc5cd471..9ed65b0b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java @@ -1,14 +1,5 @@ package fr.xephi.authme.command.executable.authme; -import java.util.List; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; @@ -20,6 +11,14 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +import java.util.List; /** * Admin command to unregister a player. @@ -55,19 +54,20 @@ public class UnregisterAdminCommand implements ExecutableCommand { if (target != null && target.isOnline()) { Utils.teleportToSpawn(target); LimboCache.getInstance().addLimboPlayer(target); - int delay = Settings.getRegistrationTimeout * 20; + int timeOut = Settings.getRegistrationTimeout * 20; int interval = Settings.getWarnMessageInterval; BukkitScheduler scheduler = sender.getServer().getScheduler(); - if (delay != 0) { - BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), delay); + if (timeOut != 0) { + BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), timeOut); LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTaskId(id); } LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId( - scheduler.runTaskAsynchronously(plugin, - new MessageTask(plugin, playerNameLowerCase, commandService.retrieveMessage(MessageKey.REGISTER_MESSAGE), interval))); + scheduler.runTask( + plugin, new MessageTask(plugin, playerNameLowerCase, MessageKey.REGISTER_MESSAGE, interval) + ) + ); if (Settings.applyBlindEffect) { - target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, - Settings.getRegistrationTimeout * 20, 2)); + target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); } commandService.send(target, MessageKey.UNREGISTERED_SUCCESS); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java index bfbb2ebf..4a7b629c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java @@ -3,14 +3,14 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; +import static fr.xephi.authme.settings.properties.PluginSettings.HELP_HEADER; +import fr.xephi.authme.util.Utils; import java.util.List; -import static fr.xephi.authme.settings.properties.PluginSettings.HELP_HEADER; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; public class VersionCommand implements ExecutableCommand { @@ -73,7 +73,7 @@ public class VersionCommand implements ExecutableCommand { private static boolean isPlayerOnline(String minecraftName) { // Note ljacqu 20151121: Generally you should use Utils#getOnlinePlayers to retrieve the list of online players. // If it's only used in a for-each loop such as here, it's fine. For other purposes, go through the Utils class. - for (Player player : Bukkit.getOnlinePlayers()) { + for (Player player : Utils.getOnlinePlayers()) { if (player.getName().equalsIgnoreCase(minecraftName)) { return true; } diff --git a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java index c87297d8..7919f6a0 100644 --- a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java +++ b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java @@ -3,39 +3,51 @@ 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; -import java.sql.SQLException; +import fr.xephi.authme.datasource.FlatFile; +import fr.xephi.authme.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; /** * Mandatory migration from the deprecated flat file datasource to SQLite. */ -public class ForceFlatToSqlite { +public class ForceFlatToSqlite implements Converter { - private final DataSource database; - private final NewSetting settings; + private final DataSource source; + private final DataSource destination; - public ForceFlatToSqlite(DataSource database, NewSetting settings) { - this.database = database; - this.settings = settings; + /** + * Constructor. + * + * @param source The datasource to convert (flatfile) + * @param destination The datasource to copy the data to (sqlite) + */ + public ForceFlatToSqlite(FlatFile source, DataSource destination) { + this.source = source; + this.destination = destination; } - public DataSource run() { - try { - DataSource sqlite = new SQLite(settings); - for (PlayerAuth auth : database.getAllAuths()) { - auth.setRealName("Player"); - sqlite.saveAuth(auth); + /** + * Perform the conversion. + */ + @Override + public void run() { + List skippedPlayers = new ArrayList<>(); + for (PlayerAuth auth : source.getAllAuths()) { + if (destination.isAuthAvailable(auth.getNickname())) { + skippedPlayers.add(auth.getNickname()); + } else { + destination.saveAuth(auth); + destination.updateQuitLoc(auth); } - settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE); - settings.save(); - ConsoleLogger.info("Database successfully converted to sqlite!"); - return sqlite; - } catch (SQLException | ClassNotFoundException e) { - ConsoleLogger.logException("Could not convert from Flatfile to SQLite:", e); } - return null; + + if (!skippedPlayers.isEmpty()) { + ConsoleLogger.showError("Warning: skipped conversion for players which were already in SQLite: " + + StringUtils.join(", ", skippedPlayers)); + } + ConsoleLogger.info("Database successfully converted from " + source.getClass().getSimpleName() + + " to " + destination.getClass().getSimpleName()); } } diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 2ef221f8..ad02c99f 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -4,14 +4,19 @@ import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.security.crypts.HashedPassword; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** @@ -20,6 +25,7 @@ public class CacheDataSource implements DataSource { private final DataSource source; private final LoadingCache> cachedAuths; + private final ListeningExecutorService executorService; /** * Constructor for CacheDataSource. @@ -27,25 +33,35 @@ public class CacheDataSource implements DataSource { * @param src DataSource */ public CacheDataSource(DataSource src) { - this.source = src; - this.cachedAuths = CacheBuilder.newBuilder() - .expireAfterWrite(8, TimeUnit.MINUTES) - .removalListener(new RemovalListener>() { + source = src; + executorService = MoreExecutors.listeningDecorator( + Executors.newCachedThreadPool(new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("AuthMe-CacheLoader") + .build()) + ); + cachedAuths = CacheBuilder.newBuilder() + .refreshAfterWrite(8, TimeUnit.MINUTES) + .build(new CacheLoader>() { @Override - public void onRemoval(RemovalNotification> removalNotification) { - String name = removalNotification.getKey(); - if (PlayerCache.getInstance().isAuthenticated(name)) { - cachedAuths.getUnchecked(name); - } + public Optional load(String key) { + return Optional.fromNullable(source.getAuth(key)); } - }) - .build( - new CacheLoader>() { - @Override - public Optional load(String key) { - return Optional.fromNullable(source.getAuth(key)); - } - }); + + @Override + public ListenableFuture> reload(final String key, Optional oldValue) { + return executorService.submit(new Callable>() { + @Override + public Optional call() { + return load(key); + } + }); + } + }); + } + + public LoadingCache> getCachedAuths() { + return cachedAuths; } @Override @@ -137,6 +153,13 @@ public class CacheDataSource implements DataSource { @Override public synchronized void close() { source.close(); + cachedAuths.invalidateAll(); + executorService.shutdown(); + try { + executorService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + ConsoleLogger.writeStackTrace(e); + } } @Override @@ -201,12 +224,6 @@ public class CacheDataSource implements DataSource { return source.getAccountsRegistered(); } - @Override - public void updateName(final String oldOne, final String newOne) { // unused method - source.updateName(oldOne, newOne); - cachedAuths.invalidate(oldOne); - } - @Override public boolean updateRealName(String user, String realName) { boolean result = source.updateRealName(user, realName); diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index becccb6a..6e3bc09b 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -169,14 +169,6 @@ public interface DataSource { */ int getAccountsRegistered(); - /** - * Method updateName. - * - * @param oldOne String - * @param newOne String - */ - void updateName(String oldOne, String newOne); - boolean updateRealName(String user, String realName); boolean updateIp(String user, String ip); diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index 7c4a92d4..72b30184 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -1,5 +1,6 @@ package fr.xephi.authme.datasource; +import com.google.common.annotations.VisibleForTesting; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; @@ -42,7 +43,7 @@ public class FlatFile implements DataSource { try { source.createNewFile(); } catch (IOException e) { - ConsoleLogger.showError(e.getMessage()); + ConsoleLogger.logException("Cannot open flatfile", e); if (Settings.isStopEnabled) { ConsoleLogger.showError("Can't use FLAT FILE... SHUTDOWN..."); instance.getServer().shutdown(); @@ -50,10 +51,14 @@ public class FlatFile implements DataSource { if (!Settings.isStopEnabled) { instance.getServer().getPluginManager().disablePlugin(instance); } - e.printStackTrace(); } } + @VisibleForTesting + public FlatFile(File source) { + this.source = source; + } + @Override public synchronized boolean isAuthAvailable(String user) { BufferedReader br = null; @@ -599,17 +604,9 @@ public class FlatFile implements DataSource { return result; } - @Override - public void updateName(String oldOne, String newOne) { - PlayerAuth auth = this.getAuth(oldOne); - auth.setNickname(newOne); - this.saveAuth(auth); - this.removeAuth(oldOne); - } - @Override public boolean updateRealName(String user, String realName) { - return false; + throw new UnsupportedOperationException("Flat file no longer supported"); } @Override @@ -626,33 +623,25 @@ public class FlatFile implements DataSource { String line; while ((line = br.readLine()) != null) { String[] args = line.split(":"); - switch (args.length) { - case 2: - auths.add(new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0])); - break; - case 3: - auths.add(new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0])); - break; - case 4: - auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0])); - break; - case 7: - auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "unavailableworld", "your@email.com", args[0])); - break; - case 8: - auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0])); - break; - case 9: - auths.add(new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0])); - break; + // We expect to encounter 2, 3, 4, 7, 8 or 9 fields. Ignore the line otherwise + if (args.length >= 2 && args.length != 5 && args.length != 6 && args.length <= 9) { + PlayerAuth.Builder builder = PlayerAuth.builder() + .name(args[0]).realName(args[0]) + .password(args[1], null); + if (args.length >= 3) builder.ip(args[2]); + if (args.length >= 4) builder.lastLogin(Long.parseLong(args[3])); + if (args.length >= 7) { + builder.locX(Double.parseDouble(args[4])) + .locY(Double.parseDouble(args[5])) + .locZ(Double.parseDouble(args[6])); + } + if (args.length >= 8) builder.locWorld(args[7]); + if (args.length >= 9) builder.email(args[8]); + auths.add(builder.build()); } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return auths; } catch (IOException ex) { - ConsoleLogger.showError(ex.getMessage()); - return auths; + ConsoleLogger.logException("Error while getting auths from flatfile:", ex); } finally { if (br != null) { try { @@ -666,7 +655,7 @@ public class FlatFile implements DataSource { @Override public List getLoggedPlayers() { - return new ArrayList<>(); + throw new UnsupportedOperationException("Flat file no longer supported"); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index e7d5c565..84927fe6 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -1,5 +1,6 @@ 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.AuthMe; @@ -41,6 +42,10 @@ public class MySQL implements DataSource { private final HashAlgorithm hashAlgorithm; private HikariDataSource ds; + private final String phpBbPrefix; + private final int phpBbGroup; + private final String wordpressPrefix; + public MySQL(NewSetting settings) throws ClassNotFoundException, SQLException, PoolInitializationException { this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST); this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT); @@ -51,6 +56,9 @@ public class MySQL implements DataSource { this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS); this.col = new Columns(settings); this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); + this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX); + this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID); + this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX); // Set the connection arguments (and check if connection is ok) try { @@ -81,6 +89,7 @@ public class MySQL implements DataSource { } } + @VisibleForTesting MySQL(NewSetting settings, HikariDataSource hikariDataSource) { this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST); this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT); @@ -91,6 +100,9 @@ public class MySQL implements DataSource { this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS); this.col = new Columns(settings); this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); + this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX); + this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID); + this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX); ds = hikariDataSource; } @@ -359,10 +371,10 @@ public class MySQL implements DataSource { if (rs.next()) { int id = rs.getInt(col.ID); // Insert player in phpbb_user_group - sql = "INSERT INTO " + Settings.getPhpbbPrefix + sql = "INSERT INTO " + phpBbPrefix + "user_group (group_id, user_id, group_leader, user_pending) VALUES (?,?,?,?);"; pst2 = con.prepareStatement(sql); - pst2.setInt(1, Settings.getPhpbbGroup); + pst2.setInt(1, phpBbGroup); pst2.setInt(2, id); pst2.setInt(3, 0); pst2.setInt(4, 0); @@ -380,7 +392,7 @@ public class MySQL implements DataSource { sql = "UPDATE " + tableName + " SET " + tableName + ".group_id=? WHERE " + col.NAME + "=?;"; pst2 = con.prepareStatement(sql); - pst2.setInt(1, Settings.getPhpbbGroup); + pst2.setInt(1, phpBbGroup); pst2.setString(2, auth.getNickname()); pst2.executeUpdate(); pst2.close(); @@ -403,7 +415,7 @@ public class MySQL implements DataSource { pst2.executeUpdate(); pst2.close(); // Increment num_users - sql = "UPDATE " + Settings.getPhpbbPrefix + sql = "UPDATE " + phpBbPrefix + "config SET config_value = config_value + 1 WHERE config_name = 'num_users';"; pst2 = con.prepareStatement(sql); pst2.executeUpdate(); @@ -417,7 +429,7 @@ public class MySQL implements DataSource { rs = pst.executeQuery(); if (rs.next()) { int id = rs.getInt(col.ID); - sql = "INSERT INTO " + Settings.getWordPressPrefix + "usermeta (user_id, meta_key, meta_value) VALUES (?,?,?);"; + sql = "INSERT INTO " + wordpressPrefix + "usermeta (user_id, meta_key, meta_value) VALUES (?,?,?);"; pst2 = con.prepareStatement(sql); // First Name pst2.setInt(1, id); @@ -620,31 +632,32 @@ public class MySQL implements DataSource { @Override public synchronized boolean removeAuth(String user) { user = user.toLowerCase(); - try (Connection con = getConnection()) { - String sql; - PreparedStatement pst; + String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; + PreparedStatement xfSelect = null; + PreparedStatement xfDelete = null; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { sql = "SELECT " + col.ID + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; - pst = con.prepareStatement(sql); - pst.setString(1, user); - ResultSet rs = pst.executeQuery(); - if (rs.next()) { - int id = rs.getInt(col.ID); - sql = "DELETE FROM xf_user_authenticate WHERE " + col.ID + "=?;"; - PreparedStatement st = con.prepareStatement(sql); - st.setInt(1, id); - st.executeUpdate(); - st.close(); + xfSelect = con.prepareStatement(sql); + xfSelect.setString(1, user); + try (ResultSet rs = xfSelect.executeQuery()) { + if (rs.next()) { + int id = rs.getInt(col.ID); + sql = "DELETE FROM xf_user_authenticate WHERE " + col.ID + "=?;"; + xfDelete = con.prepareStatement(sql); + xfDelete.setInt(1, id); + xfDelete.executeUpdate(); + } } - rs.close(); - pst.close(); } - pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); pst.setString(1, user); pst.executeUpdate(); return true; } catch (SQLException ex) { logSqlException(ex); + } finally { + close(xfSelect); + close(xfDelete); } return false; } @@ -817,18 +830,6 @@ public class MySQL implements DataSource { return result; } - @Override - public void updateName(String oldOne, String newOne) { - String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;"; - try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { - pst.setString(1, newOne); - pst.setString(2, oldOne); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } - } - @Override public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; @@ -891,22 +892,24 @@ public class MySQL implements DataSource { @Override public List getLoggedPlayers() { List auths = new ArrayList<>(); - try (Connection con = getConnection()) { - Statement st = con.createStatement(); - ResultSet rs = st.executeQuery("SELECT * FROM " + tableName + " WHERE " + col.IS_LOGGED + "=1;"); - PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;"); + String sql = "SELECT * FROM " + tableName + " WHERE " + col.IS_LOGGED + "=1;"; + try (Connection con = getConnection(); + Statement st = con.createStatement(); + ResultSet rs = st.executeQuery(sql)) { while (rs.next()) { PlayerAuth pAuth = buildAuthFromResultSet(rs); if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { - int id = rs.getInt(col.ID); - pst.setInt(1, id); - ResultSet rs2 = pst.executeQuery(); - if (rs2.next()) { - Blob blob = rs2.getBlob("data"); - byte[] bytes = blob.getBytes(1, (int) blob.length()); - pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + try (PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) { + int id = rs.getInt(col.ID); + pst.setInt(1, id); + ResultSet rs2 = pst.executeQuery(); + if (rs2.next()) { + Blob blob = rs2.getBlob("data"); + byte[] bytes = blob.getBytes(1, (int) blob.length()); + pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); + } + rs2.close(); } - rs2.close(); } auths.add(pAuth); } @@ -990,12 +993,22 @@ public class MySQL implements DataSource { } private static void close(ResultSet rs) { - if (rs != null) { - try { + try { + if (rs != null && !rs.isClosed()) { rs.close(); - } catch (SQLException e) { - ConsoleLogger.logException("Could not close ResultSet", e); } + } catch (SQLException e) { + ConsoleLogger.logException("Could not close ResultSet", e); + } + } + + private static void close(PreparedStatement pst) { + try { + if (pst != null && !pst.isClosed()) { + pst.close(); + } + } catch (SQLException e) { + ConsoleLogger.logException("Could not close PreparedStatement", e); } } diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index f7f18b32..2868a59a 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -30,8 +30,9 @@ public class SQLite implements DataSource { /** * Constructor for SQLite. * - * @throws ClassNotFoundException Exception - * @throws SQLException Exception + * @param settings The settings instance + * @throws ClassNotFoundException if no driver could be found for the datasource + * @throws SQLException when initialization of a SQL datasource failed */ public SQLite(NewSetting settings) throws ClassNotFoundException, SQLException { this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); @@ -350,8 +351,9 @@ public class SQLite implements DataSource { @Override public synchronized void close() { try { - if (con != null && !con.isClosed()) - con.close(); + if (con != null && !con.isClosed()) { + con.close(); + } } catch (SQLException ex) { logSqlException(ex); } @@ -421,17 +423,14 @@ public class SQLite implements DataSource { @Override public void purgeBanned(List banned) { - PreparedStatement pst = null; - try { + String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"; + try (PreparedStatement pst = con.prepareStatement(sql)) { for (String name : banned) { - pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); pst.setString(1, name); pst.executeUpdate(); } } catch (SQLException ex) { logSqlException(ex); - } finally { - close(pst); } } @@ -507,37 +506,17 @@ public class SQLite implements DataSource { @Override public int getAccountsRegistered() { - PreparedStatement pst = null; - ResultSet rs; - try { - pst = con.prepareStatement("SELECT COUNT(*) FROM " + tableName + ";"); - rs = pst.executeQuery(); - if (rs != null && rs.next()) { + String sql = "SELECT COUNT(*) FROM " + tableName + ";"; + try (PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery()) { + if (rs.next()) { return rs.getInt(1); } } catch (SQLException ex) { logSqlException(ex); - } finally { - close(pst); } return 0; } - @Override - public void updateName(String oldOne, String newOne) { - PreparedStatement pst = null; - try { - pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;"); - pst.setString(1, newOne); - pst.setString(2, oldOne); - pst.executeUpdate(); - } catch (SQLException ex) { - logSqlException(ex); - } finally { - close(pst); - } - } - @Override public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; @@ -569,19 +548,14 @@ public class SQLite implements DataSource { @Override public List getAllAuths() { List auths = new ArrayList<>(); - PreparedStatement pst = null; - ResultSet rs; - try { - pst = con.prepareStatement("SELECT * FROM " + tableName + ";"); - rs = pst.executeQuery(); + String sql = "SELECT * FROM " + tableName + ";"; + try (PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery()) { while (rs.next()) { PlayerAuth auth = buildAuthFromResultSet(rs); auths.add(auth); } } catch (SQLException ex) { logSqlException(ex); - } finally { - close(pst); } return auths; } @@ -589,19 +563,14 @@ public class SQLite implements DataSource { @Override public List getLoggedPlayers() { List auths = new ArrayList<>(); - PreparedStatement pst = null; - ResultSet rs; - try { - pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.IS_LOGGED + "=1;"); - rs = pst.executeQuery(); + String sql = "SELECT * FROM " + tableName + " WHERE " + col.IS_LOGGED + "=1;"; + try (PreparedStatement pst = con.prepareStatement(sql); ResultSet rs = pst.executeQuery()) { while (rs.next()) { PlayerAuth auth = buildAuthFromResultSet(rs); auths.add(auth); } } catch (SQLException ex) { logSqlException(ex); - } finally { - close(pst); } return auths; } diff --git a/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java b/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java index 963593fd..b2a5819a 100644 --- a/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java +++ b/src/main/java/fr/xephi/authme/events/AuthMeAsyncPreLoginEvent.java @@ -6,7 +6,7 @@ import org.bukkit.event.HandlerList; /** * This event is called when a player uses the /login command with correct credentials. - * {@link #setCanLogin(boolean) {@code event.setCanLogin(false)}} prevents the player from logging in. + * {@link #setCanLogin(boolean) event.setCanLogin(false)} prevents the player from logging in. */ public class AuthMeAsyncPreLoginEvent extends CustomEvent { diff --git a/src/main/java/fr/xephi/authme/output/MessageKey.java b/src/main/java/fr/xephi/authme/output/MessageKey.java index 45cdc0be..3fd7c747 100644 --- a/src/main/java/fr/xephi/authme/output/MessageKey.java +++ b/src/main/java/fr/xephi/authme/output/MessageKey.java @@ -41,7 +41,7 @@ public enum MessageKey { REGISTER_EMAIL_MESSAGE("reg_email_msg"), - MAX_REGISTER_EXCEEDED("max_reg"), + MAX_REGISTER_EXCEEDED("max_reg", "%max_acc", "%reg_count", "%reg_names"), USAGE_REGISTER("usage_reg"), diff --git a/src/main/java/fr/xephi/authme/output/Messages.java b/src/main/java/fr/xephi/authme/output/Messages.java index 9ea87f44..cf029275 100644 --- a/src/main/java/fr/xephi/authme/output/Messages.java +++ b/src/main/java/fr/xephi/authme/output/Messages.java @@ -96,6 +96,7 @@ public class Messages { * * @param key The key of the message to send * @param replacements The replacements to apply for the tags + * @return The message from the file with replacements */ public String retrieveSingle(MessageKey key, String... replacements) { String message = retrieveSingle(key); @@ -111,7 +112,7 @@ public class Messages { } /** - * Reload the messages manager. + * Reset the messages manager to retrieve messages from the given file instead of the current one. * * @param messagesFile The new file to load messages from */ diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 597d927e..a42884ae 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -2,6 +2,7 @@ package fr.xephi.authme.process; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.process.email.AsyncAddEmail; import fr.xephi.authme.process.email.AsyncChangeEmail; import fr.xephi.authme.process.join.AsynchronousJoin; @@ -20,18 +21,25 @@ public class Management { private final AuthMe plugin; private final BukkitScheduler sched; + private final ProcessService processService; + private final DataSource dataSource; + private final PlayerCache playerCache; private final NewSetting settings; /** * Constructor for Management. * * @param plugin AuthMe - * @param settings The plugin settings */ - public Management(AuthMe plugin, NewSetting settings) { + public Management(AuthMe plugin, ProcessService processService, DataSource dataSource, PlayerCache playerCache) { this.plugin = plugin; this.sched = this.plugin.getServer().getScheduler(); - this.settings = settings; + this.processService = processService; + this.dataSource = dataSource; + this.playerCache = playerCache; + + // FIXME don't pass settings anymore -> go through the service in the processes + this.settings = processService.getSettings(); } public void performLogin(final Player player, final String password, final boolean forceLogin) { @@ -39,7 +47,7 @@ public class Management { @Override public void run() { - new AsynchronousLogin(player, password, forceLogin, plugin, plugin.getDataSource(), settings) + new AsynchronousLogin(player, password, forceLogin, plugin, dataSource, settings) .process(); } }); @@ -60,7 +68,7 @@ public class Management { @Override public void run() { - new AsyncRegister(player, password, email, plugin, plugin.getDataSource(), settings).process(); + new AsyncRegister(player, password, email, plugin, dataSource, settings).process(); } }); } @@ -76,14 +84,7 @@ public class Management { } public void performJoin(final Player player) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsynchronousJoin(player, plugin, plugin.getDataSource()).process(); - } - - }); + runTask(new AsynchronousJoin(player, plugin, dataSource, playerCache, processService)); } public void performQuit(final Player player, final boolean isKick) { @@ -91,28 +92,26 @@ public class Management { @Override public void run() { - new AsynchronousQuit(player, plugin, plugin.getDataSource(), isKick).process(); + new AsynchronousQuit(player, plugin, dataSource, isKick).process(); } }); } public void performAddEmail(final Player player, final String newEmail) { - sched.runTaskAsynchronously(plugin, new Runnable() { - @Override - public void run() { - new AsyncAddEmail(player, plugin, newEmail, plugin.getDataSource(), - PlayerCache.getInstance(), settings).process(); - } - }); + runTask(new AsyncAddEmail(player, newEmail, dataSource, playerCache, processService)); } public void performChangeEmail(final Player player, final String oldEmail, final String newEmail) { sched.runTaskAsynchronously(plugin, new Runnable() { @Override public void run() { - new AsyncChangeEmail(player, plugin, oldEmail, newEmail, plugin.getDataSource(), PlayerCache.getInstance(), settings).process(); + new AsyncChangeEmail(player, plugin, oldEmail, newEmail, dataSource, playerCache, settings).process(); } }); } + + private void runTask(Process process) { + sched.runTaskAsynchronously(plugin, process); + } } diff --git a/src/main/java/fr/xephi/authme/process/Process.java b/src/main/java/fr/xephi/authme/process/Process.java new file mode 100644 index 00000000..d6efd2b2 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/Process.java @@ -0,0 +1,8 @@ +package fr.xephi.authme.process; + +/** + * Common interface for AuthMe processes. + */ +public interface Process extends Runnable { + +} diff --git a/src/main/java/fr/xephi/authme/process/ProcessService.java b/src/main/java/fr/xephi/authme/process/ProcessService.java new file mode 100644 index 00000000..e52e4235 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/ProcessService.java @@ -0,0 +1,63 @@ +package fr.xephi.authme.process; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.domain.Property; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Event; +import org.bukkit.scheduler.BukkitTask; + +/** + * Service for asynchronous and synchronous processes. + */ +public class ProcessService { + + private final NewSetting settings; + private final Messages messages; + private final AuthMe authMe; + + public ProcessService(NewSetting settings, Messages messages, AuthMe authMe) { + this.settings = settings; + this.messages = messages; + this.authMe = authMe; + } + + public T getProperty(Property property) { + return settings.getProperty(property); + } + + public NewSetting getSettings() { + return settings; + } + + public void send(CommandSender sender, MessageKey key) { + messages.send(sender, key); + } + + public String retrieveMessage(MessageKey key) { + return messages.retrieveSingle(key); + } + + public BukkitTask runTask(Runnable task) { + return authMe.getServer().getScheduler().runTask(authMe, task); + } + + public BukkitTask runTaskLater(Runnable task, long delay) { + return authMe.getServer().getScheduler().runTaskLater(authMe, task, delay); + } + + public int scheduleSyncDelayedTask(Runnable task) { + return authMe.getServer().getScheduler().scheduleSyncDelayedTask(authMe, task); + } + + public void callEvent(Event event) { + authMe.getServer().getPluginManager().callEvent(event); + } + + public AuthMe getAuthMe() { + return authMe; + } + +} diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java index d5908820..d1e49b93 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java @@ -1,40 +1,38 @@ package fr.xephi.authme.process.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.datasource.DataSource; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; -import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; /** * Async task to add an email to an account. */ -public class AsyncAddEmail { +public class AsyncAddEmail implements Process { private final Player player; private final String email; - private final Messages messages; + private final ProcessService service; private final DataSource dataSource; private final PlayerCache playerCache; - private final NewSetting settings; - public AsyncAddEmail(Player player, AuthMe plugin, String email, DataSource dataSource, - PlayerCache playerCache, NewSetting settings) { - this.messages = plugin.getMessages(); + public AsyncAddEmail(Player player, String email, DataSource dataSource, PlayerCache playerCache, + ProcessService service) { this.player = player; this.email = email; this.dataSource = dataSource; this.playerCache = playerCache; - this.settings = settings; + this.service = service; } - public void process() { + @Override + public void run() { String playerName = player.getName().toLowerCase(); if (playerCache.isAuthenticated(playerName)) { @@ -42,19 +40,19 @@ public class AsyncAddEmail { final String currentEmail = auth.getEmail(); if (currentEmail != null && !"your@email.com".equals(currentEmail)) { - messages.send(player, MessageKey.USAGE_CHANGE_EMAIL); - } else if (!Utils.isEmailCorrect(email, settings)) { - messages.send(player, MessageKey.INVALID_EMAIL); + service.send(player, MessageKey.USAGE_CHANGE_EMAIL); + } else if (!Utils.isEmailCorrect(email, service.getSettings())) { + service.send(player, MessageKey.INVALID_EMAIL); } else if (dataSource.isEmailStored(email)) { - messages.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); + service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { auth.setEmail(email); if (dataSource.updateEmail(auth)) { playerCache.updatePlayer(auth); - messages.send(player, MessageKey.EMAIL_ADDED_SUCCESS); + service.send(player, MessageKey.EMAIL_ADDED_SUCCESS); } else { ConsoleLogger.showError("Could not save email for player '" + player + "'"); - messages.send(player, MessageKey.ERROR); + service.send(player, MessageKey.ERROR); } } } else { @@ -64,11 +62,11 @@ public class AsyncAddEmail { private void sendUnloggedMessage(DataSource dataSource) { if (dataSource.isAuthAvailable(player.getName())) { - messages.send(player, MessageKey.LOGIN_MESSAGE); - } else if (Settings.emailRegistration) { - messages.send(player, MessageKey.REGISTER_EMAIL_MESSAGE); + service.send(player, MessageKey.LOGIN_MESSAGE); + } else if (service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)) { + service.send(player, MessageKey.REGISTER_EMAIL_MESSAGE); } else { - messages.send(player, MessageKey.REGISTER_MESSAGE); + service.send(player, MessageKey.REGISTER_MESSAGE); } } diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index f738268e..455c7683 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -11,66 +11,71 @@ import fr.xephi.authme.events.ProtectInventoryEvent; import fr.xephi.authme.events.SpawnTeleportEvent; import fr.xephi.authme.listener.AuthMePlayerListener; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Spawn; +import fr.xephi.authme.settings.properties.HooksSettings; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils.GroupType; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; /** */ -public class AsynchronousJoin { +public class AsynchronousJoin implements Process { private final AuthMe plugin; private final Player player; private final DataSource database; private final String name; - private final Messages m; - private final BukkitScheduler sched; + private final ProcessService service; + private final PlayerCache playerCache; - public AsynchronousJoin(Player player, AuthMe plugin, DataSource database) { - this.m = plugin.getMessages(); + public AsynchronousJoin(Player player, AuthMe plugin, DataSource database, PlayerCache playerCache, + ProcessService service) { this.player = player; this.plugin = plugin; - this.sched = plugin.getServer().getScheduler(); this.database = database; this.name = player.getName().toLowerCase(); + this.service = service; + this.playerCache = playerCache; } - public void process() { + @Override + public void run() { if (Utils.isUnrestricted(player)) { return; } - if (Settings.checkVeryGames) { + if (service.getProperty(HooksSettings.ENABLE_VERYGAMES_IP_CHECK)) { plugin.getVerygamesIp(player); } - if (plugin.ess != null && Settings.disableSocialSpy) { + if (plugin.ess != null && service.getProperty(HooksSettings.DISABLE_SOCIAL_SPY)) { plugin.ess.getUser(player).setSocialSpyEnabled(false); } final String ip = plugin.getIP(player); - if (Settings.isAllowRestrictedIp && isNameRestricted(name, ip, player.getAddress().getHostName())) { - sched.scheduleSyncDelayedTask(plugin, new Runnable() { - + if (isNameRestricted(name, ip, player.getAddress().getHostName(), service.getSettings())) { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true); - player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR)); + player.kickPlayer(service.retrieveMessage(MessageKey.NOT_OWNER_ERROR)); if (Settings.banUnsafeIp) { plugin.getServer().banIP(ip); } @@ -78,38 +83,34 @@ public class AsynchronousJoin { }); return; } - if (Settings.getMaxJoinPerIp > 0 + if (service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP) > 0 && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - && !ip.equalsIgnoreCase("127.0.0.1") - && !ip.equalsIgnoreCase("localhost") - && plugin.hasJoinedIp(player.getName(), ip)) { - sched.scheduleSyncDelayedTask(plugin, new Runnable() { - + && !"127.0.0.1".equalsIgnoreCase(ip) + && !"localhost".equalsIgnoreCase(ip) + && hasJoinedIp(player.getName(), ip, service.getSettings(), service.getAuthMe())) { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { player.kickPlayer("A player with the same IP is already in game!"); } - }); return; } - final Location spawnLoc = plugin.getSpawnLocation(player); + final Location spawnLoc = Spawn.getInstance().getSpawnLocation(player); final boolean isAuthAvailable = database.isAuthAvailable(name); if (isAuthAvailable) { if (!Settings.noTeleport) { if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - sched.scheduleSyncDelayedTask(plugin, new Runnable() { - + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); - plugin.getServer().getPluginManager().callEvent(tpEvent); + SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, playerCache.isAuthenticated(name)); + service.callEvent(tpEvent); if (!tpEvent.isCancelled() && player.isOnline() && tpEvent.getTo() != null && tpEvent.getTo().getWorld() != null) { player.teleport(tpEvent.getTo()); } } - }); } } @@ -123,12 +124,12 @@ public class AsynchronousJoin { if (ev.isCancelled()) { plugin.inventoryProtector.sendInventoryPacket(player); if (!Settings.noConsoleSpam) { - ConsoleLogger.info("ProtectInventoryEvent has been cancelled for " + player.getName() + " ..."); + ConsoleLogger.info("ProtectInventoryEvent has been cancelled for " + player.getName() + "..."); } } } - if (Settings.isSessionsEnabled && (PlayerCache.getInstance().isAuthenticated(name) || database.isLogged(name))) { + if (service.getProperty(PluginSettings.SESSIONS_ENABLED) && (playerCache.isAuthenticated(name) || database.isLogged(name))) { if (plugin.sessions.containsKey(name)) { plugin.sessions.get(name).cancel(); plugin.sessions.remove(name); @@ -137,11 +138,11 @@ public class AsynchronousJoin { database.setUnlogged(name); PlayerCache.getInstance().removePlayer(name); if (auth != null && auth.getIp().equals(ip)) { - m.send(player, MessageKey.SESSION_RECONNECTION); + service.send(player, MessageKey.SESSION_RECONNECTION); plugin.getManagement().performLogin(player, "dontneed", true); return; } else if (Settings.sessionExpireOnIpChange) { - m.send(player, MessageKey.SESSION_EXPIRED); + service.send(player, MessageKey.SESSION_EXPIRED); } } } else { @@ -154,21 +155,18 @@ public class AsynchronousJoin { if (!Settings.noTeleport && !needFirstSpawn() && Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) { - sched.scheduleSyncDelayedTask(plugin, new Runnable() { - + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { - SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); - plugin.getServer().getPluginManager().callEvent(tpEvent); - if (!tpEvent.isCancelled() && player.isOnline() && tpEvent.getTo() != null - && tpEvent.getTo().getWorld() != null) { - player.teleport(tpEvent.getTo()); + SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name)); + service.callEvent(tpEvent); + if (!tpEvent.isCancelled() && player.isOnline() && tpEvent.getTo() != null + && tpEvent.getTo().getWorld() != null) { + player.teleport(tpEvent.getTo()); + } } - } - }); } - } if (!LimboCache.getInstance().hasLimboPlayer(name)) { @@ -176,9 +174,9 @@ public class AsynchronousJoin { } Utils.setGroup(player, isAuthAvailable ? GroupType.NOTLOGGEDIN : GroupType.UNREGISTERED); - final int timeOut = Settings.getRegistrationTimeout * 20; + final int registrationTimeout = service.getProperty(RestrictionSettings.TIMEOUT) * 20; - sched.scheduleSyncDelayedTask(plugin, new Runnable() { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { player.setOp(false); @@ -186,40 +184,35 @@ public class AsynchronousJoin { player.setFlySpeed(0.0f); player.setWalkSpeed(0.0f); } - player.setNoDamageTicks(timeOut); - if (Settings.useEssentialsMotd) { + player.setNoDamageTicks(registrationTimeout); + if (service.getProperty(HooksSettings.USE_ESSENTIALS_MOTD)) { player.performCommand("motd"); } - if (Settings.applyBlindEffect) { - int blindTimeOut; + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { // Allow infinite blindness effect - if (timeOut <= 0) { - blindTimeOut = 99999; - } else { - blindTimeOut = timeOut; - } + int blindTimeOut = (registrationTimeout <= 0) ? 99999 : registrationTimeout; player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blindTimeOut, 2)); } } }); - int msgInterval = Settings.getWarnMessageInterval; - if (timeOut > 0) { - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); + int msgInterval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); + if (registrationTimeout > 0) { + BukkitTask id = service.runTaskLater(new TimeoutTask(plugin, name, player), registrationTimeout); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); } - String[] msg; + MessageKey msg; if (isAuthAvailable) { - msg = m.retrieve(MessageKey.LOGIN_MESSAGE); + msg = MessageKey.LOGIN_MESSAGE; } else { msg = Settings.emailRegistration - ? m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE) - : m.retrieve(MessageKey.REGISTER_MESSAGE); + ? MessageKey.REGISTER_EMAIL_MESSAGE + : MessageKey.REGISTER_MESSAGE; } if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) { - BukkitTask msgTask = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval)); + BukkitTask msgTask = service.runTask(new MessageTask(plugin, name, msg, msgInterval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask); } } @@ -235,13 +228,11 @@ public class AsynchronousJoin { if (!tpEvent.isCancelled()) { if (player.isOnline() && tpEvent.getTo() != null && tpEvent.getTo().getWorld() != null) { final Location fLoc = tpEvent.getTo(); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { player.teleport(fLoc); } - }); } } @@ -249,25 +240,23 @@ public class AsynchronousJoin { } private void placePlayerSafely(final Player player, final Location spawnLoc) { - if (spawnLoc == null) - return; - if (!Settings.noTeleport) + if (spawnLoc == null || service.getProperty(RestrictionSettings.NO_TELEPORT)) return; if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) return; if (!player.hasPlayedBefore()) return; - sched.scheduleSyncDelayedTask(plugin, new Runnable() { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { if (spawnLoc.getWorld() == null) { return; } Material cur = player.getLocation().getBlock().getType(); - Material top = player.getLocation().add(0D, 1D, 0D).getBlock().getType(); + Material top = player.getLocation().add(0, 1, 0).getBlock().getType(); if (cur == Material.PORTAL || cur == Material.ENDER_PORTAL || top == Material.PORTAL || top == Material.ENDER_PORTAL) { - m.send(player, MessageKey.UNSAFE_QUIT_LOCATION); + service.send(player, MessageKey.UNSAFE_QUIT_LOCATION); player.teleport(spawnLoc); } } @@ -281,12 +270,17 @@ public class AsynchronousJoin { * @param name The name to check * @param ip The IP address of the player * @param domain The hostname of the IP address + * @param settings The settings instance * @return True if the name is restricted (IP/domain is not allowed for the given name), * false if the restrictions are met or if the name has no restrictions to it */ - private static boolean isNameRestricted(String name, String ip, String domain) { + private static boolean isNameRestricted(String name, String ip, String domain, NewSetting settings) { + if (!settings.getProperty(RestrictionSettings.ENABLE_RESTRICTED_USERS)) { + return false; + } + boolean nameFound = false; - for (String entry : Settings.getRestrictedIp) { + for (String entry : settings.getProperty(RestrictionSettings.ALLOWED_RESTRICTED_USERS)) { String[] args = entry.split(";"); String testName = args[0]; String testIp = args[1]; @@ -301,4 +295,12 @@ public class AsynchronousJoin { return nameFound; } + private boolean hasJoinedIp(String name, String ip, NewSetting settings, AuthMe authMe) { + int count = 0; + for (Player player : Utils.getOnlinePlayers()) { + if (ip.equalsIgnoreCase(authMe.getIP(player)) && !player.getName().equalsIgnoreCase(name)) + count++; + } + return count >= settings.getProperty(RestrictionSettings.MAX_JOIN_PER_IP); + } } 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 09647dff..757789df 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -105,7 +105,7 @@ public class AsynchronousLogin { } else { msg = m.retrieve(MessageKey.REGISTER_MESSAGE); } - BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, + BukkitTask msgT = Bukkit.getScheduler().runTask(plugin, new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); } @@ -175,7 +175,7 @@ public class AsynchronousLogin { displayOtherAccounts(auth); if (Settings.recallEmail && (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email))) { - m.send(player, MessageKey.EMAIL_ADDED_SUCCESS); + m.send(player, MessageKey.ADD_EMAIL_MESSAGE); } if (!Settings.noConsoleSpam) { @@ -185,7 +185,6 @@ public class AsynchronousLogin { // makes player isLoggedin via API PlayerCache.getInstance().addPlayer(auth); database.setLogged(name); - plugin.otherAccounts.addPlayer(player.getUniqueId()); // As the scheduling executes the Task most likely after the current // task, we schedule it in the end @@ -232,8 +231,8 @@ public class AsynchronousLogin { String message = "[AuthMe] " + StringUtils.join(", ", auths) + "."; for (Player player : Utils.getOnlinePlayers()) { if (plugin.getPermissionsManager().hasPermission(player, AdminPermission.SEE_OTHER_ACCOUNTS) - || (player.getName().equals(this.player.getName()) - && plugin.getPermissionsManager().hasPermission(player, PlayerPermission.SEE_OWN_ACCOUNTS))) { + || (player.getName().equals(this.player.getName()) + && plugin.getPermissionsManager().hasPermission(player, PlayerPermission.SEE_OWN_ACCOUNTS))) { player.sendMessage("[AuthMe] The player " + auth.getNickname() + " has " + auths.size() + " accounts"); player.sendMessage(message); } 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 0c120b52..78995cb1 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -38,7 +38,6 @@ public class ProcessSyncPlayerLogin implements Runnable { private final String name; private final PlayerAuth auth; private final AuthMe plugin; - private final DataSource database; private final PluginManager pm; private final JsonCache playerCache; private final NewSetting settings; @@ -54,7 +53,6 @@ public class ProcessSyncPlayerLogin implements Runnable { public ProcessSyncPlayerLogin(Player player, AuthMe plugin, DataSource database, NewSetting settings) { this.plugin = plugin; - this.database = database; this.pm = plugin.getServer().getPluginManager(); this.player = player; this.name = player.getName().toLowerCase(); diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java index 5a8133b7..64c300ef 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java @@ -74,21 +74,24 @@ public class ProcessSyncronousPlayerLogout implements Runnable { int interval = Settings.getWarnMessageInterval; BukkitScheduler sched = player.getServer().getScheduler(); if (timeOut != 0) { - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); + BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); } - BukkitTask msgT = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), interval)); + BukkitTask msgT = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); - if (player.isInsideVehicle() && player.getVehicle() != null) + if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); - if (Settings.applyBlindEffect) - player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2)); + } + if (Settings.applyBlindEffect) { + player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); + } player.setOp(false); restoreSpeedEffect(); // Player is now logout... Time to fire event ! Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player)); - if (Settings.bungee) + if (Settings.bungee) { sendBungeeMessage(); + } m.send(player, MessageKey.LOGOUT_SUCCESS); ConsoleLogger.info(player.getName() + " logged out"); } diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java index 00b47744..7f716e7e 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -5,34 +5,26 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; +import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; -/** - */ public class AsynchronousQuit { - protected final AuthMe plugin; - protected final DataSource database; - protected final Player player; + private final AuthMe plugin; + private final DataSource database; + private final Player player; private final String name; private boolean isOp = false; private boolean needToChange = false; private boolean isKick = false; - /** - * Constructor for AsynchronousQuit. - * - * @param p Player - * @param plugin AuthMe - * @param database DataSource - * @param isKick boolean - */ public AsynchronousQuit(Player p, AuthMe plugin, DataSource database, boolean isKick) { this.player = p; @@ -43,9 +35,7 @@ public class AsynchronousQuit { } public void process() { - if (player == null) - return; - if (Utils.isUnrestricted(player)) { + if (player == null || Utils.isUnrestricted(player)) { return; } @@ -54,7 +44,9 @@ public class AsynchronousQuit { if (PlayerCache.getInstance().isAuthenticated(name)) { if (Settings.isSaveQuitLocationEnabled) { Location loc = player.getLocation(); - PlayerAuth auth = new PlayerAuth(name, loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName(), player.getName()); + PlayerAuth auth = PlayerAuth.builder() + .name(name).location(loc) + .realName(player.getName()).build(); database.updateQuitLoc(auth); } PlayerAuth auth = new PlayerAuth(name, ip, System.currentTimeMillis(), player.getName()); @@ -63,14 +55,11 @@ public class AsynchronousQuit { LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); if (limbo != null) { - if (limbo.getGroup() != null && !limbo.getGroup().isEmpty()) + if (!StringUtils.isEmpty(limbo.getGroup())) { Utils.addNormal(player, limbo.getGroup()); + } needToChange = true; isOp = limbo.getOperator(); - if (limbo.getTimeoutTaskId() != null) - limbo.getTimeoutTaskId().cancel(); - if (limbo.getMessageTaskId() != null) - limbo.getMessageTaskId().cancel(); LimboCache.getInstance().deleteLimboPlayer(name); } if (Settings.isSessionsEnabled && !isKick) { @@ -100,12 +89,15 @@ public class AsynchronousQuit { if (plugin.isEnabled()) { Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); } + // remove player from cache + if (database instanceof CacheDataSource) { + ((CacheDataSource) database).getCachedAuths().invalidate(name); + } } private void postLogout() { PlayerCache.getInstance().removePlayer(name); - if (database.isLogged(name)) - database.setUnlogged(name); + database.setUnlogged(name); plugin.sessions.remove(name); } } 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 e17c511d..6ff41bf6 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -1,5 +1,10 @@ package fr.xephi.authme.process.register; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; @@ -13,8 +18,6 @@ import fr.xephi.authme.security.crypts.TwoFactor; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; /** */ @@ -74,13 +77,16 @@ public class AsyncRegister { if (database.isAuthAvailable(name)) { m.send(player, MessageKey.NAME_ALREADY_REGISTERED); return false; - } else if (Settings.getmaxRegPerIp > 0 - && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) + } else if(Settings.getmaxRegPerIp > 0 && !ip.equalsIgnoreCase("127.0.0.1") && !ip.equalsIgnoreCase("localhost") - && database.getAllAuthsByIp(ip).size() >= Settings.getmaxRegPerIp) { - m.send(player, MessageKey.MAX_REGISTER_EXCEEDED); - return false; + && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)) { + Integer maxReg = Settings.getmaxRegPerIp; + List otherAccounts = database.getAllAuthsByIp(ip); + if (otherAccounts.size() >= maxReg) { + m.send(player, MessageKey.MAX_REGISTER_EXCEEDED, maxReg.toString(), Integer.toString(otherAccounts.size()), otherAccounts.toString()); + return false; + } } return true; } @@ -96,11 +102,16 @@ public class AsyncRegister { } private void emailRegister() { - if (Settings.getmaxRegPerEmail > 0 - && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) - && database.countAuthsByEmail(email) >= Settings.getmaxRegPerEmail) { - m.send(player, MessageKey.MAX_REGISTER_EXCEEDED); - return; + if(Settings.getmaxRegPerEmail > 0 + && !ip.equalsIgnoreCase("127.0.0.1") + && !ip.equalsIgnoreCase("localhost") + && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)) { + Integer maxReg = Settings.getmaxRegPerIp; + List otherAccounts = database.getAllAuthsByIp(ip); + if (otherAccounts.size() >= maxReg) { + m.send(player, MessageKey.MAX_REGISTER_EXCEEDED, maxReg.toString(), Integer.toString(otherAccounts.size()), otherAccounts.toString()); + return; + } } final HashedPassword hashedPassword = plugin.getPasswordSecurity().computeHash(password, name); PlayerAuth auth = PlayerAuth.builder() @@ -146,7 +157,6 @@ public class AsyncRegister { plugin.getManagement().performLogin(player, "dontneed", true); } - plugin.otherAccounts.addPlayer(player.getUniqueId()); ProcessSyncPasswordRegister sync = new ProcessSyncPasswordRegister(player, plugin, settings); plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, sync); diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java index 4d8dcf9e..47264c0f 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -52,14 +52,13 @@ public class ProcessSyncEmailRegister implements Runnable { int msgInterval = Settings.getWarnMessageInterval; BukkitScheduler sched = plugin.getServer().getScheduler(); - if (time != 0 && limbo != null) { - limbo.getTimeoutTaskId().cancel(); - BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), time); - limbo.setTimeoutTaskId(id); - } + if (limbo != null) { - limbo.getMessageTaskId().cancel(); - BukkitTask nwMsg = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); + if (time != 0) { + BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), time); + limbo.setTimeoutTaskId(id); + } + BukkitTask nwMsg = sched.runTask(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); limbo.setMessageTaskId(nwMsg); } 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 60f5bc18..9f9f8919 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -1,16 +1,7 @@ package fr.xephi.authme.process.register; -import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.properties.HooksSettings; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitScheduler; -import org.bukkit.scheduler.BukkitTask; - import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.limbo.LimboCache; @@ -19,10 +10,17 @@ import fr.xephi.authme.events.LoginEvent; import fr.xephi.authme.events.RestoreInventoryEvent; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; /** */ @@ -37,8 +35,8 @@ public class ProcessSyncPasswordRegister implements Runnable { /** * Constructor for ProcessSyncPasswordRegister. * - * @param player Player - * @param plugin AuthMe + * @param player Player + * @param plugin AuthMe * @param settings The plugin settings */ public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) { @@ -77,11 +75,10 @@ public class ProcessSyncPasswordRegister implements Runnable { BukkitScheduler sched = plugin.getServer().getScheduler(); BukkitTask task; if (delay != 0) { - task = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), delay); + task = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), delay); cache.getLimboPlayer(name).setTimeoutTaskId(task); } - task = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, - m.retrieve(MessageKey.LOGIN_MESSAGE), interval)); + task = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); cache.getLimboPlayer(name).setMessageTaskId(task); if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); @@ -158,7 +155,7 @@ public class ProcessSyncPasswordRegister implements Runnable { // Register is now finished; we can force all commands forceCommands(); - + sendTo(); } diff --git a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java index 58c17694..7da80c39 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -73,12 +73,11 @@ public class AsynchronousUnregister { int interval = Settings.getWarnMessageInterval; BukkitScheduler scheduler = plugin.getServer().getScheduler(); if (timeOut != 0) { - BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, - new TimeoutTask(plugin, name, player), timeOut); + BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); limboPlayer.setTimeoutTaskId(id); } - limboPlayer.setMessageTaskId(scheduler.runTaskAsynchronously(plugin, - new MessageTask(plugin, name, m.retrieve(MessageKey.REGISTER_MESSAGE), interval))); + limboPlayer.setMessageTaskId(scheduler.runTask(plugin, + new MessageTask(plugin, name, MessageKey.REGISTER_MESSAGE, interval))); m.send(player, MessageKey.UNREGISTERED_SUCCESS); ConsoleLogger.info(player.getDisplayName() + " unregistered himself"); return; diff --git a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java index ee164525..3defffe4 100644 --- a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java +++ b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java @@ -82,4 +82,13 @@ public abstract class CustomConfiguration extends YamlConfiguration { } return false; } + + public boolean containsAll(String... paths) { + for (String path : paths) { + if (!contains(path)) { + return false; + } + } + return true; + } } diff --git a/src/main/java/fr/xephi/authme/settings/OtherAccounts.java b/src/main/java/fr/xephi/authme/settings/OtherAccounts.java deleted file mode 100644 index 6e2796a3..00000000 --- a/src/main/java/fr/xephi/authme/settings/OtherAccounts.java +++ /dev/null @@ -1,96 +0,0 @@ -package fr.xephi.authme.settings; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -/** - * @author Xephi59 - * @version $Revision: 1.0 $ - */ -public class OtherAccounts extends CustomConfiguration { - - private static OtherAccounts others = null; - - public OtherAccounts() { - super(new File("." + File.separator + "plugins" + File.separator + "AuthMe" + File.separator + "otheraccounts.yml")); - others = this; - load(); - save(); - } - - /** - * Method getInstance. - * - * @return OtherAccounts - */ - public static OtherAccounts getInstance() { - if (others == null) { - others = new OtherAccounts(); - } - return others; - } - - /** - * Method clear. - * - * @param uuid UUID - */ - public void clear(UUID uuid) { - set(uuid.toString(), new ArrayList()); - save(); - } - - /** - * Method addPlayer. - * - * @param uuid UUID - */ - public void addPlayer(UUID uuid) { - try { - Player player = Bukkit.getPlayer(uuid); - if (player == null) - return; - if (!this.getStringList(uuid.toString()).contains(player.getName())) { - this.getStringList(uuid.toString()).add(player.getName()); - save(); - } - } catch (NoSuchMethodError | Exception e) { - //ignore - } - } - - /** - * Method removePlayer. - * - * @param uuid UUID - */ - public void removePlayer(UUID uuid) { - try { - Player player = Bukkit.getPlayer(uuid); - if (player == null) - return; - if (this.getStringList(uuid.toString()).contains(player.getName())) { - this.getStringList(uuid.toString()).remove(player.getName()); - save(); - } - } catch (NoSuchMethodError | Exception e) { - //ignore - } - } - - /** - * Method getAllPlayersByUUID. - * - * @param uuid UUID - * - * @return StringList - */ - public List getAllPlayersByUUID(UUID uuid) { - return this.getStringList(uuid.toString()); - } -} diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 53a8560c..591e55a0 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -60,7 +60,7 @@ public final class Settings { useCaptcha, emailRegistration, multiverse, bungee, banUnsafeIp, doubleEmailCheck, sessionExpireOnIpChange, disableSocialSpy, useEssentialsMotd, usePurge, - purgePlayerDat, purgeEssentialsFile, supportOldPassword, + purgePlayerDat, purgeEssentialsFile, purgeLimitedCreative, purgeAntiXray, purgePermissions, enableProtection, enableAntiBot, recallEmail, useWelcomeMessage, broadcastWelcomeMessage, forceRegKick, forceRegLogin, @@ -72,19 +72,16 @@ public final class Settings { getMySQLColumnGroup, unRegisteredGroup, backupWindowsPath, getRegisteredGroup, rakamakUsers, rakamakUsersIp, getmailAccount, defaultWorld, - getPhpbbPrefix, getWordPressPrefix, spawnPriority, crazyloginFileName, getPassRegex, sendPlayerTo; public static int getWarnMessageInterval, getSessionTimeout, getRegistrationTimeout, getMaxNickLength, getMinNickLength, getPasswordMinLen, getMovementRadius, getmaxRegPerIp, getNonActivatedGroup, passwordMaxLength, getRecoveryPassLength, getMailPort, maxLoginTry, captchaLength, saltLength, - getmaxRegPerEmail, bCryptLog2Rounds, getPhpbbGroup, + getmaxRegPerEmail, bCryptLog2Rounds, antiBotSensibility, antiBotDuration, delayRecall, getMaxLoginPerIp, getMaxJoinPerIp; protected static FileConfiguration configFile; - private static AuthMe plugin; - private static Settings instance; /** * Constructor for Settings. @@ -92,29 +89,27 @@ public final class Settings { * @param pl AuthMe */ public Settings(AuthMe pl) { - instance = this; - plugin = pl; - configFile = plugin.getConfig(); + configFile = pl.getConfig(); loadVariables(); } - public static void loadVariables() { + private static void loadVariables() { isPermissionCheckEnabled = load(PluginSettings.ENABLE_PERMISSION_CHECK); isForcedRegistrationEnabled = configFile.getBoolean("settings.registration.force", true); isRegistrationEnabled = configFile.getBoolean("settings.registration.enabled", true); - isTeleportToSpawnEnabled = configFile.getBoolean("settings.restrictions.teleportUnAuthedToSpawn", false); - getWarnMessageInterval = configFile.getInt("settings.registration.messageInterval", 5); - isSessionsEnabled = configFile.getBoolean("settings.sessions.enabled", false); + isTeleportToSpawnEnabled = load(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN); + getWarnMessageInterval = load(RegistrationSettings.MESSAGE_INTERVAL); + isSessionsEnabled = load(PluginSettings.SESSIONS_ENABLED); getSessionTimeout = configFile.getInt("settings.sessions.timeout", 10); - getRegistrationTimeout = configFile.getInt("settings.restrictions.timeout", 30); - isChatAllowed = configFile.getBoolean("settings.restrictions.allowChat", false); + getRegistrationTimeout = load(RestrictionSettings.TIMEOUT); + isChatAllowed = load(RestrictionSettings.ALLOW_CHAT); getMaxNickLength = configFile.getInt("settings.restrictions.maxNicknameLength", 20); getMinNickLength = configFile.getInt("settings.restrictions.minNicknameLength", 3); getPasswordMinLen = configFile.getInt("settings.security.minPasswordLength", 4); getNickRegex = configFile.getString("settings.restrictions.allowedNicknameCharacters", "[a-zA-Z0-9_?]*"); nickPattern = Pattern.compile(getNickRegex); - isAllowRestrictedIp = configFile.getBoolean("settings.restrictions.AllowRestrictedUser", false); - getRestrictedIp = configFile.getStringList("settings.restrictions.AllowedRestrictedUser"); + isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS); + getRestrictedIp = load(RestrictionSettings.ALLOWED_RESTRICTED_USERS); isMovementAllowed = configFile.getBoolean("settings.restrictions.allowMovement", false); isRemoveSpeedEnabled = configFile.getBoolean("settings.restrictions.removeSpeed", true); getMovementRadius = configFile.getInt("settings.restrictions.allowedMovementRadius", 100); @@ -172,7 +167,7 @@ public final class Settings { useCaptcha = configFile.getBoolean("Security.captcha.useCaptcha", false); maxLoginTry = configFile.getInt("Security.captcha.maxLoginTry", 5); captchaLength = configFile.getInt("Security.captcha.captchaLength", 5); - emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false); + emailRegistration = load(RegistrationSettings.USE_EMAIL_REGISTRATION); saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8); getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1); multiverse = load(HooksSettings.MULTIVERSE); @@ -190,10 +185,6 @@ public final class Settings { purgePlayerDat = configFile.getBoolean("Purge.removePlayerDat", false); purgeEssentialsFile = configFile.getBoolean("Purge.removeEssentialsFile", false); defaultWorld = configFile.getString("Purge.defaultWorld", "world"); - getPhpbbPrefix = configFile.getString("ExternalBoardOptions.phpbbTablePrefix", "phpbb_"); - getPhpbbGroup = configFile.getInt("ExternalBoardOptions.phpbbActivatedGroupId", 2); - supportOldPassword = configFile.getBoolean("settings.security.supportOldPasswordHash", false); - getWordPressPrefix = configFile.getString("ExternalBoardOptions.wordpressTablePrefix", "wp_"); purgeLimitedCreative = configFile.getBoolean("Purge.removeLimitedCreativesInventories", false); purgeAntiXray = configFile.getBoolean("Purge.removeAntiXRayFile", false); purgePermissions = configFile.getBoolean("Purge.removePermissions", false); @@ -211,15 +202,15 @@ public final class Settings { countriesBlacklist = configFile.getStringList("Protection.countriesBlacklist"); broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false); forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false); - forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false); + forceRegLogin = load(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER); spawnPriority = load(RestrictionSettings.SPAWN_PRIORITY); - getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); - getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); - checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); + getMaxLoginPerIp = load(RestrictionSettings.MAX_LOGIN_PER_IP); + getMaxJoinPerIp = load(RestrictionSettings.MAX_JOIN_PER_IP); + checkVeryGames = load(HooksSettings.ENABLE_VERYGAMES_IP_CHECK); removeJoinMessage = load(RegistrationSettings.REMOVE_JOIN_MESSAGE); removeLeaveMessage = load(RegistrationSettings.REMOVE_LEAVE_MESSAGE); delayJoinMessage = load(RegistrationSettings.DELAY_JOIN_MESSAGE); - noTeleport = configFile.getBoolean("settings.restrictions.noTeleport", false); + noTeleport = load(RestrictionSettings.NO_TELEPORT); crazyloginFileName = configFile.getString("Converter.CrazyLogin.fileName", "accounts.db"); getPassRegex = configFile.getString("settings.restrictions.allowedPasswordCharacters", "[\\x21-\\x7E]*"); applyBlindEffect = configFile.getBoolean("settings.applyBlindEffect", false); diff --git a/src/main/java/fr/xephi/authme/settings/Spawn.java b/src/main/java/fr/xephi/authme/settings/Spawn.java index 85f20401..e891e47f 100644 --- a/src/main/java/fr/xephi/authme/settings/Spawn.java +++ b/src/main/java/fr/xephi/authme/settings/Spawn.java @@ -2,7 +2,6 @@ package fr.xephi.authme.settings; import com.onarandombox.MultiverseCore.api.MVWorldManager; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.util.StringUtils; import org.bukkit.Bukkit; @@ -73,34 +72,32 @@ public class Spawn extends CustomConfiguration { } public Location getSpawn() { - try { + if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) { String worldName = getString("spawn.world"); World world = Bukkit.getWorld(worldName); - if (StringUtils.isEmpty(worldName) || world == null) { - return null; + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location( + world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"), + Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch")) + ); } - return new Location(world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"), - Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch"))); - } catch (NumberFormatException e) { - ConsoleLogger.writeStackTrace(e); - return null; } + return null; } public Location getFirstSpawn() { - try { - String worldName; - World world; - if (StringUtils.isEmpty(worldName = getString("firstspawn.world")) || - (world = Bukkit.getWorld(worldName)) == null) { - return null; + if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y", + "firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) { + String worldName = getString("firstspawn.world"); + World world = Bukkit.getWorld(worldName); + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location( + world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"), + Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch")) + ); } - return new Location(world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"), - Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch"))); - } catch (NumberFormatException e) { - ConsoleLogger.writeStackTrace(e); - return null; } + return null; } // Return the spawn location of a player diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index 0be9f67a..14ef825e 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -39,7 +39,7 @@ public class RegistrationSettings implements SettingsClass { newProperty("settings.registration.doubleEmailCheck", false); @Comment({ - "Do we force kicking player after a successful registration?", + "Do we force kick a player after a successful registration?", "Do not use with login feature below"}) public static final Property FORCE_KICK_AFTER_REGISTER = newProperty("settings.registration.forceKickAfterRegister", false); 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 4025c0be..dd26b9e7 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -64,7 +64,7 @@ public class RestrictionSettings implements SettingsClass { @Comment({ "To activate the restricted user feature you need", - "to enable this option and configure the AllowedRestrctedUser field."}) + "to enable this option and configure the AllowedRestrictedUser field."}) public static final Property ENABLE_RESTRICTED_USERS = newProperty("settings.restrictions.AllowRestrictedUser", false); diff --git a/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java b/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java index 0551b8ca..c5ff5b95 100644 --- a/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java +++ b/src/main/java/fr/xephi/authme/task/ChangePasswordTask.java @@ -53,12 +53,12 @@ public class ChangePasswordTask implements Runnable { @Override public void run() { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("Forward"); - out.writeUTF("ALL"); - out.writeUTF("AuthMe"); - out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt); - player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Forward"); + out.writeUTF("ALL"); + out.writeUTF("AuthMe"); + out.writeUTF("changepassword;" + name + ";" + hash + ";" + salt); + player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); } }); } diff --git a/src/main/java/fr/xephi/authme/task/MessageTask.java b/src/main/java/fr/xephi/authme/task/MessageTask.java index cfaa7d4a..92310f37 100644 --- a/src/main/java/fr/xephi/authme/task/MessageTask.java +++ b/src/main/java/fr/xephi/authme/task/MessageTask.java @@ -3,6 +3,7 @@ package fr.xephi.authme.task; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; +import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; @@ -24,32 +25,31 @@ public class MessageTask implements Runnable { * @param strings String[] * @param interval int */ - public MessageTask(AuthMe plugin, String name, String[] strings, - int interval) { + public MessageTask(AuthMe plugin, String name, String[] strings, int interval) { this.plugin = plugin; this.name = name; this.msg = strings; this.interval = interval; } - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ + public MessageTask(AuthMe plugin, String name, MessageKey messageKey, int interval) { + this(plugin, name, plugin.getMessages().retrieve(messageKey), interval); + } + @Override public void run() { - if (PlayerCache.getInstance().isAuthenticated(name)) + if (PlayerCache.getInstance().isAuthenticated(name)) { return; + } for (Player player : Utils.getOnlinePlayers()) { - if (player.getName().toLowerCase().equals(name)) { + if (player.getName().equalsIgnoreCase(name)) { for (String ms : msg) { player.sendMessage(ms); } - BukkitTask late = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, this, interval * 20); + BukkitTask nextTask = plugin.getServer().getScheduler().runTaskLater(plugin, this, interval * 20); if (LimboCache.getInstance().hasLimboPlayer(name)) { - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(late); + LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nextTask); } return; } diff --git a/src/main/java/fr/xephi/authme/task/TimeoutTask.java b/src/main/java/fr/xephi/authme/task/TimeoutTask.java index eedf0875..b304632d 100644 --- a/src/main/java/fr/xephi/authme/task/TimeoutTask.java +++ b/src/main/java/fr/xephi/authme/task/TimeoutTask.java @@ -4,14 +4,10 @@ import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; -/** - */ public class TimeoutTask implements Runnable { - private final AuthMe plugin; private final String name; private final Messages m; private final Player player; @@ -25,38 +21,14 @@ public class TimeoutTask implements Runnable { */ public TimeoutTask(AuthMe plugin, String name, Player player) { this.m = plugin.getMessages(); - this.plugin = plugin; this.name = name; this.player = player; } - /** - * Method getName. - * - * @return String - */ - public String getName() { - return name; - } - - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { - if (PlayerCache.getInstance().isAuthenticated(name)) { - return; + if (!PlayerCache.getInstance().isAuthenticated(name)) { + player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR)); } - - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - @Override - public void run() { - if (player.isOnline()) { - player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR)); - } - } - }); } } diff --git a/src/main/java/fr/xephi/authme/util/MigrationService.java b/src/main/java/fr/xephi/authme/util/MigrationService.java index 6da4d95e..d4ab1533 100644 --- a/src/main/java/fr/xephi/authme/util/MigrationService.java +++ b/src/main/java/fr/xephi/authme/util/MigrationService.java @@ -5,6 +5,8 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.converter.ForceFlatToSqlite; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; +import fr.xephi.authme.datasource.FlatFile; +import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.SHA256; @@ -58,12 +60,17 @@ public final class MigrationService { if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) { ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated; it will be changed " + "to SQLite... Connection will be impossible until conversion is done!"); - ForceFlatToSqlite converter = new ForceFlatToSqlite(dataSource, settings); - DataSource result = converter.run(); - if (result == null) { - throw new IllegalStateException("Error during conversion from flatfile to SQLite"); - } else { - return result; + FlatFile flatFile = (FlatFile) dataSource; + try { + SQLite sqlite = new SQLite(settings); + ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, sqlite); + converter.run(); + settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE); + settings.save(); + return sqlite; + } catch (Exception e) { + ConsoleLogger.logException("Error during conversion from Flatfile to SQLite", e); + throw new IllegalStateException(e); } } return null; diff --git a/src/main/java/fr/xephi/authme/util/Profiler.java b/src/main/java/fr/xephi/authme/util/Profiler.java index 0784aa65..73bdf5e4 100644 --- a/src/main/java/fr/xephi/authme/util/Profiler.java +++ b/src/main/java/fr/xephi/authme/util/Profiler.java @@ -2,15 +2,13 @@ package fr.xephi.authme.util; import java.text.DecimalFormat; -/** - */ -@SuppressWarnings("UnusedDeclaration") public class Profiler { /** * Defines the past time in milliseconds. */ private long time = 0; + /** * Defines the time in milliseconds the profiler last started at. */ diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index f12c8b34..a39ef756 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -154,8 +154,7 @@ public final class Utils { public static boolean isUnrestricted(Player player) { return Settings.isAllowRestrictedIp - && !Settings.getUnrestrictedName.isEmpty() - && (Settings.getUnrestrictedName.contains(player.getName().toLowerCase())); + && Settings.getUnrestrictedName.contains(player.getName().toLowerCase()); } public static void packCoords(double x, double y, double z, String w, final Player pl) { @@ -216,8 +215,7 @@ public final class Utils { ConsoleLogger.showError("Unknown list of online players of type " + type); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - ConsoleLogger.showError("Could not retrieve list of online players: [" - + e.getClass().getName() + "] " + e.getMessage()); + ConsoleLogger.logException("Could not retrieve list of online players:", e); } return Collections.emptyList(); } diff --git a/src/main/resources/messages/messages_de.yml b/src/main/resources/messages/messages_de.yml index 942460ac..04345292 100644 --- a/src/main/resources/messages/messages_de.yml +++ b/src/main/resources/messages/messages_de.yml @@ -1,5 +1,5 @@ unknown_user: '&cBenutzer ist nicht in der Datenbank' -unsafe_spawn: '&cDeine Logoutposition war unsicher, du wurdest zum Spawn teleportiert' +unsafe_spawn: '&cDeine Logoutposition war unsicher, Du wurdest zum Spawn teleportiert' not_logged_in: '&cNicht eingeloggt!' reg_voluntarily: 'Du kannst dich mit folgendem Befehl registrieren "/register "' usage_log: '&cBenutze: /login ' @@ -9,11 +9,11 @@ reg_disabled: '&cRegistrierungen sind deaktiviert' valid_session: '&2Erfolgreich eingeloggt!' login: '&2Erfolgreich eingeloggt!' vb_nonActiv: '&cDein Account wurde noch nicht aktiviert. Bitte prüfe Deine E-Mails!' -user_regged: '&cBenutzername ist schon vergeben' +user_regged: '&cDieser Benutzername ist schon vergeben' usage_reg: '&cBenutze: /register ' -max_reg: '&cDu hast die maximale Anzahl an Accounts erreicht' +max_reg: '&cDu hast die maximale Anzahl an Accounts erreicht.' no_perm: '&4Du hast keine Rechte, um diese Aktion auszuführen!' -error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator' +error: '&4Ein Fehler ist aufgetreten. Bitte kontaktiere einen Administrator.' login_msg: '&cBitte logge Dich ein mit "/login "' reg_msg: '&3Bitte registriere Dich mit "/register "' reg_email_msg: '&3Bitte registriere Dich mit "/register "' @@ -21,44 +21,43 @@ usage_unreg: '&cBenutze: /unregister ' pwd_changed: '&2Passwort geändert!' user_unknown: '&cBenutzername nicht registriert!' password_error: '&cPasswörter stimmen nicht überein!' -password_error_nick: '&cDu kannst nicht deinen Namen als Passwort nutzen!' +password_error_nick: '&cDu kannst nicht Deinen Namen als Passwort nutzen!' password_error_unsafe: '&cDu kannst nicht unsichere Passwörter nutzen!' invalid_session: '&cUngültige Session. Bitte starte das Spiel neu oder warte, bis die Session abgelaufen ist' -reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com zum registrieren' +reg_only: '&4Nur für registrierte Spieler! Bitte besuche http://example.com zum Registrieren' logged_in: '&cBereits eingeloggt!' logout: '&2Erfolgreich ausgeloggt' same_nick: '&4Jemand mit diesem Namen spielt bereits auf dem Server!' registered: '&2Erfolgreich registriert!' pass_len: '&cDein Passwort ist zu kurz oder zu lang!' -reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen' +reload: '&2Konfiguration und Datenbank wurden erfolgreich neu geladen.' timeout: '&4Zeitüberschreitung beim Login' usage_changepassword: '&cBenutze: /changepassword ' -name_len: '&4Dein Nickname ist zu kurz oder zu lang' +name_len: '&4Dein Nickname ist zu kurz oder zu lang.' regex: '&4Dein Nickname enthält nicht erlaubte Zeichen. Zulässige Zeichen: REG_EX' -add_email: '&3Bitte hinterlege Deine E-Mail Adresse: /email add ' +add_email: '&3Bitte hinterlege Deine E-Mail-Adresse: /email add ' recovery_email: '&3Passwort vergessen? Nutze "/email recovery " für ein neues Passwort' usage_captcha: '&3Um dich einzuloggen, tippe dieses Captcha so ein: /captcha ' wrong_captcha: '&cFalsches Captcha, bitte nutze: /captcha THE_CAPTCHA' valid_captcha: '&2Das Captcha ist korrekt!' -kick_forvip: '&3Ein VIP Spieler hat den vollen Server betreten!' +kick_forvip: '&3Ein VIP-Spieler hat den vollen Server betreten!' kick_fullserver: '&4Der Server ist momentan voll, Sorry!' usage_email_add: '&cBenutze: /email add ' usage_email_change: '&cBenutze: /email change ' usage_email_recovery: '&cBenutze: /email recovery ' -new_email_invalid: '&cDie neue Email ist ungültig!' -old_email_invalid: '&cDie alte Email ist ungültig!' -email_invalid: '&cUngültige Email' -email_added: '&2Email hinzugefügt!' -email_confirm: '&cBitte bestätige deine Email!' -email_changed: '&2Email aktualisiert!' -email_send: '&2Wiederherstellungs-Email wurde gesendet!' -email_exists: '&cEine Wiederherstellungs-Email wurde bereits versandt! Nutze folgenden Befehl um eine neue Email zu versenden:' -country_banned: '&4Dein Land ist gesperrt' +new_email_invalid: '&cDie neue E-Mail ist ungültig!' +old_email_invalid: '&cDie alte E-Mail ist ungültig!' +email_invalid: '&cUngültige E-Mail!' +email_added: '&2E-Mail hinzugefügt!' +email_confirm: '&cBitte bestätige Deine E-Mail!' +email_changed: '&2E-Mail aktualisiert!' +email_send: '&2Wiederherstellungs-E-Mail wurde gesendet!' +email_exists: '&cEine Wiederherstellungs-E-Mail wurde bereits versandt! Nutze folgenden Befehl um eine neue E-Mail zu versenden:' +country_banned: '&4Dein Land ist gesperrt!' antibot_auto_enabled: '&4[AntiBotService] AntiBotMod wurde aufgrund hoher Netzauslastung automatisch aktiviert!' -antibot_auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei' -kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du dich mit dem Server verbindest' -# TODO two_factor_create: Missing tag %url -two_factor_create: '&2Dein geheimer Code ist %code' -# TODO email_already_used: '&4The email address is already being used' -# TODO invalid_name_case: 'You should join using username %valid, not %invalid.' -# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' \ No newline at end of file +antibot_auto_disabled: '&2[AntiBotService] AntiBotMod wurde nach %m Minuten deaktiviert, hoffentlich ist die Invasion vorbei.' +kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor Du Dich mit dem Server verbindest.' +two_factor_create: '&2Dein geheimer Code ist %code. Du kannst ihn hier abfragen: %url' +email_already_used: '&4Diese E-Mail-Adresse wird bereits genutzt.' +invalid_name_case: 'Dein registrierter Benutzername ist &2%valid&f - nicht &4%invalid&f.' +not_owner_error: 'Du bist nicht der Besitzer dieses Accounts. Bitte wähle einen anderen Namen!' diff --git a/src/main/resources/messages/messages_en.yml b/src/main/resources/messages/messages_en.yml index 64aa9099..5b474e2d 100644 --- a/src/main/resources/messages/messages_en.yml +++ b/src/main/resources/messages/messages_en.yml @@ -12,7 +12,7 @@ login: '&2Successful login!' vb_nonActiv: '&cYour account isn''t activated yet, please check your emails!' user_regged: '&cYou already have registered this username!' usage_reg: '&cUsage: /register ' -max_reg: '&cYou have exceeded the maximum number of registrations for your connection!' +max_reg: '&cYou have exceeded the maximum number of registrations (%reg_count/%max_acc %reg_names) for your connection!' no_perm: '&4You don''t have the permission to perform this action!' error: '&4An unexpected error occurred, please contact an administrator!' login_msg: '&cPlease, login with the command "/login "' @@ -59,5 +59,5 @@ antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' email_already_used: '&4The email address is already being used' two_factor_create: '&2Your secret code is %code. You can scan it from here %url' -not_owner_error: 'You are not the owner of this account. Please try another name!' +not_owner_error: 'You are not the owner of this account. Please choose another name!' invalid_name_case: 'You should join using username %valid, not %invalid.' diff --git a/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java b/src/test/java/fr/xephi/authme/AuthMeMatchers.java similarity index 98% rename from src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java rename to src/test/java/fr/xephi/authme/AuthMeMatchers.java index 798d5315..aed77cb1 100644 --- a/src/test/java/fr/xephi/authme/datasource/AuthMeMatchers.java +++ b/src/test/java/fr/xephi/authme/AuthMeMatchers.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.datasource; +package fr.xephi.authme; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java index 62c2b890..7e62023f 100644 --- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java @@ -1,14 +1,13 @@ package fr.xephi.authme.command; -import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import fr.xephi.authme.permission.AdminPermission; +import fr.xephi.authme.permission.PermissionNode; +import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.WrapperMock; +import org.junit.BeforeClass; +import org.junit.Test; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -17,13 +16,12 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; -import org.junit.BeforeClass; -import org.junit.Test; - -import fr.xephi.authme.permission.AdminPermission; -import fr.xephi.authme.permission.PermissionNode; -import fr.xephi.authme.util.StringUtils; -import fr.xephi.authme.util.WrapperMock; +import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; /** * Test for {@link CommandInitializer} to guarantee the integrity of the defined commands. @@ -241,15 +239,11 @@ public class CommandInitializerTest { public void shouldNotHavePlayerPermissionIfDefaultsToOpOnly() { // given BiConsumer adminPermissionChecker = new BiConsumer() { - // The only exception to this check is the force login command, which should default to OP_ONLY - // but semantically it is a player permission - final List forceLoginLabels = Arrays.asList("forcelogin", "login"); - @Override public void accept(CommandDescription command, int depth) { CommandPermissions permissions = command.getCommandPermissions(); if (permissions != null && OP_ONLY.equals(permissions.getDefaultPermission())) { - if (!hasAdminNode(permissions) && !command.getLabels().equals(forceLoginLabels)) { + if (!hasAdminNode(permissions)) { fail("The command with labels " + command.getLabels() + " has OP_ONLY default " + "permission but no permission node on admin level"); } diff --git a/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java b/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java new file mode 100644 index 00000000..17d4fb06 --- /dev/null +++ b/src/test/java/fr/xephi/authme/converter/ForceFlatToSqliteTest.java @@ -0,0 +1,72 @@ +package fr.xephi.authme.converter; + +import com.google.common.io.Files; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.FlatFile; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData; +import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link ForceFlatToSqlite}. + */ +public class ForceFlatToSqliteTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private FlatFile flatFile; + + @BeforeClass + public static void setup() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void copyFile() throws IOException { + File source = TestHelper.getJarFile("/datasource-integration/flatfile-test.txt"); + File destination = temporaryFolder.newFile(); + Files.copy(source, destination); + flatFile = new FlatFile(destination); + } + + @Test + public void shouldConvertToSqlite() { + // given + DataSource dataSource = mock(DataSource.class); + ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, dataSource); + + // when + converter.run(); + + // then + ArgumentCaptor authCaptor = ArgumentCaptor.forClass(PlayerAuth.class); + verify(dataSource, times(7)).saveAuth(authCaptor.capture()); + List auths = authCaptor.getAllValues(); + assertThat(auths, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"))); + assertThat(auths, hasItem(hasAuthLocation(1.05, 2.1, 4.2, "world"))); + assertThat(auths, hasItem(hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90"))); + assertThat(auths, hasItem(hasAuthLocation(124.1, 76.3, -127.8, "nether"))); + assertThat(auths, hasItem(hasAuthBasicData("eightfields", "eightFields", "your@email.com", "6.6.6.66"))); + assertThat(auths, hasItem(hasAuthLocation(8.8, 17.6, 26.4, "eightworld"))); + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index dfd6f825..077209f4 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -4,15 +4,18 @@ import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.crypts.HashedPassword; import org.junit.Test; +import java.util.Arrays; import java.util.List; -import static fr.xephi.authme.datasource.AuthMeMatchers.equalToHash; -import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthBasicData; -import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthLocation; +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.equalTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; /** * Abstract class for data source integration tests. @@ -126,15 +129,9 @@ public abstract class AbstractDataSourceIntegrationTest { // then assertThat(response, equalTo(true)); assertThat(authList, hasSize(2)); + assertThat(authList, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"))); assertThat(newAuthList, hasSize(3)); - boolean hasBobby = false; - for (PlayerAuth auth : authList) { - if (auth.getNickname().equals("bobby")) { - hasBobby = true; - break; - } - } - assertThat(hasBobby, equalTo(true)); + assertThat(newAuthList, hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"))); } @Test @@ -201,4 +198,106 @@ public abstract class AbstractDataSourceIntegrationTest { assertThat(dataSource.getAuth("user"), hasAuthLocation(143, -42.12, 29.47, "the_end")); } + @Test + public void shouldDeletePlayers() { + // given + DataSource dataSource = getDataSource(); + List playersToDelete = Arrays.asList("bobby", "doesNotExist"); + assumeThat(dataSource.getAccountsRegistered(), equalTo(2)); + + // when + dataSource.purgeBanned(playersToDelete); + + // then + assertThat(dataSource.getAccountsRegistered(), equalTo(1)); + assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false)); + assertThat(dataSource.isAuthAvailable("user"), equalTo(true)); + } + + @Test + public void shouldUpdateEmail() { + // given + DataSource dataSource = getDataSource(); + String email = "new-user@mail.tld"; + PlayerAuth userAuth = PlayerAuth.builder().name("user").email(email).build(); + PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").email("addr@example.com").build(); + + // when + boolean response1 = dataSource.updateEmail(userAuth); + boolean response2 = dataSource.updateEmail(invalidAuth); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("user", "user", email, "34.56.78.90"))); + } + + @Test + public void shouldUpdateIp() { + // given + DataSource dataSource = getDataSource(); + String ip = "250.230.67.73"; + + // when + boolean response1 = dataSource.updateIp("bobby", ip); + boolean response2 = dataSource.updateIp("bogus", "123.123.123.123"); + + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", ip))); + } + + @Test + public void shouldCountAuths() { + // given + DataSource dataSource = getDataSource(); + + // when + int initialCount = dataSource.getAccountsRegistered(); + for (int i = 0; i < 4; ++i) { + dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).build()); + } + int endCount = dataSource.getAccountsRegistered(); + + // then + assertThat(initialCount, equalTo(2)); + assertThat(endCount, equalTo(6)); + } + + @Test + public void shouldGetAllUsersByIp() { + // given + DataSource dataSource = getDataSource(); + + // when + List initialList = dataSource.getAllAuthsByIp("123.45.67.89"); + List emptyList = dataSource.getAllAuthsByIp("8.8.8.8"); + for (int i = 0; i < 3; ++i) { + dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).ip("123.45.67.89").build()); + } + List updatedList = dataSource.getAllAuthsByIp("123.45.67.89"); + + // then + assertThat(initialList, hasSize(1)); + assertThat(initialList.get(0), equalTo("bobby")); + assertThat(emptyList, hasSize(0)); + assertThat(updatedList, hasSize(4)); + assertThat(updatedList, hasItem(equalTo("bobby"))); + assertThat(updatedList, hasItem(equalTo("test-1"))); + } + + @Test + public void shouldUpdateRealName() { + // given + DataSource dataSource = getDataSource(); + + // when + boolean response1 = dataSource.updateRealName("bobby", "BOBBY"); + boolean response2 = dataSource.updateRealName("notExists", "NOTEXISTS"); + + // then + assertThat(response1 && response2, equalTo(true)); + assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89")); + } + } diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java new file mode 100644 index 00000000..f5ba58e8 --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java @@ -0,0 +1,321 @@ +package fr.xephi.authme.datasource; + +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test class which runs through a datasource implementation and verifies that all + * instances of {@link AutoCloseable} that are created in the calls are closed again. + *

+ * Instead of an actual connection to a datasource, we pass a mock Connection object + * which is set to create additional mocks on demand for Statement and ResultSet objects. + * This test ensures that all such objects that are created will be closed again by + * keeping a list of mocks ({@link #closeables}) and then verifying that all have been + * closed {@link #verifyHaveMocksBeenClosed()}. + */ +@RunWith(Parameterized.class) +public abstract class AbstractResourceClosingTest { + + /** List of DataSource method names not to test. */ + private static final Set IGNORED_METHODS = ImmutableSet.of("reload", "close", "getType"); + + /** Collection of values to use to call methods with the parameters they expect. */ + private static final Map, Object> PARAM_VALUES = getDefaultParameters(); + + /** + * Custom list of hash algorithms to use to test a method. By default we define {@link HashAlgorithm#XFBCRYPT} as + * algorithms we use as a lot of methods execute additional statements in {@link MySQL}. If other algorithms + * have custom behaviors, they can be supplied in this map so it will be tested as well. + */ + private static final Map CUSTOM_ALGORITHMS = getCustomAlgorithmList(); + + /** Mock of a settings instance. */ + private static NewSetting settings; + + /** The datasource to test. */ + private DataSource dataSource; + + /** The DataSource method to test. */ + private Method method; + + /** Keeps track of the closeables which are created during the tested call. */ + private List closeables = new ArrayList<>(); + + /** + * Constructor for the test instance verifying the given method with the given hash algorithm. + * + * @param method The DataSource method to test + * @param name The name of the method + * @param algorithm The hash algorithm to use + */ + public AbstractResourceClosingTest(Method method, String name, HashAlgorithm algorithm) { + // Note ljacqu 20160227: The name parameter is necessary as we pass it from the @Parameters method; + // we use the method name in the annotation to name the test sensibly + this.method = method; + given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm); + } + + /** Initialize the settings mock and makes it return the default of any given property by default. */ + @BeforeClass + public static void initializeSettings() throws IOException, ClassNotFoundException { + settings = mock(NewSetting.class); + given(settings.getProperty(any(Property.class))).willAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + return ((Property) invocation.getArguments()[0]).getDefaultValue(); + } + }); + ConsoleLoggerTestInitializer.setupLogger(); + } + + /** Initialize the dataSource implementation to test based on a mock connection. */ + @Before + public void setUpMockConnection() throws Exception { + Connection connection = initConnection(); + dataSource = createDataSource(settings, connection); + } + + /** + * The actual test -- executes the method given through the constructor and then verifies that all + * AutoCloseable mocks it constructed have been closed. + */ + @Test + public void shouldCloseResources() throws IllegalAccessException, InvocationTargetException { + method.invoke(dataSource, buildParamListForMethod(method)); + verifyHaveMocksBeenClosed(); + } + + /** + * Initialization method -- provides the parameters to run the test with by scanning all DataSource + * methods. By default, we run one test per method with the default hash algorithm, XFBCRYPT. + * If the map of custom algorithms has an entry for the method name, we add an entry for each algorithm + * supplied by the map. + * + * @return Test parameters + */ + @Parameterized.Parameters(name = "{1}({2})") + public static Collection data() { + List methods = getDataSourceMethods(); + List data = new ArrayList<>(); + // Use XFBCRYPT if nothing else specified as there is a lot of specific behavior to this hash algorithm in MySQL + final HashAlgorithm[] defaultAlgorithm = new HashAlgorithm[]{HashAlgorithm.XFBCRYPT}; + for (Method method : methods) { + HashAlgorithm[] algorithms = Objects.firstNonNull(CUSTOM_ALGORITHMS.get(method.getName()), defaultAlgorithm); + for (HashAlgorithm algorithm : algorithms) { + data.add(new Object[]{method, method.getName(), algorithm}); + } + } + return data; + } + + /* Create a DataSource instance with the given mock settings and mock connection. */ + protected abstract DataSource createDataSource(NewSetting settings, Connection connection) throws Exception; + + /* Get all methods of the DataSource interface, minus the ones in the ignored list. */ + private static List getDataSourceMethods() { + List publicMethods = new ArrayList<>(); + for (Method method : DataSource.class.getDeclaredMethods()) { + if (!IGNORED_METHODS.contains(method.getName())) { + publicMethods.add(method); + } + } + return publicMethods; + } + + /** + * Verify that all AutoCloseables that have been created during the method execution have been closed. + */ + private void verifyHaveMocksBeenClosed() { + if (closeables.isEmpty()) { + System.out.println("Note: detected no AutoCloseables for method '" + method.getName() + "'"); + } + try { + for (AutoCloseable autoCloseable : closeables) { + verify(autoCloseable).close(); + } + } catch (Exception e) { + throw new IllegalStateException("Error verifying if autoCloseable was closed", e); + } + } + + /** + * Helper method for building a list of test values to satisfy a method's signature. + * + * @param method The method to create a valid parameter list for + * @return Parameter list to invoke the given method with + */ + private static Object[] buildParamListForMethod(Method method) { + List params = new ArrayList<>(); + int index = 0; + for (Class paramType : method.getParameterTypes()) { + // Checking List.class == paramType instead of Class#isAssignableFrom means we really only accept List, + // but that is a sensible assumption and makes our life much easier later on when juggling with Type + Object param = (List.class == paramType) + ? getTypedList(method.getGenericParameterTypes()[index]) + : PARAM_VALUES.get(paramType); + Preconditions.checkNotNull(param, "No param type for " + paramType); + params.add(param); + ++index; + } + return params.toArray(); + } + + /** + * Return a list with some test elements that correspond to the given list type's generic type. + * + * @param type The list type to process and build a test list for + * @return Test list with sample elements of the correct type + */ + private static List getTypedList(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Preconditions.checkArgument(List.class == parameterizedType.getRawType(), type + " should be a List"); + Type genericType = parameterizedType.getActualTypeArguments()[0]; + + Object element = PARAM_VALUES.get(genericType); + Preconditions.checkNotNull(element, "No sample element for list of generic type " + genericType); + return Arrays.asList(element, element, element); + } + throw new IllegalStateException("Cannot build list for unexpected Type: " + type); + } + + /* Initialize the map of test values to pass to methods to satisfy their signature. */ + private static Map, Object> getDefaultParameters() { + HashedPassword hash = new HashedPassword("test", "test"); + return ImmutableMap., Object>builder() + .put(String.class, "test") + .put(int.class, 3) + .put(long.class, 102L) + .put(PlayerAuth.class, PlayerAuth.builder().name("test").realName("test").password(hash).build()) + .put(HashedPassword.class, hash) + .build(); + } + + /** + * Return the custom list of hash algorithms to test a method with to execute code specific to + * one hash algorithm. By default, XFBCRYPT is used. Only MySQL has code specific to algorithms + * but for technical reasons the custom list will be used for all tested classes. + * + * @return List of custom algorithms by method + */ + private static Map getCustomAlgorithmList() { + // We use XFBCRYPT as default encryption method so we don't have to list many of the special cases for it + return ImmutableMap.builder() + .put("saveAuth", new HashAlgorithm[]{HashAlgorithm.PHPBB, HashAlgorithm.WORDPRESS}) + .build(); + } + + // --------------------- + // Mock initialization + // --------------------- + /** + * Initialize the connection mock which produces additional AutoCloseable mocks and records them. + * + * @return Connection mock + */ + private Connection initConnection() { + Connection connection = mock(Connection.class); + try { + given(connection.prepareStatement(anyString())).willAnswer(preparedStatementAnswer()); + given(connection.createStatement()).willAnswer(preparedStatementAnswer()); + given(connection.createBlob()).willReturn(mock(Blob.class)); + return connection; + } catch (SQLException e) { + throw new IllegalStateException("Could not initialize connection mock", e); + } + } + + /* Create Answer that returns a PreparedStatement mock. */ + private Answer preparedStatementAnswer() { + return new Answer() { + @Override + public PreparedStatement answer(InvocationOnMock invocation) throws SQLException { + PreparedStatement pst = mock(PreparedStatement.class); + closeables.add(pst); + given(pst.executeQuery()).willAnswer(resultSetAnswer()); + given(pst.executeQuery(anyString())).willAnswer(resultSetAnswer()); + return pst; + } + }; + } + + /* Create Answer that returns a ResultSet mock. */ + private Answer resultSetAnswer() throws SQLException { + return new Answer() { + @Override + public ResultSet answer(InvocationOnMock invocation) throws Throwable { + ResultSet rs = initResultSet(); + closeables.add(rs); + return rs; + } + }; + } + + /* Create a ResultSet mock. */ + private ResultSet initResultSet() throws SQLException { + ResultSet rs = mock(ResultSet.class); + // Return true for ResultSet#next the first time to make sure we execute all code + given(rs.next()).willAnswer(new Answer() { + boolean isInitial = true; + @Override + public Boolean answer(InvocationOnMock invocation) { + if (isInitial) { + isInitial = false; + return true; + } + return false; + } + }); + given(rs.getString(anyInt())).willReturn("test"); + given(rs.getString(anyString())).willReturn("test"); + + Blob blob = mock(Blob.class); + given(blob.getBytes(anyLong(), anyInt())).willReturn(new byte[]{}); + given(blob.length()).willReturn(0L); + given(rs.getBlob(anyInt())).willReturn(blob); + given(rs.getBlob(anyString())).willReturn(blob); + return rs; + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java new file mode 100644 index 00000000..154511c2 --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/FlatFileIntegrationTest.java @@ -0,0 +1,100 @@ +package fr.xephi.authme.datasource; + +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.cache.auth.PlayerAuth; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +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.CoreMatchers.hasItem; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertThat; + +/** + * Integration test for the deprecated {@link FlatFile} datasource. The flatfile datasource is no longer used. + * Essentially, the only time we use it is in {@link fr.xephi.authme.converter.ForceFlatToSqlite}, + * which requires {@link FlatFile#getAllAuths()}. + */ +public class FlatFileIntegrationTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private DataSource dataSource; + + @Before + public void copyFileToTemporaryFolder() throws IOException { + File originalFile = TestHelper.getJarFile("/datasource-integration/flatfile-test.txt"); + File copy = temporaryFolder.newFile(); + Files.copy(originalFile, copy); + dataSource = new FlatFile(copy); + } + + @Test + public void shouldReturnIfAuthIsAvailableOrNot() { + // given / when + boolean isBobbyAvailable = dataSource.isAuthAvailable("bobby"); + boolean isChrisAvailable = dataSource.isAuthAvailable("chris"); + boolean isUserAvailable = dataSource.isAuthAvailable("USER"); + + // then + assertThat(isBobbyAvailable, equalTo(true)); + assertThat(isChrisAvailable, equalTo(false)); + assertThat(isUserAvailable, equalTo(true)); + } + + @Test + public void shouldReturnAllAuths() { + // given / when + List authList = dataSource.getAllAuths(); + + // then + assertThat(authList, hasSize(7)); + assertThat(getName("bobby", authList), hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")); + assertThat(getName("bobby", authList), hasAuthLocation(1.05, 2.1, 4.2, "world")); + assertThat(getName("bobby", authList).getPassword(), equalToHash("$SHA$11aa0706173d7272$dbba966")); + assertThat(getName("twofields", authList), hasAuthBasicData("twofields", "twoFields", "your@email.com", "127.0.0.1")); + assertThat(getName("twofields", authList).getPassword(), equalToHash("hash1234")); + assertThat(getName("threefields", authList), hasAuthBasicData("threefields", "threeFields", "your@email.com", "33.33.33.33")); + assertThat(getName("fourfields", authList), hasAuthBasicData("fourfields", "fourFields", "your@email.com", "4.4.4.4")); + assertThat(getName("fourfields", authList).getLastLogin(), equalTo(404040404L)); + assertThat(getName("sevenfields", authList), hasAuthLocation(7.7, 14.14, 21.21, "world")); + assertThat(getName("eightfields", authList), hasAuthLocation(8.8, 17.6, 26.4, "eightworld")); + assertThat(getName("eightfields", authList).getLastLogin(), equalTo(1234567888L)); + assertThat(getName("eightfields", authList).getPassword(), equalToHash("hash8168")); + } + + @Test + public void shouldAddAuth() { + // given / when + boolean response = dataSource.saveAuth( + PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").ip("123.45.67.77").build()); + List authList = dataSource.getAllAuths(); + + // then + assertThat(response, equalTo(true)); + assertThat(authList, hasSize(8)); + assertThat(authList, hasItem(hasAuthBasicData("test", "test", "user@EXAMPLE.org", "123.45.67.77"))); + } + + private static PlayerAuth getName(String name, Collection auths) { + for (PlayerAuth auth : auths) { + if (name.equals(auth.getNickname())) { + return auth; + } + } + throw new IllegalStateException("Did not find auth with name '" + name + "'"); + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java new file mode 100644 index 00000000..9986725c --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/MySqlResourceClosingTest.java @@ -0,0 +1,29 @@ +package fr.xephi.authme.datasource; + +import com.zaxxer.hikari.HikariDataSource; +import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.settings.NewSetting; + +import java.lang.reflect.Method; +import java.sql.Connection; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Resource closing test for {@link MySQL}. + */ +public class MySqlResourceClosingTest extends AbstractResourceClosingTest { + + public MySqlResourceClosingTest(Method method, String name, HashAlgorithm algorithm) { + super(method, name, algorithm); + } + + @Override + protected DataSource createDataSource(NewSetting settings, Connection connection) throws Exception { + HikariDataSource hikariDataSource = mock(HikariDataSource.class); + given(hikariDataSource.getConnection()).willReturn(connection); + return new MySQL(settings, hikariDataSource); + } + +} diff --git a/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java b/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java new file mode 100644 index 00000000..3275aafa --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/SQLiteResourceClosingTest.java @@ -0,0 +1,23 @@ +package fr.xephi.authme.datasource; + +import fr.xephi.authme.security.HashAlgorithm; +import fr.xephi.authme.settings.NewSetting; + +import java.lang.reflect.Method; +import java.sql.Connection; + +/** + * Resource closing test for {@link SQLite}. + */ +public class SQLiteResourceClosingTest extends AbstractResourceClosingTest { + + public SQLiteResourceClosingTest(Method method, String name, HashAlgorithm algorithm) { + super(method, name, algorithm); + } + + @Override + protected DataSource createDataSource(NewSetting settings, Connection connection) throws Exception { + return new SQLite(settings, connection); + } + +} diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java index 7cc7c55d..5c165039 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java @@ -1,14 +1,13 @@ package fr.xephi.authme.process.email; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.entity.Player; import org.junit.After; @@ -27,11 +26,10 @@ import static org.mockito.Mockito.when; */ public class AsyncAddEmailTest { - private Messages messages; private Player player; private DataSource dataSource; private PlayerCache playerCache; - private NewSetting settings; + private ProcessService service; @BeforeClass public static void setUp() { @@ -42,10 +40,10 @@ public class AsyncAddEmailTest { // Clean up the fields to ensure that no test uses elements of another test @After public void removeFieldValues() { - messages = null; player = null; dataSource = null; playerCache = null; + service = null; } @Test @@ -61,11 +59,11 @@ public class AsyncAddEmailTest { given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(true); // when - process.process(); + process.run(); // then verify(dataSource).updateEmail(auth); - verify(messages).send(player, MessageKey.EMAIL_ADDED_SUCCESS); + verify(service).send(player, MessageKey.EMAIL_ADDED_SUCCESS); verify(auth).setEmail("my.mail@example.org"); verify(playerCache).updatePlayer(auth); } @@ -83,11 +81,11 @@ public class AsyncAddEmailTest { given(dataSource.updateEmail(any(PlayerAuth.class))).willReturn(false); // when - process.process(); + process.run(); // then verify(dataSource).updateEmail(auth); - verify(messages).send(player, MessageKey.ERROR); + verify(service).send(player, MessageKey.ERROR); } @Test @@ -102,10 +100,10 @@ public class AsyncAddEmailTest { given(dataSource.isEmailStored("some.mail@example.org")).willReturn(false); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.USAGE_CHANGE_EMAIL); + verify(service).send(player, MessageKey.USAGE_CHANGE_EMAIL); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -121,10 +119,10 @@ public class AsyncAddEmailTest { given(dataSource.isEmailStored("invalid_mail")).willReturn(false); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.INVALID_EMAIL); + verify(service).send(player, MessageKey.INVALID_EMAIL); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -140,10 +138,10 @@ public class AsyncAddEmailTest { given(dataSource.isEmailStored("player@mail.tld")).willReturn(true); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); + verify(service).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -156,10 +154,10 @@ public class AsyncAddEmailTest { given(dataSource.isAuthAvailable("Username12")).willReturn(true); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.LOGIN_MESSAGE); + verify(service).send(player, MessageKey.LOGIN_MESSAGE); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -170,13 +168,13 @@ public class AsyncAddEmailTest { given(player.getName()).willReturn("user"); given(playerCache.isAuthenticated("user")).willReturn(false); given(dataSource.isAuthAvailable("user")).willReturn(false); - Settings.emailRegistration = true; + given(service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.REGISTER_EMAIL_MESSAGE); + verify(service).send(player, MessageKey.REGISTER_EMAIL_MESSAGE); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -187,13 +185,13 @@ public class AsyncAddEmailTest { given(player.getName()).willReturn("user"); given(playerCache.isAuthenticated("user")).willReturn(false); given(dataSource.isAuthAvailable("user")).willReturn(false); - Settings.emailRegistration = false; + given(service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(false); // when - process.process(); + process.run(); // then - verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + verify(service).send(player, MessageKey.REGISTER_MESSAGE); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); } @@ -204,14 +202,12 @@ public class AsyncAddEmailTest { * @return The created process */ private AsyncAddEmail createProcess(String email) { - messages = mock(Messages.class); - AuthMe authMe = mock(AuthMe.class); - when(authMe.getMessages()).thenReturn(messages); player = mock(Player.class); dataSource = mock(DataSource.class); playerCache = mock(PlayerCache.class); - settings = mock(NewSetting.class); - return new AsyncAddEmail(player, authMe, email, dataSource, playerCache, settings); + service = mock(ProcessService.class); + when(service.getSettings()).thenReturn(mock(NewSetting.class)); + return new AsyncAddEmail(player, email, dataSource, playerCache, service); } } diff --git a/src/test/resources/datasource-integration/flatfile-test.txt b/src/test/resources/datasource-integration/flatfile-test.txt new file mode 100644 index 00000000..da5a1312 --- /dev/null +++ b/src/test/resources/datasource-integration/flatfile-test.txt @@ -0,0 +1,7 @@ +Bobby:$SHA$11aa0706173d7272$dbba966:123.45.67.89:1449136800:1.05:2.1:4.2:world:your@email.com +user:b28c32f624a4eb161d6adc9acb5bfc5b:34.56.78.90:1453242857:124.1:76.3:-127.8:nether:user@example.org +twoFields:hash1234 +threeFields:hash369:33.33.33.33 +fourFields:$hash$4444:4.4.4.4:404040404 +sevenFields:hash7749:5.5.5.55:1414141414:7.7:14.14:21.21 +eightFields:hash8168:6.6.6.66:1234567888:8.8:17.6:26.4:eightworld