diff --git a/README.md b/README.md index c8a12dd0..9b3d75f0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ #####News: -- We are going to release a stable (and working!) version of AuthMeBungee, in the meantime, a fixed version of the unofficial AuthMeBridgeBukkit plugin can be found at: https://github.com/Xephi/AuthMeReloaded/issues/557 Thanks to @beetle2k +- Latest version of AuthMeBridge is finally compatible with latest AuthMe snapshots! ;) #####Development tools: @@ -62,7 +62,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 82dfd0b4..aacd54fb 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-R0.1-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,15 @@ + org.eluder.coveralls coveralls-maven-plugin 4.1.0 - + + false + + @@ -293,6 +268,8 @@ UTF-8 UTF-8 true + public + false @@ -359,7 +336,7 @@ com.zaxxer HikariCP - 2.4.3 + 2.4.5 compile @@ -371,8 +348,8 @@ org.slf4j - slf4j-jdk14 - 1.7.16 + slf4j-simple + 1.7.20 compile true @@ -413,7 +390,7 @@ com.google.code.gson gson - 2.6.1 + 2.6.2 compile true @@ -687,7 +664,7 @@ net.minelink CombatTagPlus - 1.2.1-SNAPSHOT + 1.2.2-SNAPSHOT provided diff --git a/src/main/java/fr/xephi/authme/AntiBot.java b/src/main/java/fr/xephi/authme/AntiBot.java index f93c2605..75a2a3b5 100644 --- a/src/main/java/fr/xephi/authme/AntiBot.java +++ b/src/main/java/fr/xephi/authme/AntiBot.java @@ -2,92 +2,106 @@ package fr.xephi.authme; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerStatePermission; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.util.Wrapper; -import org.bukkit.Bukkit; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.ProtectionSettings; +import fr.xephi.authme.util.BukkitService; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; +import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; +import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND; + /** * The AntiBot Service Management class. */ public class AntiBot { - private static final Wrapper wrapper = Wrapper.getInstance(); - private static final AuthMe plugin = wrapper.getAuthMe(); - private static final Messages messages = wrapper.getMessages(); - private static final List antibotPlayers = new ArrayList<>(); - private static AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED; + private final NewSetting settings; + private final Messages messages; + private final PermissionsManager permissionsManager; + private final BukkitService bukkitService; + private final List antibotPlayers = new ArrayList<>(); + private AntiBotStatus antiBotStatus = AntiBotStatus.DISABLED; - public static void setupAntiBotService() { - if (!Settings.enableAntiBot) { - return; + public AntiBot(NewSetting settings, Messages messages, PermissionsManager permissionsManager, + BukkitService bukkitService) { + this.settings = settings; + this.messages = messages; + this.permissionsManager = permissionsManager; + this.bukkitService = bukkitService; + + setupAntiBotService(); + } + + private void setupAntiBotService() { + if (settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)) { + bukkitService.scheduleSyncDelayedTask(new Runnable() { + @Override + public void run() { + antiBotStatus = AntiBotStatus.LISTENING; + } + }, 2 * TICKS_PER_MINUTE); } - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - @Override - public void run() { + } + + public void overrideAntiBotStatus(boolean activated) { + if (antiBotStatus != AntiBotStatus.DISABLED) { + if (activated) { + antiBotStatus = AntiBotStatus.ACTIVE; + } else { antiBotStatus = AntiBotStatus.LISTENING; } - }, 2400); - } - - public static void overrideAntiBotStatus(boolean activated) { - if (antiBotStatus == AntiBotStatus.DISABLED) { - return; - } - if (activated) { - antiBotStatus = AntiBotStatus.ACTIVE; - } else { - antiBotStatus = AntiBotStatus.LISTENING; } } - public static AntiBotStatus getAntiBotStatus() { + public AntiBotStatus getAntiBotStatus() { return antiBotStatus; } - public static void activateAntiBot() { + public void activateAntiBot() { antiBotStatus = AntiBotStatus.ACTIVE; for (String s : messages.retrieve(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)) { - Bukkit.broadcastMessage(s); + bukkitService.broadcastMessage(s); } - wrapper.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + final int duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION); + bukkitService.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { if (antiBotStatus == AntiBotStatus.ACTIVE) { antiBotStatus = AntiBotStatus.LISTENING; antibotPlayers.clear(); for (String s : messages.retrieve(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE)) { - Bukkit.broadcastMessage(s.replace("%m", "" + Settings.antiBotDuration)); + bukkitService.broadcastMessage(s.replace("%m", Integer.toString(duration))); } } } - }, Settings.antiBotDuration * 1200); + }, duration * TICKS_PER_MINUTE); } - public static void checkAntiBot(final Player player) { + public void checkAntiBot(final Player player) { if (antiBotStatus == AntiBotStatus.ACTIVE || antiBotStatus == AntiBotStatus.DISABLED) { return; } - if (plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)) { + if (permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)) { return; } antibotPlayers.add(player.getName().toLowerCase()); - if (antibotPlayers.size() > Settings.antiBotSensibility) { + if (antibotPlayers.size() > settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)) { activateAntiBot(); return; } - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + bukkitService.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { antibotPlayers.remove(player.getName().toLowerCase()); } - }, 300); + }, 15 * TICKS_PER_SECOND); } public enum AntiBotStatus { diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 622ceafe..018ebf31 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -1,11 +1,8 @@ package fr.xephi.authme; -import com.earth2me.essentials.Essentials; -import com.google.common.base.Charsets; -import com.google.common.io.Resources; -import com.onarandombox.MultiverseCore.MultiverseCore; import fr.xephi.authme.api.API; import fr.xephi.authme.api.NewAPI; +import fr.xephi.authme.cache.IpAddressManager; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.backup.JsonCache; @@ -24,7 +21,7 @@ import fr.xephi.authme.datasource.FlatFile; import fr.xephi.authme.datasource.MySQL; import fr.xephi.authme.datasource.SQLite; import fr.xephi.authme.hooks.BungeeCordMessage; -import fr.xephi.authme.hooks.EssSpawn; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.listener.AuthMeBlockListener; import fr.xephi.authme.listener.AuthMeEntityListener; import fr.xephi.authme.listener.AuthMeInventoryPacketAdapter; @@ -42,24 +39,29 @@ 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.SpawnLoader; 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.PurgeSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.settings.properties.SettingsFieldRetriever; +import fr.xephi.authme.settings.propertymap.PropertyMap; +import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.CollectionUtils; +import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.MigrationService; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; -import net.minelink.ctplus.CombatTagPlus; import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -72,8 +74,6 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; import java.io.File; -import java.io.IOException; -import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; @@ -111,7 +111,7 @@ public class AuthMe extends JavaPlugin { 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 @@ -119,26 +119,26 @@ 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 */ - public Essentials ess; - public MultiverseCore multiverse; - public CombatTagPlus combatTagPlus; public AuthMeInventoryPacketAdapter inventoryProtector; public AuthMeTabCompletePacketAdapter tabComplete; public AuthMeTablistPacketAdapter tablistHider; private Management management; - private CommandHandler commandHandler = null; - private PermissionsManager permsMan = null; + private CommandHandler commandHandler; + private PermissionsManager permsMan; private NewSetting newSettings; private Messages messages; private JsonCache playerBackup; private PasswordSecurity passwordSecurity; private DataSource database; + private IpAddressManager ipAddressManager; + private PluginHooks pluginHooks; + private SpawnLoader spawnLoader; + private AntiBot antiBot; + private boolean autoPurging; /** * Get the plugin's instance. @@ -248,16 +248,24 @@ public class AuthMe extends JavaPlugin { return; } + pluginHooks = new PluginHooks(server.getPluginManager()); + MigrationService.changePlainTextToSha256(newSettings, database, new SHA256()); - passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.PASSWORD_HASH), - Bukkit.getPluginManager(), newSettings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)); + passwordSecurity = new PasswordSecurity(getDataSource(), newSettings, Bukkit.getPluginManager()); + ipAddressManager = new IpAddressManager(newSettings); + + // Initialize spawn loader + spawnLoader = new SpawnLoader(getDataFolder(), newSettings, pluginHooks); + // Set up the permissions manager and command handler permsMan = initializePermissionsManager(); - commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings); + commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings, ipAddressManager, + pluginHooks, spawnLoader, antiBot); - // Setup otherAccounts file - this.otherAccounts = OtherAccounts.getInstance(); + // AntiBot delay + BukkitService bukkitService = new BukkitService(this); + antiBot = new AntiBot(newSettings, messages, permsMan, bukkitService); // Set up Metrics MetricsStarter.setupMetrics(plugin, newSettings); @@ -265,25 +273,12 @@ public class AuthMe extends JavaPlugin { // Set console filter setupConsoleFilter(); - // AntiBot delay - AntiBot.setupAntiBotService(); - // Download and load GeoIp.dat file if absent GeoLiteAPI.isDataAvailable(); // Set up the mail API setupMailApi(); - // Hooks - // Check Combat Tag Plus Version - checkCombatTagPlus(); - - // Check Multiverse - checkMultiverse(); - - // Check Essentials - checkEssentials(); - // Check if the ProtocolLib is available. If so we could listen for // inventory protection checkProtocolLib(); @@ -297,26 +292,24 @@ public class AuthMe extends JavaPlugin { playerBackup = new JsonCache(); // Set the DataManager - dataManager = new DataManager(this); + dataManager = new DataManager(this, pluginHooks); // Set up the new API setupApi(); // Set up the management - management = new Management(this, newSettings); + ProcessService processService = new ProcessService(newSettings, messages, this, database, ipAddressManager, + passwordSecurity, pluginHooks, spawnLoader); + management = new Management(this, processService, database, PlayerCache.getInstance()); // Set up the BungeeCord hook - setupBungeeCordHook(); + setupBungeeCordHook(newSettings, ipAddressManager); // Reload support hook reloadSupportHook(); // Register event listeners - registerEventListeners(); - - // Purge on start if enabled - autoPurge(); - + registerEventListeners(messages, database, management, pluginHooks, spawnLoader, antiBot); // Start Email recall task if needed scheduleRecallEmailTask(); @@ -330,6 +323,26 @@ public class AuthMe extends JavaPlugin { // Successful message ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " correctly enabled!"); + + // Purge on start if enabled + runAutoPurge(); + } + + /** + * Reload certain components. + * + * @throws Exception if an error occurs + */ + public void reload() throws Exception { + newSettings.reload(); + // We do not change database type for consistency issues, but we'll output a note in the logs + if (!newSettings.getProperty(DatabaseSettings.BACKEND).equals(database.getType())) { + ConsoleLogger.info("Note: cannot change database type during /authme reload"); + } + database.reload(); + messages.reload(newSettings.getMessagesFile()); + passwordSecurity.reload(newSettings); + spawnLoader.initialize(newSettings); } /** @@ -361,15 +374,16 @@ public class AuthMe extends JavaPlugin { /** * Register all event listeners. */ - private void registerEventListeners() { + private void registerEventListeners(Messages messages, DataSource dataSource, Management management, + PluginHooks pluginHooks, SpawnLoader spawnLoader, AntiBot antiBot) { // Get the plugin manager instance PluginManager pluginManager = server.getPluginManager(); // Register event listeners - pluginManager.registerEvents(new AuthMePlayerListener(this), this); + pluginManager.registerEvents(new AuthMePlayerListener(this, messages, dataSource, antiBot, management), this); pluginManager.registerEvents(new AuthMeBlockListener(), this); pluginManager.registerEvents(new AuthMeEntityListener(), this); - pluginManager.registerEvents(new AuthMeServerListener(this), this); + pluginManager.registerEvents(new AuthMeServerListener(this, messages, pluginHooks, spawnLoader), this); // Try to register 1.6 player listeners try { @@ -407,20 +421,23 @@ public class AuthMe extends JavaPlugin { /** * Set up the BungeeCord hook. */ - private void setupBungeeCordHook() { - if (newSettings.getProperty(HooksSettings.BUNGEECORD)) { + private void setupBungeeCordHook(NewSetting settings, IpAddressManager ipAddressManager) { + if (settings.getProperty(HooksSettings.BUNGEECORD)) { Bukkit.getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); - Bukkit.getMessenger().registerIncomingPluginChannel(this, "BungeeCord", new BungeeCordMessage(this)); + Bukkit.getMessenger().registerIncomingPluginChannel( + this, "BungeeCord", new BungeeCordMessage(this, ipAddressManager)); } } private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages, - PasswordSecurity passwordSecurity, NewSetting settings) { + PasswordSecurity passwordSecurity, NewSetting settings, + IpAddressManager ipAddressManager, PluginHooks pluginHooks, + SpawnLoader spawnLoader, AntiBot antiBot) { HelpProvider helpProvider = new HelpProvider(permissionsManager, settings.getProperty(HELP_HEADER)); Set baseCommands = CommandInitializer.buildCommands(); CommandMapper mapper = new CommandMapper(baseCommands, permissionsManager); - CommandService commandService = new CommandService( - this, mapper, helpProvider, messages, passwordSecurity, permissionsManager, settings); + CommandService commandService = new CommandService(this, mapper, helpProvider, messages, passwordSecurity, + permissionsManager, settings, ipAddressManager, pluginHooks, spawnLoader, antiBot); return new CommandHandler(commandService); } @@ -455,8 +472,10 @@ public class AuthMe extends JavaPlugin { private NewSetting createNewSetting() { File configFile = new File(getDataFolder(), "config.yml"); - return SettingsMigrationService.copyFileFromResource(configFile, "config.yml") - ? new NewSetting(configFile, getDataFolder()) + PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields(); + SettingsMigrationService migrationService = new SettingsMigrationService(); + return FileUtils.copyFileFromResource(configFile, "config.yml") + ? new NewSetting(configFile, getDataFolder(), properties, migrationService) : null; } @@ -546,6 +565,8 @@ public class AuthMe extends JavaPlugin { * * @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 * @see AuthMe#database */ public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException { @@ -600,80 +621,20 @@ public class AuthMe extends JavaPlugin { return manager; } - /** - * Get the permissions manager instance. - * - * @return Permissions Manager instance. - */ - public PermissionsManager getPermissionsManager() { - return this.permsMan; - } - // Set the console filter to remove the passwords private void setLog4JFilter() { Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() { - @Override public void run() { - org.apache.logging.log4j.core.Logger coreLogger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); - coreLogger.addFilter(new Log4JFilter()); + org.apache.logging.log4j.core.Logger logger; + logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); + logger.addFilter(new Log4JFilter()); + logger = (org.apache.logging.log4j.core.Logger) LogManager.getLogger("net.minecraft"); + logger.addFilter(new Log4JFilter()); } }); } - // Get the Multiverse plugin - public void checkMultiverse() { - if (Settings.multiverse && server.getPluginManager().isPluginEnabled("Multiverse-Core")) { - try { - multiverse = (MultiverseCore) server.getPluginManager().getPlugin("Multiverse-Core"); - ConsoleLogger.info("Hooked correctly with Multiverse-Core"); - } catch (Exception | NoClassDefFoundError ignored) { - multiverse = null; - } - } else { - multiverse = null; - } - } - - // Get the Essentials plugin - public void checkEssentials() { - if (server.getPluginManager().isPluginEnabled("Essentials")) { - try { - ess = (Essentials) server.getPluginManager().getPlugin("Essentials"); - ConsoleLogger.info("Hooked correctly with Essentials"); - } catch (Exception | NoClassDefFoundError ignored) { - ess = null; - } - } else { - ess = null; - } - if (server.getPluginManager().isPluginEnabled("EssentialsSpawn")) { - try { - essentialsSpawn = new EssSpawn().getLocation(); - ConsoleLogger.info("Hooked correctly with EssentialsSpawn"); - } catch (Exception e) { - essentialsSpawn = null; - ConsoleLogger.showError("Can't read the /plugins/Essentials/spawn.yml file!"); - } - } else { - essentialsSpawn = null; - } - } - - // Check the presence of CombatTag - public void checkCombatTagPlus() { - if (server.getPluginManager().isPluginEnabled("CombatTagPlus")) { - try { - combatTagPlus = (CombatTagPlus) server.getPluginManager().getPlugin("CombatTagPlus"); - ConsoleLogger.info("Hooked correctly with CombatTagPlus"); - } catch (Exception | NoClassDefFoundError ignored) { - combatTagPlus = null; - } - } else { - combatTagPlus = null; - } - } - // Check the presence of the ProtocolLib plugin public void checkProtocolLib() { if (!server.getPluginManager().isPluginEnabled("ProtocolLib")) { @@ -716,7 +677,10 @@ public class AuthMe extends JavaPlugin { } String name = player.getName().toLowerCase(); if (PlayerCache.getInstance().isAuthenticated(name) && !player.isDead() && Settings.isSaveQuitLocationEnabled) { - final PlayerAuth auth = new PlayerAuth(player.getName().toLowerCase(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), player.getWorld().getName(), player.getName()); + final PlayerAuth auth = PlayerAuth.builder() + .name(player.getName().toLowerCase()) + .realName(player.getName()) + .location(player.getLocation()).build(); database.updateQuitLoc(auth); } if (LimboCache.getInstance().hasLimboPlayer(name)) { @@ -726,8 +690,8 @@ public class AuthMe extends JavaPlugin { } Utils.addNormal(player, limbo.getGroup()); - player.setOp(limbo.getOperator()); - limbo.getTimeoutTaskId().cancel(); + player.setOp(limbo.isOperator()); + limbo.getTimeoutTask().cancel(); LimboCache.getInstance().deleteLimboPlayer(name); if (this.playerBackup.doesCacheExist(player)) { this.playerBackup.removeCache(player); @@ -747,34 +711,43 @@ public class AuthMe extends JavaPlugin { } // Purge inactive players from the database, as defined in the configuration - private void autoPurge() { - if (!Settings.usePurge) { + private void runAutoPurge() { + if (!newSettings.getProperty(PurgeSettings.USE_AUTO_PURGE) || autoPurging) { return; } - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DATE, -(Settings.purgeDelay)); - long until = calendar.getTimeInMillis(); - List cleared = database.autoPurgeDatabase(until); - if (CollectionUtils.isEmpty(cleared)) { - return; - } - ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!"); - if (Settings.purgeEssentialsFile && this.ess != null) - dataManager.purgeEssentials(cleared); - if (Settings.purgePlayerDat) - dataManager.purgeDat(cleared); - if (Settings.purgeLimitedCreative) - dataManager.purgeLimitedCreative(cleared); - if (Settings.purgeAntiXray) - dataManager.purgeAntiXray(cleared); - if (Settings.purgePermissions) - dataManager.purgePermissions(cleared); + autoPurging = true; + server.getScheduler().runTaskAsynchronously(this, new Runnable() { + @Override + public void run() { + ConsoleLogger.info("AutoPurging the Database..."); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DATE, -newSettings.getProperty(PurgeSettings.DAYS_BEFORE_REMOVE_PLAYER)); + long until = calendar.getTimeInMillis(); + List cleared = database.autoPurgeDatabase(until); + if (CollectionUtils.isEmpty(cleared)) { + return; + } + ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!"); + if (newSettings.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) && pluginHooks.isEssentialsAvailable()) + dataManager.purgeEssentials(cleared); + if (newSettings.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) + dataManager.purgeDat(cleared); + if (newSettings.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) + dataManager.purgeLimitedCreative(cleared); + if (newSettings.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) + dataManager.purgeAntiXray(cleared); + if (newSettings.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) + dataManager.purgePermissions(cleared); + ConsoleLogger.info("AutoPurge Finished!"); + autoPurging = false; + } + }); } // Return the spawn location of a player @Deprecated public Location getSpawnLocation(Player player) { - return Spawn.getInstance().getSpawnLocation(player); + return spawnLoader.getSpawnLocation(player); } private void scheduleRecallEmailTask() { @@ -786,7 +759,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); @@ -794,77 +767,37 @@ public class AuthMe extends JavaPlugin { } } } - }, 1, 1200 * Settings.delayRecall); + }, 1, 1200 * newSettings.getProperty(EmailSettings.DELAY_RECALL)); } public String replaceAllInfo(String message, Player player) { String playersOnline = Integer.toString(Utils.getOnlinePlayers().size()); + String ipAddress = ipAddressManager.getPlayerIp(player); return message .replace("&", "\u00a7") .replace("{PLAYER}", player.getName()) .replace("{ONLINE}", playersOnline) .replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers())) - .replace("{IP}", getIP(player)) + .replace("{IP}", ipAddress) .replace("{LOGINS}", Integer.toString(PlayerCache.getInstance().getLogged())) .replace("{WORLD}", player.getWorld().getName()) .replace("{SERVER}", server.getServerName()) .replace("{VERSION}", server.getBukkitVersion()) - .replace("{COUNTRY}", GeoLiteAPI.getCountryName(getIP(player))); - } - - /** - * Gets a player's real IP through VeryGames method. - * - * @param player The player to process. - */ - @Deprecated - public void getVerygamesIp(final Player player) { - final String name = player.getName().toLowerCase(); - String currentIp = player.getAddress().getAddress().getHostAddress(); - if (realIp.containsKey(name)) { - currentIp = realIp.get(name); - } - String sUrl = "http://monitor-1.verygames.net/api/?action=ipclean-real-ip&out=raw&ip=%IP%&port=%PORT%"; - sUrl = sUrl.replace("%IP%", currentIp).replace("%PORT%", "" + player.getAddress().getPort()); - try { - String result = Resources.toString(new URL(sUrl), Charsets.UTF_8); - if (!StringUtils.isEmpty(result) && !result.equalsIgnoreCase("error") && !result.contains("error")) { - currentIp = result; - realIp.put(name, currentIp); - } - } catch (IOException e) { - ConsoleLogger.showError("Could not fetch Very Games API with URL '" + - sUrl + "' - " + StringUtils.formatException(e)); - } - } - - public String getIP(final Player player) { - final String name = player.getName().toLowerCase(); - String ip = player.getAddress().getAddress().getHostAddress(); - if (realIp.containsKey(name)) { - ip = realIp.get(name); - } - return ip; + .replace("{COUNTRY}", GeoLiteAPI.getCountryName(ipAddress)); } public boolean isLoggedIp(String name, String ip) { int count = 0; for (Player player : Utils.getOnlinePlayers()) { - if (ip.equalsIgnoreCase(getIP(player)) && database.isLogged(player.getName().toLowerCase()) && !player.getName().equalsIgnoreCase(name)) - count++; + if (ip.equalsIgnoreCase(ipAddressManager.getPlayerIp(player)) + && database.isLogged(player.getName().toLowerCase()) + && !player.getName().equalsIgnoreCase(name)) { + ++count; + } } 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. * @@ -888,6 +821,15 @@ public class AuthMe extends JavaPlugin { return commandHandler.processCommand(sender, commandLabel, args); } + /** + * Get the permissions manager instance. + * + * @return Permissions Manager instance. + */ + public PermissionsManager getPermissionsManager() { + return this.permsMan; + } + /** * Return the management instance. * @@ -905,4 +847,8 @@ public class AuthMe extends JavaPlugin { return passwordSecurity; } + public PluginHooks getPluginHooks() { + return pluginHooks; + } + } diff --git a/src/main/java/fr/xephi/authme/DataManager.java b/src/main/java/fr/xephi/authme/DataManager.java index e0afe6bf..0545611d 100644 --- a/src/main/java/fr/xephi/authme/DataManager.java +++ b/src/main/java/fr/xephi/authme/DataManager.java @@ -1,161 +1,112 @@ package fr.xephi.authme; -import java.io.File; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.properties.PurgeSettings; +import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; -import fr.xephi.authme.permission.PermissionsManager; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.util.Utils; +import java.io.File; +import java.util.ArrayList; +import java.util.List; /** */ public class DataManager { - public final AuthMe plugin; + private final AuthMe plugin; + private final PluginHooks pluginHooks; /** * Constructor for DataManager. * - * @param plugin AuthMe + * @param plugin The plugin instance + * @param pluginHooks Plugin hooks instance */ - public DataManager(AuthMe plugin) { + public DataManager(AuthMe plugin, PluginHooks pluginHooks) { this.plugin = plugin; + this.pluginHooks = pluginHooks; } - /** - * Method getOfflinePlayer. - * - * @param name String - * - * @return OfflinePlayer - */ - public synchronized OfflinePlayer getOfflinePlayer(final String name) { - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future result = executor.submit(new Callable() { - - public synchronized OfflinePlayer call() throws Exception { - OfflinePlayer result = null; - try { - for (OfflinePlayer op : Bukkit.getOfflinePlayers()) - if (op.getName().equalsIgnoreCase(name)) { - result = op; - break; - } - } catch (Exception ignored) { + private List getOfflinePlayers(List names) { + List result = new ArrayList<>(); + for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { + for (String name : names) { + if (name.equalsIgnoreCase(op.getName())) { + result.add(op); } - return result; } - }); - try { - return result.get(); - } catch (Exception e) { - return (null); - } finally { - executor.shutdown(); } + return result; } - /** - * Method purgeAntiXray. - * - * @param cleared List of String - */ - public synchronized void purgeAntiXray(List cleared) { + public void purgeAntiXray(List cleared) { int i = 0; - for (String name : cleared) { - try { - org.bukkit.OfflinePlayer player = getOfflinePlayer(name); - if (player == null) - continue; - String playerName = player.getName(); - File playerFile = new File("." + File.separator + "plugins" + File.separator + "AntiXRayData" + File.separator + "PlayerData" + File.separator + playerName); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); + File dataFolder = new File("." + File.separator + "plugins" + File.separator + "AntiXRayData" + + File.separator + "PlayerData"); + if (!dataFolder.exists() || !dataFolder.isDirectory()) { + return; + } + for (String file : dataFolder.list()) { + if (cleared.contains(file.toLowerCase())) { + File playerFile = new File(dataFolder, file); + if (playerFile.exists() && playerFile.delete()) { i++; } - } catch (Exception ignored) { } } - ConsoleLogger.info("AutoPurgeDatabase : Remove " + i + " AntiXRayData Files"); + ConsoleLogger.info("AutoPurge: Removed " + i + " AntiXRayData Files"); } - /** - * Method purgeLimitedCreative. - * - * @param cleared List of String - */ public synchronized void purgeLimitedCreative(List cleared) { int i = 0; - for (String name : cleared) { - try { - org.bukkit.OfflinePlayer player = getOfflinePlayer(name); - if (player == null) - continue; - String playerName = player.getName(); - File playerFile = new File("." + File.separator + "plugins" + File.separator + "LimitedCreative" + File.separator + "inventories" + File.separator + playerName + ".yml"); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; - } - playerFile = new File("." + File.separator + "plugins" + File.separator + "LimitedCreative" + File.separator + "inventories" + File.separator + playerName + "_creative.yml"); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; - } - playerFile = new File("." + File.separator + "plugins" + File.separator + "LimitedCreative" + File.separator + "inventories" + File.separator + playerName + "_adventure.yml"); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; - } - } catch (Exception ignored) { - } - } - ConsoleLogger.info("AutoPurgeDatabase : Remove " + i + " LimitedCreative Survival, Creative and Adventure files"); - } - - /** - * Method purgeDat. - * - * @param cleared List of String - */ - public synchronized void purgeDat(List cleared) { - int i = 0; - for (String name : cleared) { - try { - org.bukkit.OfflinePlayer player = getOfflinePlayer(name); - if (player == null) { - continue; - } - - try { - File playerFile = new File(plugin.getServer().getWorldContainer() + File.separator + Settings.defaultWorld + File.separator + "players" + File.separator + player.getUniqueId() + ".dat"); - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; - } catch (Exception ignore) { - File playerFile = new File(plugin.getServer().getWorldContainer() + File.separator + Settings.defaultWorld + File.separator + "players" + File.separator + player.getName() + ".dat"); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; + File dataFolder = new File("." + File.separator + "plugins" + File.separator + "LimitedCreative" + + File.separator + "inventories"); + for (String file : dataFolder.list()) { + String name = file; + int idx; + idx = file.lastIndexOf("_creative.yml"); + if (idx != -1) { + name = name.substring(0, idx); + } else { + idx = file.lastIndexOf("_adventure.yml"); + if (idx != -1) { + name = name.substring(0, idx); + } else { + idx = file.lastIndexOf(".yml"); + if (idx != -1) { + name = name.substring(0, idx); } } - } catch (Exception ignore) { + } + if (name.equals(file)) { + continue; + } + if (cleared.contains(name.toLowerCase())) { + File dataFile = new File(dataFolder, file); + if (dataFile.exists() && dataFile.delete()) { + i++; + } } } - ConsoleLogger.info("AutoPurgeDatabase : Remove " + i + " .dat Files"); + ConsoleLogger.info("AutoPurge: Removed " + i + " LimitedCreative Survival, Creative and Adventure files"); + } + + public synchronized void purgeDat(List cleared) { + int i = 0; + File dataFolder = new File(plugin.getServer().getWorldContainer() + + File.separator + plugin.getSettings().getProperty(PurgeSettings.DEFAULT_WORLD) + + File.separator + "players"); + List offlinePlayers = getOfflinePlayers(cleared); + for (OfflinePlayer player : offlinePlayers) { + File playerFile = new File(dataFolder, Utils.getUUIDorName(player) + ".dat"); + if (playerFile.delete()) { + i++; + } + } + ConsoleLogger.info("AutoPurge: Removed " + i + " .dat Files"); } /** @@ -163,107 +114,49 @@ public class DataManager { * * @param cleared List of String */ - @SuppressWarnings("deprecation") public void purgeEssentials(List cleared) { int i = 0; - for (String name : cleared) { - try { - File playerFile = new File(plugin.ess.getDataFolder() + File.separator + "userdata" + File.separator + plugin.getServer().getOfflinePlayer(name).getUniqueId() + ".yml"); - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); + File essentialsDataFolder = pluginHooks.getEssentialsDataFolder(); + if (essentialsDataFolder == null) { + ConsoleLogger.info("Cannot purge Essentials: plugin is not loaded"); + return; + } + + final File userDataFolder = new File(essentialsDataFolder, "userdata"); + List offlinePlayers = getOfflinePlayers(cleared); + for (OfflinePlayer player : offlinePlayers) { + File playerFile = new File(userDataFolder, Utils.getUUIDorName(player) + ".yml"); + if (playerFile.exists() && playerFile.delete()) { i++; - } catch (Exception e) { - File playerFile = new File(plugin.ess.getDataFolder() + File.separator + "userdata" + File.separator + name + ".yml"); - if (playerFile.exists()) { - //noinspection ResultOfMethodCallIgnored - playerFile.delete(); - i++; - } } } - ConsoleLogger.info("AutoPurgeDatabase : Remove " + i + " EssentialsFiles"); + + ConsoleLogger.info("AutoPurge: Removed " + i + " EssentialsFiles"); } // TODO: What is this method for? Is it correct? - - /** - * @param cleared Cleared players. - */ + // TODO: Make it work with OfflinePlayers group data. public synchronized void purgePermissions(List cleared) { // Get the permissions manager, and make sure it's valid - PermissionsManager permsMan = this.plugin.getPermissionsManager(); - if (permsMan == null) + PermissionsManager permsMan = plugin.getPermissionsManager(); + if (permsMan == null) { ConsoleLogger.showError("Unable to access permissions manager instance!"); - assert permsMan != null; - + return; + } int i = 0; for (String name : cleared) { - try { - permsMan.removeAllGroups(this.getOnlinePlayerLower(name.toLowerCase())); - i++; - } catch (Exception ignored) { - } + permsMan.removeAllGroups(getOnlinePlayerLower(name)); + i++; } - ConsoleLogger.info("AutoPurgeDatabase : Removed " + i + "permissions"); - - /*int i = 0; - for (String name : cleared) { - try { - OfflinePlayer p = this.getOfflinePlayer(name); - for (String group : permission.getPlayerGroups((Player) p)) { - permission.playerRemoveGroup(null, p, group); - } - i++; - } catch (Exception e) { - } - } - ConsoleLogger.info("AutoPurgeDatabase : Remove " + i + " Permissions");*/ + ConsoleLogger.info("AutoPurge: Removed permissions from " + i + " player(s)."); } - /** - * Method isOnline. - * - * @param player Player - * @param name String - * - * @return boolean - */ - public boolean isOnline(Player player, final String name) { - if (player.isOnline()) - return true; - ExecutorService executor = Executors.newSingleThreadExecutor(); - Future result = executor.submit(new Callable() { - - @Override - public synchronized Boolean call() throws Exception { - for (OfflinePlayer op : Utils.getOnlinePlayers()) - if (op.getName().equalsIgnoreCase(name)) { - return true; - } - return false; - } - }); - try { - return result.get(); - } catch (Exception e) { - return false; - } finally { - executor.shutdown(); - } - } - - /** - * Method getOnlinePlayerLower. - * - * @param name String - * - * @return Player - */ - public Player getOnlinePlayerLower(String name) { + private Player getOnlinePlayerLower(String name) { name = name.toLowerCase(); for (Player player : Utils.getOnlinePlayers()) { - if (player.getName().equalsIgnoreCase(name)) + if (player.getName().equalsIgnoreCase(name)) { return player; + } } return null; } diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index 03fed1a8..0f543762 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) { @@ -65,11 +69,20 @@ public class NewAPI { return plugin; } + /** + * Gather the version number of the plugin. + * This can be used to determine whether certain API features are available or not. + * + * @return Plugin version identifier as a string. + */ + public String getPluginVersion() { + return AuthMe.getPluginVersion(); + } + /** * 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 +90,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 +114,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 +141,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 +148,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 +169,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 +187,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 +197,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/CaptchaManager.java b/src/main/java/fr/xephi/authme/cache/CaptchaManager.java new file mode 100644 index 00000000..342ad7c3 --- /dev/null +++ b/src/main/java/fr/xephi/authme/cache/CaptchaManager.java @@ -0,0 +1,81 @@ +package fr.xephi.authme.cache; + +import fr.xephi.authme.security.RandomString; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.SecuritySettings; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * Manager for the handling of captchas. + */ +public class CaptchaManager { + + private final int threshold; + private final int captchaLength; + private final ConcurrentHashMap playerCounts; + private final ConcurrentHashMap captchaCodes; + + public CaptchaManager(NewSetting settings) { + this.playerCounts = new ConcurrentHashMap<>(); + this.captchaCodes = new ConcurrentHashMap<>(); + this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA); + this.captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH); + } + + public void increaseCount(String player) { + String playerLower = player.toLowerCase(); + Integer currentCount = playerCounts.get(playerLower); + if (currentCount == null) { + playerCounts.put(playerLower, 1); + } else { + playerCounts.put(playerLower, currentCount + 1); + } + } + + /** + * Return whether the given player is required to solve a captcha. + * + * @param player The player to verify + * @return True if the player has to solve a captcha, false otherwise + */ + public boolean isCaptchaRequired(String player) { + Integer count = playerCounts.get(player.toLowerCase()); + return count != null && count >= threshold; + } + + /** + * Return the captcha code for the player. Creates one if none present, so call only after + * checking with {@link #isCaptchaRequired}. + * + * @param player The player + * @return The code required for the player + */ + public String getCaptchaCode(String player) { + String code = captchaCodes.get(player.toLowerCase()); + if (code == null) { + code = RandomString.generate(captchaLength); + captchaCodes.put(player.toLowerCase(), code); + } + return code; + } + + /** + * Return whether the supplied code is correct for the given player. + * + * @param player The player to check + * @param code The supplied code + * @return True if the code matches or if no captcha is required for the player, false otherwise + */ + public boolean checkCode(String player, String code) { + String savedCode = captchaCodes.get(player.toLowerCase()); + if (savedCode == null) { + return true; + } else if (savedCode.equalsIgnoreCase(code)) { + captchaCodes.remove(player.toLowerCase()); + return true; + } + return false; + } + +} diff --git a/src/main/java/fr/xephi/authme/cache/IpAddressManager.java b/src/main/java/fr/xephi/authme/cache/IpAddressManager.java new file mode 100644 index 00000000..41d1d77f --- /dev/null +++ b/src/main/java/fr/xephi/authme/cache/IpAddressManager.java @@ -0,0 +1,99 @@ +package fr.xephi.authme.cache; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.HooksSettings; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.net.URL; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Stateful manager for looking up IP address appropriately, including caching. + */ +public class IpAddressManager { + + /** Whether or not to use the VeryGames API for IP lookups. */ + private final boolean useVeryGamesIpCheck; + /** Cache for lookups via the VeryGames API. */ + private final ConcurrentHashMap ipCache; + + /** + * Constructor. + * + * @param settings The settings instance + */ + public IpAddressManager(NewSetting settings) { + this.useVeryGamesIpCheck = settings.getProperty(HooksSettings.ENABLE_VERYGAMES_IP_CHECK); + this.ipCache = new ConcurrentHashMap<>(); + } + + /** + * Return the player's IP address. If enabled in the settings, the IP address returned by the + * VeryGames API will be returned. + * + * @param player The player to look up + * @return The IP address of the player + */ + public String getPlayerIp(Player player) { + if (useVeryGamesIpCheck) { + final String playerName = player.getName().toLowerCase(); + final String cachedValue = ipCache.get(playerName); + if (cachedValue != null) { + return cachedValue; + } + + final String plainIp = player.getAddress().getAddress().getHostAddress(); + String veryGamesResult = getVeryGamesIp(plainIp, player.getAddress().getPort()); + if (veryGamesResult != null) { + ipCache.put(playerName, veryGamesResult); + return veryGamesResult; + } + } + return player.getAddress().getAddress().getHostAddress(); + } + + /** + * Add a player to the IP address cache. + * + * @param player The player to add or update the cache entry for + * @param ip The IP address to add + */ + public void addCache(String player, String ip) { + if (useVeryGamesIpCheck) { + ipCache.put(player.toLowerCase(), ip); + } + } + + /** + * Remove a player's cache entry. + * + * @param player The player to remove + */ + public void removeCache(String player) { + if (useVeryGamesIpCheck) { + ipCache.remove(player.toLowerCase()); + } + } + + // returns null if IP could not be looked up + private String getVeryGamesIp(final String plainIp, final int port) { + final String sUrl = String.format("http://monitor-1.verygames.net/api/?action=ipclean-real-ip" + + "&out=raw&ip=%s&port=%d", plainIp, port); + + try { + String result = Resources.toString(new URL(sUrl), Charsets.UTF_8); + if (!StringUtils.isEmpty(result) && !result.contains("error")) { + return result; + } + } catch (IOException e) { + ConsoleLogger.logException("Could not fetch Very Games API with URL '" + sUrl + "':", e); + } + return null; + } + +} diff --git a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java index c2bb4f70..9bc57e20 100644 --- a/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java +++ b/src/main/java/fr/xephi/authme/cache/auth/PlayerAuth.java @@ -8,20 +8,24 @@ import static com.google.common.base.Preconditions.checkNotNull; /** + * AuthMe player data. */ public class PlayerAuth { + /** The player's name in lowercase, e.g. "xephi". */ private String nickname; + /** The player's name in the correct casing, e.g. "Xephi". */ + private String realName; private HashedPassword password; + private String email; private String ip; + private int groupId; private long lastLogin; + // Fields storing the player's quit location private double x; private double y; private double z; private String world; - private int groupId; - private String email; - private String realName; /** * @param serialized String @@ -31,92 +35,19 @@ public class PlayerAuth { } /** - * Constructor for PlayerAuth. + * Constructor. Instantiate objects with the {@link #builder() builder}. * - * @param nickname String - * @param ip String - * @param lastLogin long - * @param realName String - */ - public PlayerAuth(String nickname, String ip, long lastLogin, String realName) { - this(nickname, new HashedPassword(""), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param x double - * @param y double - * @param z double - * @param world String - * @param realName String - */ - public PlayerAuth(String nickname, double x, double y, double z, String world, String realName) { - this(nickname, new HashedPassword(""), -1, "127.0.0.1", System.currentTimeMillis(), x, y, z, world, - "your@email.com", realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param email String - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, String email, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param hash String - * @param ip String - * @param lastLogin long - * @param x double - * @param y double - * @param z double - * @param world String - * @param email String - * @param realName String - */ - public PlayerAuth(String nickname, String hash, String ip, long lastLogin, double x, double y, double z, - String world, String email, String realName) { - this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName); - } - - /** - * Constructor for PlayerAuth. - * - * @param nickname String - * @param password String - * @param groupId int - * @param ip String - * @param lastLogin long - * @param x double - * @param y double - * @param z double - * @param world String - * @param email String - * @param realName String + * @param nickname all lowercase name of the player + * @param password password + * @param groupId the group id + * @param ip the associated ip address + * @param lastLogin player's last login (timestamp) + * @param x quit location: x coordinate + * @param y quit location: y coordinate + * @param z quit location: z coordinate + * @param world quit location: world name + * @param email the associated email + * @param realName the player's name with proper casing */ private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, double x, double y, double z, String world, String email, String realName) { @@ -133,24 +64,6 @@ public class PlayerAuth { this.realName = realName; } - /** - * Method set. - * - * @param auth PlayerAuth - */ - public void set(PlayerAuth auth) { - this.setEmail(auth.getEmail()); - this.setPassword(auth.getPassword()); - this.setIp(auth.getIp()); - this.setLastLogin(auth.getLastLogin()); - this.setNickname(auth.getNickname()); - this.setQuitLocX(auth.getQuitLocX()); - this.setQuitLocY(auth.getQuitLocY()); - this.setQuitLocZ(auth.getQuitLocZ()); - this.setWorld(auth.getWorld()); - this.setRealName(auth.getRealName()); - } - public void setNickname(String nickname) { this.nickname = nickname.toLowerCase(); 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 177ccce5..81e30543 100644 --- a/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java +++ b/src/main/java/fr/xephi/authme/cache/backup/JsonCache.java @@ -35,29 +35,6 @@ public class JsonCache { .create(); } - public void createCache(Player player, PlayerData playerData) { - if (player == null) { - return; - } - - String name = player.getName().toLowerCase(); - File file = new File(cacheDir, name + File.separator + "cache.json"); - if (file.exists()) { - return; - } - if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { - return; - } - - try { - String data = gson.toJson(playerData); - Files.touch(file); - Files.write(data, file, Charsets.UTF_8); - } catch (IOException e) { - ConsoleLogger.writeStackTrace(e); - } - } - public PlayerData readCache(Player player) { String name = player.getName().toLowerCase(); File file = new File(cacheDir, name + File.separator + "cache.json"); diff --git a/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java b/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java index 2679df64..49eaf9ee 100644 --- a/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java +++ b/src/main/java/fr/xephi/authme/cache/limbo/LimboCache.java @@ -16,8 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; public class LimboCache { private volatile static LimboCache singleton; - public final ConcurrentHashMap cache; - public final AuthMe plugin; + private final ConcurrentHashMap cache; + private final AuthMe plugin; private final JsonCache jsonCache; /** @@ -84,7 +84,7 @@ public class LimboCache { checkNotNull(name); name = name.toLowerCase(); if (cache.containsKey(name)) { - cache.get(name).clearTask(); + cache.get(name).clearTasks(); cache.remove(name); } } diff --git a/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java b/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java index 917a22bc..6295bbb7 100644 --- a/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java +++ b/src/main/java/fr/xephi/authme/cache/limbo/LimboPlayer.java @@ -4,14 +4,16 @@ import org.bukkit.Location; import org.bukkit.scheduler.BukkitTask; /** + * Represents a player which is not logged in and keeps track of certain states (like OP status, flying) + * which may be revoked from the player until he has logged in or registered. */ public class LimboPlayer { private final String name; private final boolean fly; private Location loc = null; - private BukkitTask timeoutTaskId = null; - private BukkitTask messageTaskId = null; + private BukkitTask timeoutTask = null; + private BukkitTask messageTask = null; private boolean operator = false; private String group; @@ -25,36 +27,36 @@ public class LimboPlayer { } /** - * Method getName. + * Return the name of the player. * - * @return String + * @return The player's name */ public String getName() { return name; } /** - * Method getLoc. + * Return the player's original location. * - * @return Location + * @return The player's location */ public Location getLoc() { return loc; } /** - * Method getOperator. + * Return whether the player is an operator or not (i.e. whether he is an OP). * - * @return boolean + * @return True if the player has OP status, false otherwise */ - public boolean getOperator() { + public boolean isOperator() { return operator; } /** - * Method getGroup. + * Return the player's permissions group. * - * @return String + * @return The permissions group the player belongs to */ public String getGroup() { return group; @@ -64,54 +66,61 @@ public class LimboPlayer { return fly; } - public BukkitTask getTimeoutTaskId() { - return timeoutTaskId; - } - /** - * Method setTimeoutTaskId. + * Return the timeout task, which kicks the player if he hasn't registered or logged in + * after a configurable amount of time. * - * @param i BukkitTask + * @return The timeout task associated to the player */ - public void setTimeoutTaskId(BukkitTask i) { - if (this.timeoutTaskId != null) { - this.timeoutTaskId.cancel(); - } - this.timeoutTaskId = i; + public BukkitTask getTimeoutTask() { + return timeoutTask; } /** - * Method getMessageTaskId. + * Set the timeout task of the player. The timeout task kicks the player after a configurable + * amount of time if he hasn't logged in or registered. * - * @return BukkitTask + * @param timeoutTask The task to set */ - public BukkitTask getMessageTaskId() { - return messageTaskId; + public void setTimeoutTask(BukkitTask timeoutTask) { + if (this.timeoutTask != null) { + this.timeoutTask.cancel(); + } + this.timeoutTask = timeoutTask; } /** - * Method setMessageTaskId. + * Return the message task reminding the player to log in or register. * - * @param messageTaskId BukkitTask + * @return The task responsible for sending the message regularly */ - public void setMessageTaskId(BukkitTask messageTaskId) { - if (this.messageTaskId != null) { - this.messageTaskId.cancel(); - } - this.messageTaskId = messageTaskId; + public BukkitTask getMessageTask() { + return messageTask; } /** - * Method clearTask. + * Set the messages task responsible for telling the player to log in or register. + * + * @param messageTask The message task to set */ - public void clearTask() { - if (messageTaskId != null) { - messageTaskId.cancel(); + public void setMessageTask(BukkitTask messageTask) { + if (this.messageTask != null) { + this.messageTask.cancel(); } - messageTaskId = null; - if (timeoutTaskId != null) { - timeoutTaskId.cancel(); + this.messageTask = messageTask; + } + + /** + * Clears all tasks associated to the player. + */ + public void clearTasks() { + if (messageTask != null) { + messageTask.cancel(); } - timeoutTaskId = null; + messageTask = null; + if (timeoutTask != null) { + timeoutTask.cancel(); + } + timeoutTask = null; } } diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index 0f73effa..b4150ade 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(); @@ -159,7 +159,7 @@ public final class CommandInitializer { .labels("getip", "ip") .description("Get player's IP") .detailedDescription("Get the IP address of the specified online player.") - .withArgument("player", "Player Name", false) + .withArgument("player", "Player name", false) .permissions(OP_ONLY, AdminPermission.GET_IP) .executableCommand(new GetIpCommand()) .build(); @@ -170,7 +170,6 @@ public final class CommandInitializer { .labels("spawn", "home") .description("Teleport to spawn") .detailedDescription("Teleport to the spawn.") - .withArgument("player", "Player Name", false) .permissions(OP_ONLY, AdminPermission.SPAWN) .executableCommand(new SpawnCommand()) .build(); diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index 5b8b5e49..a4791e68 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -1,18 +1,22 @@ package fr.xephi.authme.command; +import fr.xephi.authme.AntiBot; import fr.xephi.authme.AuthMe; +import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.cache.IpAddressManager; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.domain.Property; import org.bukkit.command.CommandSender; -import java.io.File; import java.util.List; /** @@ -28,6 +32,10 @@ public class CommandService { private final PasswordSecurity passwordSecurity; private final PermissionsManager permissionsManager; private final NewSetting settings; + private final IpAddressManager ipAddressManager; + private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; + private final AntiBot antiBot; /** * Constructor. @@ -39,10 +47,14 @@ public class CommandService { * @param passwordSecurity The Password Security instance * @param permissionsManager The permissions manager * @param settings The settings manager + * @param ipAddressManager The IP address manager + * @param pluginHooks The plugin hooks instance + * @param spawnLoader The spawn loader */ public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages, - PasswordSecurity passwordSecurity, PermissionsManager permissionsManager, - NewSetting settings) { + PasswordSecurity passwordSecurity, PermissionsManager permissionsManager, NewSetting settings, + IpAddressManager ipAddressManager, PluginHooks pluginHooks, SpawnLoader spawnLoader, + AntiBot antiBot) { this.authMe = authMe; this.messages = messages; this.helpProvider = helpProvider; @@ -50,6 +62,10 @@ public class CommandService { this.passwordSecurity = passwordSecurity; this.permissionsManager = permissionsManager; this.settings = settings; + this.ipAddressManager = ipAddressManager; + this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; + this.antiBot = antiBot; } /** @@ -163,15 +179,6 @@ public class CommandService { return messages.retrieve(key); } - /** - * Change the messages instance to retrieve messages from the given file. - * - * @param file The new file to read messages from - */ - public void reloadMessages(File file) { - messages.reload(file); - } - /** * Retrieve the given property's value. * @@ -192,4 +199,24 @@ public class CommandService { return settings; } + public IpAddressManager getIpAddressManager() { + return ipAddressManager; + } + + public PlayerCache getPlayerCache() { + return PlayerCache.getInstance(); + } + + public PluginHooks getPluginHooks() { + return pluginHooks; + } + + public SpawnLoader getSpawnLoader() { + return spawnLoader; + } + + public AntiBot getAntiBot() { + return antiBot; + } + } diff --git a/src/main/java/fr/xephi/authme/command/CommandUtils.java b/src/main/java/fr/xephi/authme/command/CommandUtils.java index 905e8922..016cbce1 100644 --- a/src/main/java/fr/xephi/authme/command/CommandUtils.java +++ b/src/main/java/fr/xephi/authme/command/CommandUtils.java @@ -8,6 +8,9 @@ import java.util.List; public final class CommandUtils { + private CommandUtils() { + } + public static int getMinNumberOfArguments(CommandDescription command) { int mandatoryArguments = 0; for (CommandArgumentDescription argument : command.getArguments()) { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index cb6bddcf..8ba9d740 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -2,7 +2,6 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; @@ -41,6 +40,8 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { commandService.send(sender, MessageKey.INVALID_PASSWORD_LENGTH); return; } + // TODO #602 20160312: The UNSAFE_PASSWORDS should be all lowercase + // -> introduce a lowercase String list property type if (commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS).contains(playerPassLowerCase)) { commandService.send(sender, MessageKey.PASSWORD_UNSAFE_ERROR); return; @@ -53,8 +54,8 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { public void run() { DataSource dataSource = commandService.getDataSource(); PlayerAuth auth = null; - if (PlayerCache.getInstance().isAuthenticated(playerNameLowerCase)) { - auth = PlayerCache.getInstance().getAuth(playerNameLowerCase); + if (commandService.getPlayerCache().isAuthenticated(playerNameLowerCase)) { + auth = commandService.getPlayerCache().getAuth(playerNameLowerCase); } else if (dataSource.isAuthAvailable(playerNameLowerCase)) { auth = dataSource.getAuth(playerNameLowerCase); } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java index 3370edf6..50b3b169 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java @@ -1,27 +1,22 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; +/** + * Teleports the player to the first spawn. + */ public class FirstSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - // Make sure the command executor is a player - try { - if (Spawn.getInstance().getFirstSpawn() != null) { - player.teleport(Spawn.getInstance().getFirstSpawn()); - } else { - player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn"); - } - } catch (NullPointerException ex) { - // TODO ljacqu 20151119: Catching NullPointerException is never a good idea. Find what can cause one instead - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().getFirstSpawn() != null) { + player.teleport(commandService.getSpawnLoader().getFirstSpawn()); + } else { + player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java index 63d65b47..53a3051c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java @@ -8,13 +8,16 @@ import org.bukkit.command.CommandSender; import java.util.List; +/** + * Returns a player's email. + */ public class GetEmailCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); - PlayerAuth auth = commandService.getDataSource().getAuth(playerName.toLowerCase()); + PlayerAuth auth = commandService.getDataSource().getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.UNKNOWN_USER); } else { diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java index 7554d0fa..d3ba537d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetIpCommand.java @@ -1,21 +1,18 @@ package fr.xephi.authme.command.executable.authme; -import java.util.List; - +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.settings.properties.HooksSettings; +import fr.xephi.authme.util.Utils; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.command.CommandService; -import fr.xephi.authme.command.ExecutableCommand; -import fr.xephi.authme.util.Utils; +import java.util.List; public class GetIpCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { - final AuthMe plugin = AuthMe.getInstance(); - // Get the player query String playerName = (arguments.size() >= 1) ? arguments.get(0) : sender.getName(); @@ -25,9 +22,12 @@ public class GetIpCommand implements ExecutableCommand { return; } - // TODO ljacqu 20151212: Revise the messages (actual IP vs. real IP...?) - sender.sendMessage(player.getName() + "'s actual IP is : " + player.getAddress().getAddress().getHostAddress() + sender.sendMessage(player.getName() + "'s IP is: " + player.getAddress().getAddress().getHostAddress() + ":" + player.getAddress().getPort()); - sender.sendMessage(player.getName() + "'s real IP is : " + plugin.getIP(player)); + + if (commandService.getProperty(HooksSettings.ENABLE_VERYGAMES_IP_CHECK)) { + sender.sendMessage(player.getName() + "'s real IP is: " + + commandService.getIpAddressManager().getPlayerIp(player)); + } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java index 434a648e..05fdab2b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/LastLoginCommand.java @@ -10,6 +10,7 @@ import java.util.Date; import java.util.List; /** + * Returns the last login date of the given user. */ public class LastLoginCommand implements ExecutableCommand { @@ -18,23 +19,24 @@ public class LastLoginCommand implements ExecutableCommand { // Get the player String playerName = (arguments.size() >= 1) ? arguments.get(0) : sender.getName(); - PlayerAuth auth = commandService.getDataSource().getAuth(playerName.toLowerCase()); + PlayerAuth auth = commandService.getDataSource().getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.USER_NOT_REGISTERED); return; } // Get the last login date - long lastLogin = auth.getLastLogin(); + final long lastLogin = auth.getLastLogin(); final long diff = System.currentTimeMillis() - lastLogin; - final String lastLoginMessage = (int) (diff / 86400000) + " days " + (int) (diff / 3600000 % 24) + " hours " - + (int) (diff / 60000 % 60) + " mins " + (int) (diff / 1000 % 60) + " secs"; + final String lastLoginMessage = (int) (diff / 86400000) + " days " + + (int) (diff / 3600000 % 24) + " hours " + + (int) (diff / 60000 % 60) + " mins " + + (int) (diff / 1000 % 60) + " secs"; Date date = new Date(lastLogin); // Show the player status sender.sendMessage("[AuthMe] " + playerName + " last login: " + date.toString()); - sender.sendMessage("[AuthMe] The player " + auth.getNickname() + " last logged in " - + lastLoginMessage + " ago."); + sender.sendMessage("[AuthMe] The player " + playerName + " last logged in " + lastLoginMessage + " ago."); sender.sendMessage("[AuthMe] Last Player's IP: " + auth.getIp()); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java index b94ce260..0ace9424 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeBannedPlayersCommand.java @@ -3,19 +3,23 @@ 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 fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PurgeSettings; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import java.util.ArrayList; import java.util.List; +/** + * Command for purging data of banned players. Depending on the settings + * it purges (deletes) data from third-party plugins as well. + */ public class PurgeBannedPlayersCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // AuthMe plugin instance - final AuthMe plugin = AuthMe.getInstance(); + final AuthMe plugin = commandService.getAuthMe(); // Get the list of banned players List bannedPlayers = new ArrayList<>(); @@ -24,14 +28,15 @@ public class PurgeBannedPlayersCommand implements ExecutableCommand { } // Purge the banned players - plugin.getDataSource().purgeBanned(bannedPlayers); - if (Settings.purgeEssentialsFile && plugin.ess != null) + commandService.getDataSource().purgeBanned(bannedPlayers); + if (commandService.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) + && commandService.getPluginHooks().isEssentialsAvailable()) plugin.dataManager.purgeEssentials(bannedPlayers); - if (Settings.purgePlayerDat) + if (commandService.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) plugin.dataManager.purgeDat(bannedPlayers); - if (Settings.purgeLimitedCreative) + if (commandService.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) plugin.dataManager.purgeLimitedCreative(bannedPlayers); - if (Settings.purgeAntiXray) + if (commandService.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) plugin.dataManager.purgeAntiXray(bannedPlayers); // Show a status message diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java index 8797145b..43e1daa5 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeCommand.java @@ -3,20 +3,23 @@ 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 fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PurgeSettings; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import java.util.Calendar; import java.util.List; +/** + * Command for purging the data of players which have not been since for a given number + * of days. Depending on the settings, this removes player data in third-party plugins as well. + */ public class PurgeCommand implements ExecutableCommand { + private static final int MINIMUM_LAST_SEEN_DAYS = 30; + @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { - // AuthMe plugin instance - AuthMe plugin = AuthMe.getInstance(); - // Get the days parameter String daysStr = arguments.get(0); @@ -30,8 +33,9 @@ public class PurgeCommand implements ExecutableCommand { } // Validate the value - if (days < 30) { - sender.sendMessage(ChatColor.RED + "You can only purge data older than 30 days"); + if (days < MINIMUM_LAST_SEEN_DAYS) { + sender.sendMessage(ChatColor.RED + "You can only purge data older than " + + MINIMUM_LAST_SEEN_DAYS + " days"); return; } @@ -47,13 +51,15 @@ public class PurgeCommand implements ExecutableCommand { sender.sendMessage(ChatColor.GOLD + "Deleted " + purged.size() + " user accounts"); // Purge other data - if (Settings.purgeEssentialsFile && plugin.ess != null) + AuthMe plugin = commandService.getAuthMe(); + if (commandService.getProperty(PurgeSettings.REMOVE_ESSENTIALS_FILES) && + commandService.getPluginHooks().isEssentialsAvailable()) plugin.dataManager.purgeEssentials(purged); - if (Settings.purgePlayerDat) + if (commandService.getProperty(PurgeSettings.REMOVE_PLAYER_DAT)) plugin.dataManager.purgeDat(purged); - if (Settings.purgeLimitedCreative) + if (commandService.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) plugin.dataManager.purgeLimitedCreative(purged); - if (Settings.purgeAntiXray) + if (commandService.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) plugin.dataManager.purgeAntiXray(purged); // Show a status message diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java index 15a6e80b..118f8460 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommand.java @@ -8,45 +8,39 @@ import org.bukkit.command.CommandSender; import java.util.List; +/** + * Removes the stored last position of a user or of all. + */ public class PurgeLastPositionCommand implements ExecutableCommand { @Override public void executeCommand(final CommandSender sender, List arguments, CommandService commandService) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); - String playerNameLowerCase = playerName.toLowerCase(); - if (playerNameLowerCase.equalsIgnoreCase("*")) - { - for (PlayerAuth auth : commandService.getDataSource().getAllAuths()) - { - // Set the last position - auth.setQuitLocX(0D); - auth.setQuitLocY(0D); - auth.setQuitLocZ(0D); - auth.setWorld("world"); + if ("*".equals(playerName)) { + for (PlayerAuth auth : commandService.getDataSource().getAllAuths()) { + resetLastPosition(auth); commandService.getDataSource().updateQuitLoc(auth); - } - sender.sendMessage("All players last position locations are now reset"); - } - else - { + } + sender.sendMessage("All players last position locations are now reset"); + } else { // Get the user auth and make sure the user exists - PlayerAuth auth = commandService.getDataSource().getAuth(playerNameLowerCase); + PlayerAuth auth = commandService.getDataSource().getAuth(playerName); if (auth == null) { commandService.send(sender, MessageKey.UNKNOWN_USER); return; } - // Set the last position - auth.setQuitLocX(0D); - auth.setQuitLocY(0D); - auth.setQuitLocZ(0D); - auth.setWorld("world"); + resetLastPosition(auth); commandService.getDataSource().updateQuitLoc(auth); - - // Show a status message - sender.sendMessage(playerNameLowerCase + "'s last position location is now reset"); + sender.sendMessage(playerName + "'s last position location is now reset"); } + } + private static void resetLastPosition(PlayerAuth auth) { + auth.setQuitLocX(0d); + auth.setQuitLocY(0d); + auth.setQuitLocZ(0d); + auth.setWorld("world"); } } 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/ReloadCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java index 0c7adf2f..91df5832 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java @@ -5,7 +5,6 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.settings.Spawn; import org.bukkit.command.CommandSender; import java.util.List; @@ -19,13 +18,7 @@ public class ReloadCommand implements ExecutableCommand { public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { AuthMe plugin = commandService.getAuthMe(); try { - commandService.getSettings().reload(); - commandService.reloadMessages(commandService.getSettings().getMessagesFile()); - Spawn.reload(); - // TODO #432: We should not reload only certain plugin entities but actually reinitialize all elements, - // i.e. here in the future we might not have setupDatabase() but Authme.onEnable(), maybe after - // a call to some destructor method - plugin.setupDatabase(commandService.getSettings()); + plugin.reload(); commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); } catch (Exception e) { sender.sendMessage("Error occurred during reload of AuthMe: aborting"); diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java index 31942bc7..a3cc7d45 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SetFirstSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().setFirstSpawn(player.getLocation())) { - player.sendMessage("[AuthMe] Correctly defined new first spawn point"); - } else { - player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().setFirstSpawn(player.getLocation())) { + player.sendMessage("[AuthMe] Correctly defined new first spawn point"); + } else { + player.sendMessage("[AuthMe] SetFirstSpawn has failed, please retry"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java index f6243300..3201ea4c 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetSpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SetSpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().setSpawn(player.getLocation())) { - player.sendMessage("[AuthMe] Correctly defined new spawn point"); - } else { - player.sendMessage("[AuthMe] SetSpawn has failed, please retry"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().setSpawn(player.getLocation())) { + player.sendMessage("[AuthMe] Correctly defined new spawn point"); + } else { + player.sendMessage("[AuthMe] SetSpawn has failed, please retry"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java index ddcc7815..04f8e563 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java @@ -1,9 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.settings.Spawn; import org.bukkit.entity.Player; import java.util.List; @@ -12,14 +10,10 @@ public class SpawnCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - try { - if (Spawn.getInstance().getSpawn() != null) { - player.teleport(Spawn.getInstance().getSpawn()); - } else { - player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn"); - } - } catch (NullPointerException ex) { - ConsoleLogger.showError(ex.getMessage()); + if (commandService.getSpawnLoader().getSpawn() != null) { + player.teleport(commandService.getSpawnLoader().getSpawn()); + } else { + player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn"); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java index e11645a9..3c36c169 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommand.java @@ -18,8 +18,9 @@ public class SwitchAntiBotCommand implements ExecutableCommand { @Override public void executeCommand(final CommandSender sender, List arguments, CommandService commandService) { + AntiBot antiBot = commandService.getAntiBot(); if (arguments.isEmpty()) { - sender.sendMessage("[AuthMe] AntiBot status: " + AntiBot.getAntiBotStatus().name()); + sender.sendMessage("[AuthMe] AntiBot status: " + antiBot.getAntiBotStatus().name()); return; } @@ -27,10 +28,10 @@ public class SwitchAntiBotCommand implements ExecutableCommand { // Enable or disable the mod if ("ON".equalsIgnoreCase(newState)) { - AntiBot.overrideAntiBotStatus(true); + antiBot.overrideAntiBotStatus(true); sender.sendMessage("[AuthMe] AntiBot Manual Override: enabled!"); } else if ("OFF".equalsIgnoreCase(newState)) { - AntiBot.overrideAntiBotStatus(false); + antiBot.overrideAntiBotStatus(false); sender.sendMessage("[AuthMe] AntiBot Manual Override: disabled!"); } else { sender.sendMessage(ChatColor.DARK_RED + "Invalid AntiBot mode!"); 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 9ed65b0b..03cf400d 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 @@ -8,6 +8,7 @@ import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; @@ -59,14 +60,14 @@ public class UnregisterAdminCommand implements ExecutableCommand { BukkitScheduler scheduler = sender.getServer().getScheduler(); if (timeOut != 0) { BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), timeOut); - LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTaskId(id); + LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTask(id); } - LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId( + LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTask( scheduler.runTask( plugin, new MessageTask(plugin, playerNameLowerCase, MessageKey.REGISTER_MESSAGE, interval) ) ); - if (Settings.applyBlindEffect) { + if (commandService.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { 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..7b99521d 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,7 +3,7 @@ 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 fr.xephi.authme.util.Utils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -71,9 +71,7 @@ public class VersionCommand implements ExecutableCommand { * @return True if the player is online, false otherwise. */ 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/command/executable/captcha/CaptchaCommand.java b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java index 8c5a0764..aba0a950 100644 --- a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java @@ -7,7 +7,6 @@ import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.settings.properties.SecuritySettings; -import fr.xephi.authme.util.Wrapper; import org.bukkit.entity.Player; import java.util.List; @@ -18,9 +17,8 @@ public class CaptchaCommand extends PlayerCommand { public void runCommand(Player player, List arguments, CommandService commandService) { final String playerNameLowerCase = player.getName().toLowerCase(); final String captcha = arguments.get(0); - Wrapper wrapper = Wrapper.getInstance(); - final AuthMe plugin = wrapper.getAuthMe(); - PlayerCache playerCache = wrapper.getPlayerCache(); + final AuthMe plugin = commandService.getAuthMe(); + PlayerCache playerCache = PlayerCache.getInstance(); // Command logic if (playerCache.isAuthenticated(playerNameLowerCase)) { diff --git a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java index d27ff298..336df152 100644 --- a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java @@ -8,7 +8,6 @@ import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.ChangePasswordTask; -import fr.xephi.authme.util.Wrapper; import org.bukkit.entity.Player; import java.util.List; @@ -24,8 +23,7 @@ public class ChangePasswordCommand extends PlayerCommand { String newPassword = arguments.get(1); String name = player.getName().toLowerCase(); - Wrapper wrapper = Wrapper.getInstance(); - final PlayerCache playerCache = wrapper.getPlayerCache(); + final PlayerCache playerCache = commandService.getPlayerCache(); if (!playerCache.isAuthenticated(name)) { commandService.send(player, MessageKey.NOT_LOGGED_IN); return; diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java index c7d8e822..48a1c3f0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java @@ -7,6 +7,7 @@ import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; @@ -17,7 +18,7 @@ public class RegisterCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments, CommandService commandService) { - if (Settings.getPasswordHash == HashAlgorithm.TWO_FACTOR) { + if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { //for two factor auth we don't need to check the usage commandService.getManagement().performRegister(player, "", ""); return; diff --git a/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java b/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java index 8b4ee564..c617ccad 100644 --- a/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java +++ b/src/main/java/fr/xephi/authme/converter/CrazyLoginConverter.java @@ -31,27 +31,13 @@ public class CrazyLoginConverter implements Converter { this.sender = sender; } - /** - * Method getInstance. - * - * @return CrazyLoginConverter - */ - public CrazyLoginConverter getInstance() { - return this; - } - - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { String fileName = Settings.crazyloginFileName; try { File source = new File(AuthMe.getInstance().getDataFolder() + File.separator + fileName); if (!source.exists()) { - sender.sendMessage("Error while trying to import datas, please put " + fileName + " in AuthMe folder!"); + sender.sendMessage("Error while trying to import data, please put " + fileName + " in AuthMe folder!"); return; } String line; @@ -59,14 +45,17 @@ public class CrazyLoginConverter implements Converter { while ((line = users.readLine()) != null) { if (line.contains("|")) { String[] args = line.split("\\|"); - if (args.length < 2) + if (args.length < 2 || "name".equalsIgnoreCase(args[0])) { continue; - if (args[0].equalsIgnoreCase("name")) - continue; - String playerName = args[0].toLowerCase(); + } + String playerName = args[0]; String psw = args[1]; if (psw != null) { - PlayerAuth auth = new PlayerAuth(playerName, psw, "127.0.0.1", System.currentTimeMillis(), playerName); + PlayerAuth auth = PlayerAuth.builder() + .name(playerName.toLowerCase()) + .realName(playerName) + .password(psw, null) + .build(); database.saveAuth(auth); } } 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/converter/RakamakConverter.java b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java index 66e995c2..289e11be 100644 --- a/src/main/java/fr/xephi/authme/converter/RakamakConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RakamakConverter.java @@ -22,9 +22,9 @@ import java.util.Map.Entry; */ public class RakamakConverter implements Converter { - public final AuthMe instance; - public final DataSource database; - public final CommandSender sender; + private final AuthMe instance; + private final DataSource database; + private final CommandSender sender; public RakamakConverter(AuthMe instance, CommandSender sender) { this.instance = instance; diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java index 42bd681d..b79657e2 100644 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java @@ -5,35 +5,46 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; +import static fr.xephi.authme.util.StringUtils.makePath; + public class RoyalAuthConverter implements Converter { + private static final String LAST_LOGIN_PATH = "timestamps.quit"; + private static final String PASSWORD_PATH = "login.password"; private final AuthMe plugin; - private final DataSource data; + private final DataSource dataSource; public RoyalAuthConverter(AuthMe plugin) { this.plugin = plugin; - this.data = plugin.getDataSource(); + this.dataSource = plugin.getDataSource(); } @Override public void run() { - for (OfflinePlayer o : plugin.getServer().getOfflinePlayers()) { + for (OfflinePlayer player : plugin.getServer().getOfflinePlayers()) { try { - String name = o.getName().toLowerCase(); - String sp = File.separator; - File file = new File("." + sp + "plugins" + sp + "RoyalAuth" + sp + "userdata" + sp + name + ".yml"); - if (data.isAuthAvailable(name)) + String name = player.getName().toLowerCase(); + File file = new File(makePath(".", "plugins", "RoyalAuth", "userdata", name + ".yml")); + + if (dataSource.isAuthAvailable(name) || !file.exists()) { continue; - if (!file.exists()) - continue; - RoyalAuthYamlReader ra = new RoyalAuthYamlReader(file); - PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); - data.saveAuth(auth); + } + FileConfiguration configuration = YamlConfiguration.loadConfiguration(file); + PlayerAuth auth = PlayerAuth.builder() + .name(name) + .password(configuration.getString(PASSWORD_PATH), null) + .lastLogin(configuration.getLong(LAST_LOGIN_PATH)) + .realName(player.getName()) + .build(); + + dataSource.saveAuth(auth); } catch (Exception e) { - ConsoleLogger.logException("Error while trying to import " + o.getName() + " RoyalAuth data", e); + ConsoleLogger.logException("Error while trying to import " + player.getName() + " RoyalAuth data", e); } } } diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java deleted file mode 100644 index c0437cb4..00000000 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthYamlReader.java +++ /dev/null @@ -1,22 +0,0 @@ -package fr.xephi.authme.converter; - -import fr.xephi.authme.settings.CustomConfiguration; - -import java.io.File; - -class RoyalAuthYamlReader extends CustomConfiguration { - - public RoyalAuthYamlReader(File file) { - super(file); - load(); - save(); - } - - public long getLastLogin() { - return getLong("timestamps.quit"); - } - - public String getHash() { - return getString("login.password"); - } -} diff --git a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java index bb25b04b..4c5b3b4f 100644 --- a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java +++ b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.Scanner; import java.util.UUID; +import static fr.xephi.authme.util.StringUtils.makePath; + class vAuthFileReader { private final AuthMe plugin; @@ -28,7 +30,7 @@ class vAuthFileReader { } public void convert() { - final File file = new File(plugin.getDataFolder().getParent() + File.separator + "vAuth" + File.separator + "passwords.yml"); + final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml")); Scanner scanner; try { scanner = new Scanner(file); @@ -46,9 +48,15 @@ class vAuthFileReader { } if (pname == null) continue; - auth = new PlayerAuth(pname.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", pname); + auth = PlayerAuth.builder() + .name(pname.toLowerCase()) + .realName(pname) + .password(password, null).build(); } else { - auth = new PlayerAuth(name.toLowerCase(), password, "127.0.0.1", System.currentTimeMillis(), "your@email.com", name); + auth = PlayerAuth.builder() + .name(name.toLowerCase()) + .realName(name) + .password(password, null).build(); } database.saveAuth(auth); } diff --git a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java b/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java index 8d9bca70..34ec2a5e 100644 --- a/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java +++ b/src/main/java/fr/xephi/authme/converter/xAuthToFlat.java @@ -50,7 +50,10 @@ class xAuthToFlat { String pl = getIdPlayer(id); String psw = getPassword(id); if (psw != null && !psw.isEmpty() && pl != null) { - PlayerAuth auth = new PlayerAuth(pl, psw, "192.168.0.1", 0, "your@email.com", pl); + PlayerAuth auth = PlayerAuth.builder() + .name(pl.toLowerCase()) + .realName(pl) + .password(psw, null).build(); database.saveAuth(auth); } } @@ -69,7 +72,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); ps.setInt(1, id); rs = ps.executeQuery(); @@ -91,7 +95,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT * FROM `%s`", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT * FROM `%s`", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); rs = ps.executeQuery(); while (rs.next()) { @@ -112,7 +117,8 @@ class xAuthToFlat { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); + String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?", + xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT)); ps = conn.prepareStatement(sql); ps.setInt(1, accountId); rs = ps.executeQuery(); diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index ad02c99f..6617f7b5 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -64,6 +64,11 @@ public class CacheDataSource implements DataSource { return cachedAuths; } + @Override + public void reload() { + source.reload(); + } + @Override public synchronized boolean isAuthAvailable(String user) { return getAuth(user) != null; @@ -162,12 +167,6 @@ public class CacheDataSource implements DataSource { } } - @Override - public void reload() { // unused method - source.reload(); - cachedAuths.invalidateAll(); - } - @Override public synchronized boolean updateEmail(final PlayerAuth auth) { boolean result = source.updateEmail(auth); diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index 6e3bc09b..5e2cc0c7 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -121,8 +121,6 @@ public interface DataSource { */ void close(); - void reload(); - /** * Method purgeBanned. * @@ -189,4 +187,9 @@ public interface DataSource { boolean isEmailStored(String email); + /** + * Reload the data source. + */ + void reload(); + } diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index c52d8e73..288012da 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; @@ -9,6 +10,7 @@ import fr.xephi.authme.settings.Settings; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; @@ -18,6 +20,8 @@ import java.util.ArrayList; import java.util.List; /** + * Deprecated flat file datasource. The only method guaranteed to work is {@link FlatFile#getAllAuths()} + * as to migrate the entries to {@link SQLite} when AuthMe starts. */ @Deprecated public class FlatFile implements DataSource { @@ -42,7 +46,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 +54,19 @@ 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 void reload() { + throw new UnsupportedOperationException("Flatfile no longer supported"); + } + @Override public synchronized boolean isAuthAvailable(String user) { BufferedReader br = null; @@ -66,19 +79,11 @@ public class FlatFile implements DataSource { return true; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return false; } @@ -100,17 +105,12 @@ public class FlatFile implements DataSource { BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(source, true)); - bw.write(auth.getNickname() + ":" + auth.getPassword() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); + bw.write(auth.getNickname() + ":" + auth.getPassword().getHash() + ":" + auth.getIp() + ":" + auth.getLastLogin() + ":" + auth.getQuitLocX() + ":" + auth.getQuitLocY() + ":" + auth.getQuitLocZ() + ":" + auth.getWorld() + ":" + auth.getEmail() + "\n"); } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(bw); } return true; } @@ -121,6 +121,7 @@ public class FlatFile implements DataSource { } @Override + // Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file. public boolean updatePassword(String user, HashedPassword password) { user = user.toLowerCase(); if (!isAuthAvailable(user)) { @@ -134,45 +135,18 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equals(user)) { - // Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file. - switch (args.length) { - case 4: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } - case 7: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]); - break; - } - case 8: { - newAuth = new PlayerAuth(args[0], password.getHash(), 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: { - newAuth = new PlayerAuth(args[0], password.getHash(), 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; - } - default: { - newAuth = new PlayerAuth(args[0], password.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]); - break; - } + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setPassword(password); } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(user); @@ -194,44 +168,19 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(auth.getNickname())) { - switch (args.length) { - case 4: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } - case 7: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]); - break; - } - case 8: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]); - break; - } - case 9: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]); - break; - } - default: { - newAuth = new PlayerAuth(args[0], args[1], auth.getIp(), auth.getLastLogin(), 0, 0, 0, "world", "your@email.com", args[0]); - break; - } + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setLastLogin(auth.getLastLogin()); + newAuth.setIp(auth.getIp()); } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(auth.getNickname()); @@ -253,23 +202,22 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(auth.getNickname())) { - newAuth = new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), auth.getQuitLocX(), auth.getQuitLocY(), auth.getQuitLocZ(), auth.getWorld(), auth.getEmail(), args[0]); + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setQuitLocX(auth.getQuitLocX()); + newAuth.setQuitLocY(auth.getQuitLocY()); + newAuth.setQuitLocZ(auth.getQuitLocZ()); + newAuth.setWorld(auth.getWorld()); + newAuth.setEmail(auth.getEmail()); + } break; } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } if (newAuth != null) { removeAuth(auth.getNickname()); @@ -301,25 +249,12 @@ public class FlatFile implements DataSource { for (String l : lines) { bw.write(l + "\n"); } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return cleared; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return cleared; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } return cleared; } @@ -345,25 +280,12 @@ public class FlatFile implements DataSource { for (String l : lines) { bw.write(l + "\n"); } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return false; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return false; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } return true; } @@ -377,35 +299,14 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equalsIgnoreCase(user)) { - switch (args.length) { - case 2: - return new PlayerAuth(args[0], args[1], "192.168.0.1", 0, "your@email.com", args[0]); - case 3: - return new PlayerAuth(args[0], args[1], args[2], 0, "your@email.com", args[0]); - case 4: - return new PlayerAuth(args[0], args[1], args[2], Long.parseLong(args[3]), "your@email.com", args[0]); - case 7: - return 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]); - case 8: - return 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]); - case 9: - return 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]); - } + return buildAuthFromArray(args); } } - } catch (FileNotFoundException ex) { - ConsoleLogger.showError(ex.getMessage()); - return null; } catch (IOException ex) { ConsoleLogger.showError(ex.getMessage()); return null; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return null; } @@ -414,10 +315,6 @@ public class FlatFile implements DataSource { public synchronized void close() { } - @Override - public void reload() { - } - @Override public boolean updateEmail(PlayerAuth auth) { if (!isAuthAvailable(auth.getNickname())) { @@ -431,7 +328,10 @@ public class FlatFile implements DataSource { while ((line = br.readLine()) != null) { String[] args = line.split(":"); if (args[0].equals(auth.getNickname())) { - newAuth = 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], auth.getEmail(), args[0]); + newAuth = buildAuthFromArray(args); + if (newAuth != null) { + newAuth.setEmail(auth.getEmail()); + } break; } } @@ -539,18 +439,8 @@ public class FlatFile implements DataSource { ConsoleLogger.showError(ex.getMessage()); } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } - if (bw != null) { - try { - bw.close(); - } catch (IOException ignored) { - } - } + silentClose(br); + silentClose(bw); } } @@ -589,19 +479,14 @@ public class FlatFile implements DataSource { ConsoleLogger.showError(ex.getMessage()); return result; } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return result; } @Override public boolean updateRealName(String user, String realName) { - return false; + throw new UnsupportedOperationException("Flat file no longer supported"); } @Override @@ -618,51 +503,56 @@ 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; + PlayerAuth auth = buildAuthFromArray(args); + if (auth != null) { + auths.add(auth); } } - } 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 { - br.close(); - } catch (IOException ignored) { - } - } + silentClose(br); } return auths; } @Override public List getLoggedPlayers() { - return new ArrayList<>(); + throw new UnsupportedOperationException("Flat file no longer supported"); } @Override public boolean isEmailStored(String email) { throw new UnsupportedOperationException("Flat file no longer supported"); } + + private static PlayerAuth buildAuthFromArray(String[] args) { + // Format allows 2, 3, 4, 7, 8, 9 fields. Anything else is unknown + if (args.length >= 2 && args.length <= 9 && args.length != 5 && args.length != 6) { + 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]); + return builder.build(); + } + return null; + } + + private static void silentClose(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException ignored) { + // silent close + } + } + } } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 1ab37d5e..db4ed538 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -3,7 +3,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; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.security.HashAlgorithm; @@ -42,6 +41,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); @@ -52,6 +55,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 { @@ -93,6 +99,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; } @@ -122,7 +131,8 @@ public class MySQL implements DataSource { ConsoleLogger.info("Connection arguments loaded, Hikari ConnectionPool ready!"); } - private synchronized void reloadArguments() throws RuntimeException { + @Override + public synchronized void reload() throws RuntimeException { if (ds != null) { ds.close(); } @@ -361,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); @@ -382,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(); @@ -405,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(); @@ -419,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); @@ -622,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; } @@ -684,17 +695,6 @@ public class MySQL implements DataSource { return false; } - @Override - public void reload() { - try { - reloadArguments(); - } catch (Exception ex) { - ConsoleLogger.logException("Can't reconnect to MySQL database... " + - "Please check your MySQL configuration! Encountered", ex); - AuthMe.getInstance().stopOrUnload(); - } - } - @Override public synchronized void close() { if (ds != null && !ds.isClosed()) { @@ -881,22 +881,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); } @@ -980,12 +982,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 d0c39e15..f0462840 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -30,8 +30,10 @@ 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); @@ -55,6 +57,10 @@ public class SQLite implements DataSource { this.con = connection; } + private static void logSqlException(SQLException e) { + ConsoleLogger.logException("Error while executing SQL statement:", e); + } + private synchronized void connect() throws ClassNotFoundException, SQLException { Class.forName("org.sqlite.JDBC"); ConsoleLogger.info("SQLite driver loaded"); @@ -123,6 +129,11 @@ public class SQLite implements DataSource { ConsoleLogger.info("SQLite Setup finished"); } + @Override + public void reload() { + // TODO 20160309: Implement reloading + } + @Override public synchronized boolean isAuthAvailable(String user) { PreparedStatement pst = null; @@ -277,24 +288,23 @@ public class SQLite implements DataSource { @Override public List autoPurgeDatabase(long until) { - PreparedStatement pst = null; - ResultSet rs = null; List list = new ArrayList<>(); - try { - pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.LAST_LOGIN + "(); + return list; } @Override @@ -350,17 +360,14 @@ 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); } } - @Override - public void reload() { - } - private void close(Statement st) { if (st != null) { try { @@ -421,17 +428,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,18 +511,13 @@ 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; } @@ -526,7 +525,7 @@ public class SQLite implements DataSource { @Override public boolean updateRealName(String user, String realName) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; - try(PreparedStatement pst = con.prepareStatement(sql)) { + try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, realName); pst.setString(2, user); pst.executeUpdate(); @@ -540,7 +539,7 @@ public class SQLite implements DataSource { @Override public boolean updateIp(String user, String ip) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; - try(PreparedStatement pst = con.prepareStatement(sql)) { + try (PreparedStatement pst = con.prepareStatement(sql)) { pst.setString(1, ip); pst.setString(2, user); pst.executeUpdate(); @@ -554,19 +553,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; } @@ -574,19 +568,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; } @@ -607,10 +596,6 @@ public class SQLite implements DataSource { return false; } - private static void logSqlException(SQLException e) { - ConsoleLogger.logException("Error while executing SQL statement:", e); - } - private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null; 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/hooks/BungeeCordMessage.java b/src/main/java/fr/xephi/authme/hooks/BungeeCordMessage.java index 0c4fd2de..4b6534da 100644 --- a/src/main/java/fr/xephi/authme/hooks/BungeeCordMessage.java +++ b/src/main/java/fr/xephi/authme/hooks/BungeeCordMessage.java @@ -7,6 +7,7 @@ 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.cache.IpAddressManager; import fr.xephi.authme.security.crypts.HashedPassword; import org.bukkit.entity.Player; import org.bukkit.plugin.messaging.PluginMessageListener; @@ -15,30 +16,33 @@ import org.bukkit.plugin.messaging.PluginMessageListener; */ public class BungeeCordMessage implements PluginMessageListener { - public final AuthMe plugin; + private final AuthMe plugin; + private final IpAddressManager ipAddressManager; /** * Constructor for BungeeCordMessage. * - * @param plugin AuthMe + * @param plugin The plugin instance + * @param ipAddressManager The IP address manager */ - public BungeeCordMessage(AuthMe plugin) { + public BungeeCordMessage(AuthMe plugin, IpAddressManager ipAddressManager) { this.plugin = plugin; + this.ipAddressManager = ipAddressManager; } @Override public void onPluginMessageReceived(String channel, Player player, byte[] message) { - if (!channel.equals("BungeeCord")) { + if (!"BungeeCord".equals(channel)) { return; } ByteArrayDataInput in = ByteStreams.newDataInput(message); String subChannel = in.readUTF(); - if (subChannel.equals("IP")) { // We need only the IP channel + if ("IP".equals(subChannel)) { // We need only the IP channel String ip = in.readUTF(); // Put the IP (only the ip not the port) in the hashMap - plugin.realIp.put(player.getName().toLowerCase(), ip); + ipAddressManager.addCache(player.getName(), ip); } - if (subChannel.equalsIgnoreCase("AuthMe")) { + if ("AuthMe".equalsIgnoreCase(subChannel)) { String str = in.readUTF(); final String[] args = str.split(";"); final String act = args[0]; @@ -65,10 +69,10 @@ public class BungeeCordMessage implements PluginMessageListener { ConsoleLogger.info("Player " + auth.getNickname() + " has registered out from one of your server!"); } else if ("changepassword".equals(act)) { - final String password = args[2]; + final String password = args[2]; final String salt = args.length >= 4 ? args[3] : null; - auth.setPassword(new HashedPassword(password, salt)); - PlayerCache.getInstance().updatePlayer(auth); + auth.setPassword(new HashedPassword(password, salt)); + PlayerCache.getInstance().updatePlayer(auth); dataSource.updatePassword(auth); } diff --git a/src/main/java/fr/xephi/authme/hooks/EssSpawn.java b/src/main/java/fr/xephi/authme/hooks/EssSpawn.java deleted file mode 100644 index 0644ee10..00000000 --- a/src/main/java/fr/xephi/authme/hooks/EssSpawn.java +++ /dev/null @@ -1,51 +0,0 @@ -package fr.xephi.authme.hooks; - -import fr.xephi.authme.settings.CustomConfiguration; -import org.bukkit.Bukkit; -import org.bukkit.Location; - -import java.io.File; - -/** - */ -public class EssSpawn extends CustomConfiguration { - - private static EssSpawn spawn; - - public EssSpawn() { - super(new File("." + File.separator + "plugins" + File.separator + "Essentials" + File.separator + "spawn.yml")); - spawn = this; - load(); - } - - /** - * Method getInstance. - * - * @return EssSpawn - */ - public static EssSpawn getInstance() { - if (spawn == null) { - spawn = new EssSpawn(); - } - return spawn; - } - - /** - * Method getLocation. - * - * @return Location - */ - public Location getLocation() { - try { - if (!this.contains("spawns.default.world")) - return null; - if (this.getString("spawns.default.world").isEmpty() || this.getString("spawns.default.world").equals("")) - return null; - Location location = new Location(Bukkit.getWorld(this.getString("spawns.default.world")), this.getDouble("spawns.default.x"), this.getDouble("spawns.default.y"), this.getDouble("spawns.default.z"), Float.parseFloat(this.getString("spawns.default.yaw")), Float.parseFloat(this.getString("spawns.default.pitch"))); - return location; - } catch (NullPointerException | NumberFormatException npe) { - return null; - } - } - -} diff --git a/src/main/java/fr/xephi/authme/hooks/PluginHooks.java b/src/main/java/fr/xephi/authme/hooks/PluginHooks.java new file mode 100644 index 00000000..3a54709a --- /dev/null +++ b/src/main/java/fr/xephi/authme/hooks/PluginHooks.java @@ -0,0 +1,158 @@ +package fr.xephi.authme.hooks; + +import com.earth2me.essentials.Essentials; +import com.onarandombox.MultiverseCore.MultiverseCore; +import com.onarandombox.MultiverseCore.api.MVWorldManager; +import fr.xephi.authme.ConsoleLogger; +import net.minelink.ctplus.CombatTagPlus; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +import java.io.File; + +/** + * Hooks into third-party plugins and allows to perform actions on them. + */ +public class PluginHooks { + + private final PluginManager pluginManager; + private Essentials essentials; + private MultiverseCore multiverse; + private CombatTagPlus combatTagPlus; + + /** + * Constructor. + * + * @param pluginManager The server's plugin manager + */ + public PluginHooks(PluginManager pluginManager) { + this.pluginManager = pluginManager; + tryHookToCombatPlus(); + tryHookToEssentials(); + tryHookToMultiverse(); + } + + /** + * Enable or disable the social spy status of the given user if Essentials is available. + * + * @param player The player to modify + * @param socialSpyStatus The social spy status (enabled/disabled) to set + */ + public void setEssentialsSocialSpyStatus(Player player, boolean socialSpyStatus) { + if (essentials != null) { + essentials.getUser(player).setSocialSpyEnabled(socialSpyStatus); + } + } + + /** + * If Essentials is hooked into, return Essentials' data folder. + * + * @return The Essentials data folder, or null if unavailable + */ + public File getEssentialsDataFolder() { + if (essentials != null) { + return essentials.getDataFolder(); + } + return null; + } + + /** + * Return the spawn of the given world as defined by Multiverse (if available). + * + * @param world The world to get the Multiverse spawn for + * @return The spawn location from Multiverse, or null if unavailable + */ + public Location getMultiverseSpawn(World world) { + if (multiverse != null) { + MVWorldManager manager = multiverse.getMVWorldManager(); + if (manager.isMVWorld(world)) { + return manager.getMVWorld(world).getSpawnLocation(); + } + } + return null; + } + + /** + * Query the CombatTagPlus plugin whether the given player is an NPC. + * + * @param player The player to verify + * @return True if the player is an NPC according to CombatTagPlus, false if not or if the plugin is unavailable + */ + public boolean isNpcInCombatTagPlus(Player player) { + return combatTagPlus != null && combatTagPlus.getNpcPlayerHelper().isNpc(player); + } + + + // ------ + // "Is plugin available" methods + // ------ + public boolean isEssentialsAvailable() { + return essentials != null; + } + + public boolean isMultiverseAvailable() { + return multiverse != null; + } + + public boolean isCombatTagPlusAvailable() { + return combatTagPlus != null; + } + + // ------ + // Hook methods + // ------ + public void tryHookToEssentials() { + try { + essentials = getPlugin(pluginManager, "Essentials", Essentials.class); + } catch (Exception | NoClassDefFoundError ignored) { + essentials = null; + } + } + + public void tryHookToCombatPlus() { + try { + combatTagPlus = getPlugin(pluginManager, "CombatTagPlus", CombatTagPlus.class); + } catch (Exception | NoClassDefFoundError ignored) { + combatTagPlus = null; + } + } + + public void tryHookToMultiverse() { + try { + multiverse = getPlugin(pluginManager, "Multiverse-Core", MultiverseCore.class); + } catch (Exception | NoClassDefFoundError ignored) { + multiverse = null; + } + } + + // ------ + // Unhook methods + // ------ + public void unhookEssentials() { + essentials = null; + } + public void unhookCombatPlus() { + combatTagPlus = null; + } + public void unhookMultiverse() { + multiverse = null; + } + + // ------ + // Helpers + // ------ + private static T getPlugin(PluginManager pluginManager, String name, Class clazz) + throws Exception, NoClassDefFoundError { + if (pluginManager.isPluginEnabled(name)) { + T plugin = clazz.cast(pluginManager.getPlugin(name)); + ConsoleLogger.info("Hooked successfully into " + name); + return plugin; + } + return null; + } + + +} diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java index c5c0b2a6..50b0e61c 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeEntityListener.java @@ -87,7 +87,7 @@ public class AuthMeEntityListener implements Listener { } } - // TODO: Need to check this, player can't throw snowball but the item is taken. + // TODO #568: Need to check this, player can't throw snowball but the item is taken. @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void onProjectileLaunch(ProjectileLaunchEvent event) { if (event.getEntity() == null) { @@ -103,7 +103,7 @@ public class AuthMeEntityListener implements Listener { } player = (Player) shooter; } else { - // TODO ljacqu 20151220: Invoking getShooter() with null but method isn't static + // TODO #568 20151220: Invoking getShooter() with null but method isn't static try { if (getShooter == null) { getShooter = Projectile.class.getMethod("getShooter"); diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeInventoryPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeInventoryPacketAdapter.java index 18f21f8b..62dcf7e5 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeInventoryPacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeInventoryPacketAdapter.java @@ -22,29 +22,35 @@ import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.reflect.MethodUtils; + import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.settings.Settings; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collections; import java.util.logging.Level; -/** - */ +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + public class AuthMeInventoryPacketAdapter extends PacketAdapter { private static final int PLAYER_INVENTORY = 0; - // http://wiki.vg/Inventory#Inventory (0-4 crafting, 5-8 armor, 9-35 main inventory, 36-44 hotbar) + // http://wiki.vg/Inventory#Inventory (0-4 crafting, 5-8 armor, 9-35 main inventory, 36-44 hotbar, 45 off hand) // +1 because an index starts with 0 private static final int CRAFTING_SIZE = 5; private static final int ARMOR_SIZE = 4; private static final int MAIN_SIZE = 27; private static final int HOTBAR_SIZE = 9; + private static final int OFF_HAND_POSITION = 45; + + private final boolean offHandSupported = MethodUtils + .getAccessibleMethod(PlayerInventory.class, "getItemInOffHand", new Class[]{}) != null; public AuthMeInventoryPacketAdapter(AuthMe plugin) { super(plugin, PacketType.Play.Server.SET_SLOT, PacketType.Play.Server.WINDOW_ITEMS); @@ -90,17 +96,23 @@ public class AuthMeInventoryPacketAdapter extends PacketAdapter { ItemStack[] storedInventory = Arrays.copyOfRange(mainInventory, HOTBAR_SIZE, mainInventory.length); // concat all parts of the inventory together - int inventorySize = playerCrafting.length + armorContents.length + mainInventory.length; + int inventorySize = CRAFTING_SIZE + ARMOR_SIZE + MAIN_SIZE + HOTBAR_SIZE; + if (offHandSupported) { + inventorySize++; + } + ItemStack[] completeInventory = new ItemStack[inventorySize]; - System.arraycopy(playerCrafting, 0, completeInventory, 0, playerCrafting.length); - System.arraycopy(armorContents, 0, completeInventory, playerCrafting.length, armorContents.length); + System.arraycopy(playerCrafting, 0, completeInventory, 0, CRAFTING_SIZE); + System.arraycopy(armorContents, 0, completeInventory, CRAFTING_SIZE, ARMOR_SIZE); // storedInventory and hotbar - System.arraycopy(storedInventory, 0, completeInventory - , playerCrafting.length + armorContents.length, storedInventory.length); - System.arraycopy(hotbar, 0, completeInventory - , playerCrafting.length + armorContents.length + storedInventory.length, hotbar.length); + System.arraycopy(storedInventory, 0, completeInventory, CRAFTING_SIZE + ARMOR_SIZE, MAIN_SIZE); + System.arraycopy(hotbar, 0, completeInventory, CRAFTING_SIZE + ARMOR_SIZE + MAIN_SIZE, HOTBAR_SIZE); + + if (offHandSupported) { + completeInventory[OFF_HAND_POSITION] = player.getInventory().getItemInOffHand(); + } inventoryPacket.getItemArrayModifier().write(0, completeInventory); try { diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index d563f33c..b5e0a9b3 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -9,10 +9,12 @@ 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.DataSource; import fr.xephi.authme.output.MessageKey; 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.settings.Settings; import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.Utils; @@ -58,10 +60,17 @@ public class AuthMePlayerListener implements Listener { public static final ConcurrentHashMap causeByAuthMe = new ConcurrentHashMap<>(); private final AuthMe plugin; private final Messages m; + private final DataSource dataSource; + private final AntiBot antiBot; + private final Management management; - public AuthMePlayerListener(AuthMe plugin) { - this.m = plugin.getMessages(); + public AuthMePlayerListener(AuthMe plugin, Messages messages, DataSource dataSource, AntiBot antiBot, + Management management) { this.plugin = plugin; + this.m = messages; + this.dataSource = dataSource; + this.antiBot = antiBot; + this.management = management; } private void handleChat(AsyncPlayerChatEvent event) { @@ -80,15 +89,14 @@ public class AuthMePlayerListener implements Listener { } event.setCancelled(true); - sendLoginRegisterMSG(player); + sendLoginOrRegisterMessage(player); } - // TODO: new name - private void sendLoginRegisterMSG(final Player player) { + private void sendLoginOrRegisterMessage(final Player player) { plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() { @Override public void run() { - if (plugin.getDataSource().isAuthAvailable(player.getName().toLowerCase())) { + if (dataSource.isAuthAvailable(player.getName().toLowerCase())) { m.send(player, MessageKey.LOGIN_MESSAGE); } else { if (Settings.emailRegistration) { @@ -117,7 +125,7 @@ public class AuthMePlayerListener implements Listener { return; } event.setCancelled(true); - sendLoginRegisterMSG(event.getPlayer()); + sendLoginOrRegisterMessage(event.getPlayer()); } @EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL) @@ -230,14 +238,14 @@ public class AuthMePlayerListener implements Listener { Bukkit.getScheduler().runTask(plugin, new Runnable() { @Override public void run() { - plugin.getManagement().performJoin(player); + management.performJoin(player); } }); } @EventHandler(priority = EventPriority.HIGHEST) public void onPreLogin(AsyncPlayerPreLoginEvent event) { - PlayerAuth auth = plugin.getDataSource().getAuth(event.getName()); + PlayerAuth auth = dataSource.getAuth(event.getName()); if (Settings.preventOtherCase && auth != null && auth.getRealName() != null) { String realName = auth.getRealName(); if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) { @@ -246,7 +254,7 @@ public class AuthMePlayerListener implements Listener { return; } if (realName.isEmpty() || realName.equals("Player")) { - plugin.getDataSource().updateRealName(event.getName().toLowerCase(), event.getName()); + dataSource.updateRealName(event.getName().toLowerCase(), event.getName()); } } @@ -320,7 +328,7 @@ public class AuthMePlayerListener implements Listener { } final String name = player.getName().toLowerCase(); - boolean isAuthAvailable = plugin.getDataSource().isAuthAvailable(name); + boolean isAuthAvailable = dataSource.isAuthAvailable(name); if (Settings.isKickNonRegisteredEnabled && !isAuthAvailable) { if (Settings.antiBotInAction) { @@ -346,7 +354,7 @@ public class AuthMePlayerListener implements Listener { return; } - AntiBot.checkAntiBot(player); + antiBot.checkAntiBot(player); if (Settings.bungee) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); @@ -367,7 +375,7 @@ public class AuthMePlayerListener implements Listener { event.setQuitMessage(null); } - plugin.getManagement().performQuit(player, false); + management.performQuit(player, false); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @@ -493,13 +501,13 @@ public class AuthMePlayerListener implements Listener { Player player = event.getPlayer(); String name = player.getName().toLowerCase(); Location spawn = plugin.getSpawnLocation(player); - if (Settings.isSaveQuitLocationEnabled && plugin.getDataSource().isAuthAvailable(name)) { + if (Settings.isSaveQuitLocationEnabled && dataSource.isAuthAvailable(name)) { PlayerAuth auth = PlayerAuth.builder() .name(name) .realName(player.getName()) .location(spawn) .build(); - plugin.getDataSource().updateQuitLoc(auth); + dataSource.updateQuitLoc(auth); } if (spawn != null && spawn.getWorld() != null) { event.setRespawnLocation(spawn); diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java index 54cce57a..d0f16357 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeServerListener.java @@ -2,9 +2,11 @@ package fr.xephi.authme.listener; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.util.GeoLiteAPI; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -12,18 +14,21 @@ import org.bukkit.event.Listener; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.server.ServerListPingEvent; -import org.bukkit.plugin.Plugin; /** */ public class AuthMeServerListener implements Listener { private final AuthMe plugin; - private final Messages m; + private final Messages messages; + private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; - public AuthMeServerListener(AuthMe plugin) { - this.m = plugin.getMessages(); + public AuthMeServerListener(AuthMe plugin, Messages messages, PluginHooks pluginHooks, SpawnLoader spawnLoader) { this.plugin = plugin; + this.messages = messages; + this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; } @EventHandler(priority = EventPriority.HIGHEST) @@ -31,11 +36,11 @@ public class AuthMeServerListener implements Listener { if (!Settings.countriesBlacklist.isEmpty() || !Settings.countries.isEmpty()){ String countryCode = GeoLiteAPI.getCountryCode(event.getAddress().getHostAddress()); if( Settings.countriesBlacklist.contains(countryCode)) { - event.setMotd(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR)); + event.setMotd(messages.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR)); return; } if (Settings.enableProtection && !Settings.countries.contains(countryCode)) { - event.setMotd(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR)); + event.setMotd(messages.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR)); } } } @@ -47,38 +52,21 @@ public class AuthMeServerListener implements Listener { return; } - // Get the plugin instance - Plugin pluginInstance = event.getPlugin(); - - // Make sure it's not this plugin itself - if (pluginInstance.equals(this.plugin)) { - return; + final String pluginName = event.getPlugin().getName(); + if ("Essentials".equalsIgnoreCase(pluginName)) { + pluginHooks.unhookEssentials(); + ConsoleLogger.info("Essentials has been disabled: unhooking"); + } else if ("Multiverse-Core".equalsIgnoreCase(pluginName)) { + pluginHooks.unhookMultiverse(); + ConsoleLogger.info("Multiverse-Core has been disabled: unhooking"); + } else if ("CombatTagPlus".equalsIgnoreCase(pluginName)) { + pluginHooks.unhookCombatPlus(); + ConsoleLogger.info("CombatTagPlus has been disabled: unhooking"); + } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { + spawnLoader.unloadEssentialsSpawn(); + ConsoleLogger.info("EssentialsSpawn has been disabled: unhooking"); } - // Call the onPluginDisable method in the permissions manager - this.plugin.getPermissionsManager().onPluginDisable(event); - - String pluginName = pluginInstance.getName(); - if (pluginName.equalsIgnoreCase("Essentials")) { - plugin.ess = null; - ConsoleLogger.info("Essentials has been disabled, unhook!"); - return; - } - if (pluginName.equalsIgnoreCase("EssentialsSpawn")) { - plugin.essentialsSpawn = null; - ConsoleLogger.info("EssentialsSpawn has been disabled, unhook!"); - return; - } - if (pluginName.equalsIgnoreCase("Multiverse-Core")) { - plugin.multiverse = null; - ConsoleLogger.info("Multiverse-Core has been disabled, unhook!"); - return; - } - if (pluginName.equalsIgnoreCase("CombatTagPlus")) { - plugin.combatTagPlus = null; - ConsoleLogger.info("CombatTagPlus has been disabled, unhook!"); - return; - } if (pluginName.equalsIgnoreCase("ProtocolLib")) { plugin.inventoryProtector = null; plugin.tablistHider = null; @@ -89,22 +77,22 @@ public class AuthMeServerListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onPluginEnable(PluginEnableEvent event) { - // Call the onPluginEnable method in the permissions manager - this.plugin.getPermissionsManager().onPluginEnable(event); + // Make sure the plugin instance isn't null + if (event.getPlugin() == null) { + return; + } - String pluginName = event.getPlugin().getName(); - if (pluginName.equalsIgnoreCase("Essentials") || pluginName.equalsIgnoreCase("EssentialsSpawn")) { - plugin.checkEssentials(); - return; - } - if (pluginName.equalsIgnoreCase("Multiverse-Core")) { - plugin.checkMultiverse(); - return; - } - if (pluginName.equalsIgnoreCase("CombatTagPlus")) { - plugin.checkCombatTagPlus(); - return; + final String pluginName = event.getPlugin().getName(); + if ("Essentials".equalsIgnoreCase(pluginName)) { + pluginHooks.tryHookToEssentials(); + } else if ("Multiverse-Core".equalsIgnoreCase(pluginName)) { + pluginHooks.tryHookToMultiverse(); + } else if ("CombatTagPlus".equalsIgnoreCase(pluginName)) { + pluginHooks.tryHookToCombatPlus(); + } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { + spawnLoader.loadEssentialsSpawn(); } + if (pluginName.equalsIgnoreCase("ProtocolLib")) { plugin.checkProtocolLib(); } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java index d85a5c5e..bd32d1b8 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMeTablistPacketAdapter.java @@ -29,11 +29,13 @@ public class AuthMeTablistPacketAdapter extends PacketAdapter { } } + // TODO: fix this in 1.9 public void register() { - ProtocolLibrary.getProtocolManager().addPacketListener(this); + ConsoleLogger.showError("The hideTablistBeforeLogin feature is temporarily disabled due to issues with 1.9 clients."); + //ProtocolLibrary.getProtocolManager().addPacketListener(this); } public void unregister() { - ProtocolLibrary.getProtocolManager().removePacketListener(this); + //ProtocolLibrary.getProtocolManager().removePacketListener(this); } } diff --git a/src/main/java/fr/xephi/authme/output/Log4JFilter.java b/src/main/java/fr/xephi/authme/output/Log4JFilter.java index 9b2bea4c..336af779 100644 --- a/src/main/java/fr/xephi/authme/output/Log4JFilter.java +++ b/src/main/java/fr/xephi/authme/output/Log4JFilter.java @@ -59,13 +59,16 @@ public class Log4JFilter implements Filter { @Override public Result filter(Logger arg0, Level arg1, Marker arg2, String message, Object... arg4) { + if (message == null) { + return Result.NEUTRAL; + } return validateMessage(message); } @Override public Result filter(Logger arg0, Level arg1, Marker arg2, Object message, Throwable arg4) { - if (message == null) { - return Result.NEUTRAL; + if (message == null) { + return Result.NEUTRAL; } return validateMessage(message.toString()); } 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/permission/PermissionsManager.java b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java index e42ea46f..5805b8d6 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsManager.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManager.java @@ -24,7 +24,6 @@ import de.bananaco.bpermissions.api.CalculableType; import fr.xephi.authme.command.CommandDescription; import fr.xephi.authme.util.CollectionUtils; import net.milkbowl.vault.permission.Permission; -import ru.tehkode.permissions.PermissionManager; import ru.tehkode.permissions.PermissionUser; import ru.tehkode.permissions.bukkit.PermissionsEx; @@ -38,7 +37,7 @@ import ru.tehkode.permissions.bukkit.PermissionsEx; * Written by Tim Visée. *

* @author Tim Visée, http://timvisee.com - * @version 0.2.1 + * @version 0.3 */ public class PermissionsManager implements PermissionsService { @@ -59,9 +58,14 @@ public class PermissionsManager implements PermissionsService { */ private Logger log; /** - * Type of permissions system that is currently used. + * The permissions manager Bukkit listener instance. */ - private PermissionsSystemType permsType = PermissionsSystemType.NONE; + private PermissionsManagerBukkitListener bukkitListener; + /** + * Type of permissions system that is currently used. + * Null if no permissions system is hooked and/or used. + */ + private PermissionsSystemType permsType = null; /** * Essentials group manager instance. */ @@ -82,6 +86,21 @@ public class PermissionsManager implements PermissionsService { this.server = server; this.plugin = plugin; this.log = log; + + // Create and register the Bukkit listener on the server if it's valid + if(this.server != null) { + // Create the Bukkit listener + this.bukkitListener = new PermissionsManagerBukkitListener(this); + + // Get the plugin manager instance + PluginManager pluginManager = this.server.getPluginManager(); + + // Register the Bukkit listener + pluginManager.registerEvents(this.bukkitListener, this.plugin); + + // Show a status message. + //this.log.info("Started permission plugins state listener!"); + } } /** @@ -90,7 +109,7 @@ public class PermissionsManager implements PermissionsService { * @return False if there isn't any permissions system used. */ public boolean isEnabled() { - return !permsType.equals(PermissionsSystemType.NONE); + return permsType != null; } /** @@ -108,110 +127,96 @@ public class PermissionsManager implements PermissionsService { * @return The detected permissions system. */ public PermissionsSystemType setup() { + // Force-unhook from current hooked permissions systems + unhook(); + // Define the plugin manager - final PluginManager pm = this.server.getPluginManager(); + final PluginManager pluginManager = this.server.getPluginManager(); - // Reset used permissions system type - permsType = PermissionsSystemType.NONE; + // Reset used permissions system type flag + permsType = null; - // PermissionsEx, check if it's available - try { - Plugin pex = pm.getPlugin("PermissionsEx"); - if (pex != null) { - PermissionManager pexPerms = PermissionsEx.getPermissionManager(); - if (pexPerms != null) { - permsType = PermissionsSystemType.PERMISSIONS_EX; + // Loop through all the available permissions system types + for(PermissionsSystemType type : PermissionsSystemType.values()) { + // Try to find and hook the current plugin if available, print an error if failed + try { + // Try to find the plugin for the current permissions system + Plugin plugin = pluginManager.getPlugin(type.getPluginName()); - System.out.println("[" + plugin.getName() + "] Hooked into PermissionsEx!"); - return permsType; + // Make sure a plugin with this name was found + if(plugin == null) + continue; + + // Make sure the plugin is enabled before hooking + if(!plugin.isEnabled()) { + this.log.info("Not hooking into " + type.getName() + " because it's disabled!"); + continue; } - } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into PermissionsEx!"); - } - // PermissionsBukkit, check if it's available - try { - Plugin bukkitPerms = pm.getPlugin("PermissionsBukkit"); - if (bukkitPerms != null) { - permsType = PermissionsSystemType.PERMISSIONS_BUKKIT; - System.out.println("[" + plugin.getName() + "] Hooked into PermissionsBukkit!"); - return permsType; - } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into PermissionsBukkit!"); - } + // Use the proper method to hook this plugin + switch(type) { + case PERMISSIONS_EX: + // Get the permissions manager for PermissionsEx and make sure it isn't null + if(PermissionsEx.getPermissionManager() == null) { + this.log.info("Failed to hook into " + type.getName() + "!"); + continue; + } - // bPermissions, check if it's available - try { - Plugin bPerms = pm.getPlugin("bPermissions"); - if (bPerms != null) { - permsType = PermissionsSystemType.B_PERMISSIONS; - System.out.println("[" + plugin.getName() + "] Hooked into bPermissions!"); - return permsType; - } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into bPermissions!"); - } + break; - // Essentials Group Manager, check if it's available - try { - final Plugin groupManagerPlugin = pm.getPlugin("GroupManager"); - if (groupManagerPlugin != null && groupManagerPlugin.isEnabled()) { - permsType = PermissionsSystemType.ESSENTIALS_GROUP_MANAGER; - groupManagerPerms = (GroupManager) groupManagerPlugin; - System.out.println("[" + plugin.getName() + "] Hooked into Essentials Group Manager!"); - return permsType; - } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into Essentials Group Manager!"); - } + case ESSENTIALS_GROUP_MANAGER: + // Set the plugin instance + groupManagerPerms = (GroupManager) plugin; + break; - // zPermissions, check if it's available - try { - Plugin zPerms = pm.getPlugin("zPermissions"); - if (zPerms != null) { - zPermissionsService = Bukkit.getServicesManager().load(ZPermissionsService.class); - if (zPermissionsService != null) { - permsType = PermissionsSystemType.Z_PERMISSIONS; - System.out.println("[" + plugin.getName() + "] Hooked into zPermissions!"); - return permsType; + case Z_PERMISSIONS: + // Set the zPermissions service and make sure it's valid + zPermissionsService = Bukkit.getServicesManager().load(ZPermissionsService.class); + if(zPermissionsService == null) { + this.log.info("Failed to hook into " + type.getName() + "!"); + continue; + } + + break; + + case VAULT: + // Get the permissions provider service + RegisteredServiceProvider permissionProvider = this.server.getServicesManager().getRegistration(Permission.class); + if (permissionProvider == null) { + this.log.info("Failed to hook into " + type.getName() + "!"); + continue; + } + + // Get the Vault provider and make sure it's valid + vaultPerms = permissionProvider.getProvider(); + if(vaultPerms == null) { + this.log.info("Not using " + type.getName() + " because it's disabled!"); + continue; + } + + break; + + default: } + + // Set the hooked permissions system type + this.permsType = type; + + // Show a success message + this.log.info("Hooked into " + type.getName() + "!"); + + // Return the used permissions system type + return type; + + } catch (Exception ex) { + // An error occurred, show a warning message + this.log.info("Error while hooking into " + type.getName() + "!"); } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into zPermissions!"); } - // Vault, check if it's available - try { - final Plugin vaultPlugin = pm.getPlugin("Vault"); - if (vaultPlugin != null && vaultPlugin.isEnabled()) { - RegisteredServiceProvider permissionProvider = this.server.getServicesManager().getRegistration(Permission.class); - if (permissionProvider != null) { - vaultPerms = permissionProvider.getProvider(); - if (vaultPerms.isEnabled()) { - permsType = PermissionsSystemType.VAULT; - System.out.println("[" + plugin.getName() + "] Hooked into Vault Permissions!"); - return permsType; - } else { - System.out.println("[" + plugin.getName() + "] Not using Vault Permissions, Vault Permissions is disabled!"); - } - } - } - } catch (Exception ex) { - // An error occurred, show a warning message - System.out.println("[" + plugin.getName() + "] Error while hooking into Vault Permissions!"); - } - - // No recognized permissions system found - permsType = PermissionsSystemType.NONE; - System.out.println("[" + plugin.getName() + "] No supported permissions system found! Permissions disabled!"); - return PermissionsSystemType.NONE; + // No recognized permissions system found, show a message and return + this.log.info("No supported permissions system found! Permissions are disabled!"); + return null; } /** @@ -219,7 +224,7 @@ public class PermissionsManager implements PermissionsService { */ public void unhook() { // Reset the current used permissions system - this.permsType = PermissionsSystemType.NONE; + this.permsType = null; // Print a status message to the console this.log.info("Unhooked from Permissions!"); @@ -277,7 +282,14 @@ public class PermissionsManager implements PermissionsService { } } - + /** + * Get the permissions manager Bukkit listener instance. + * + * @return Listener instance. + */ + public PermissionsManagerBukkitListener getListener() { + return this.bukkitListener; + } /** * Check if the command sender has permission for the given permissions node. If no permissions system is used or @@ -383,13 +395,9 @@ public class PermissionsManager implements PermissionsService { // Vault return vaultPerms.has(player, permsNode); - case NONE: + default: // Not hooked into any permissions system, return default return def; - - default: - // Something went wrong, return false to prevent problems - return false; } } @@ -416,12 +424,8 @@ public class PermissionsManager implements PermissionsService { // Vault return vaultPerms.hasGroupSupport(); - case NONE: - // Not hooked into any permissions system, return false - return false; - default: - // Something went wrong, return false to prevent problems + // Not hooked into any permissions system, return false return false; } } @@ -469,12 +473,8 @@ public class PermissionsManager implements PermissionsService { // Vault return Arrays.asList(vaultPerms.getPlayerGroups(player)); - case NONE: - // Not hooked into any permissions system, return an empty list - return new ArrayList<>(); - default: - // Something went wrong, return an empty list to prevent problems + // Not hooked into any permissions system, return an empty list return new ArrayList<>(); } } @@ -521,12 +521,8 @@ public class PermissionsManager implements PermissionsService { // Vault return vaultPerms.getPrimaryGroup(player); - case NONE: - // Not hooked into any permissions system, return null - return null; - default: - // Something went wrong, return null to prevent problems + // Not hooked into any permissions system, return null return null; } } @@ -575,12 +571,8 @@ public class PermissionsManager implements PermissionsService { // Vault return vaultPerms.playerInGroup(player, groupName); - case NONE: - // Not hooked into any permissions system, return an empty list - return false; - default: - // Something went wrong, return an empty list to prevent problems + // Not hooked into any permissions system, return an empty list return false; } } @@ -632,12 +624,8 @@ public class PermissionsManager implements PermissionsService { vaultPerms.playerAddGroup(player, groupName); return true; - case NONE: - // Not hooked into any permissions system, return false - return false; - default: - // Something went wrong, return false + // Not hooked into any permissions system, return false return false; } } @@ -713,12 +701,8 @@ public class PermissionsManager implements PermissionsService { vaultPerms.playerRemoveGroup(player, groupName); return true; - case NONE: - // Not hooked into any permissions system, return false - return false; - default: - // Something went wrong, return false + // Not hooked into any permissions system, return false return false; } } @@ -802,12 +786,8 @@ public class PermissionsManager implements PermissionsService { vaultPerms.playerAddGroup(player, groupName); return true; - case NONE: - // Not hooked into any permissions system, return false - return false; - default: - // Something went wrong, return false + // Not hooked into any permissions system, return false return false; } } @@ -867,6 +847,4 @@ public class PermissionsManager implements PermissionsService { // Remove each group return removeGroups(player, groupNames); } - - } diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsManagerBukkitListener.java b/src/main/java/fr/xephi/authme/permission/PermissionsManagerBukkitListener.java new file mode 100644 index 00000000..d87f1b3c --- /dev/null +++ b/src/main/java/fr/xephi/authme/permission/PermissionsManagerBukkitListener.java @@ -0,0 +1,85 @@ +package fr.xephi.authme.permission; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; + +public class PermissionsManagerBukkitListener implements Listener { + + /** + * The permissions manager instance. + */ + private PermissionsManager permissionsManager; + + /** + * Whether the listener is enabled or not. + */ + private boolean enabled = true; + + /** + * Constructor.\ + * + * @param permissionsManager Permissions manager instance. + */ + public PermissionsManagerBukkitListener(PermissionsManager permissionsManager) { + this.permissionsManager = permissionsManager; + } + + /** + * Check whether the listener is enabled. + * + * @return True if the listener is enabled. + */ + public boolean isEnabled() { + return this.enabled; + } + + /** + * Set whether the listener is enabled. + * Disabling the listener will stop the event handling until it's enabled again. + * + * @param enabled True if enabled, false if disabled. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Called when a plugin is enabled. + * + * @param event Event reference. + */ + @EventHandler + public void onPluginEnable(PluginEnableEvent event) { + // Make sure the listener is enabled + if(!isEnabled()) + return; + + // Make sure the permissions manager is set + if(this.permissionsManager == null) + return; + + // Call the onPluginEnable method in the permissions manager + permissionsManager.onPluginEnable(event); + } + + /** + * Called when a plugin is disabled. + * + * @param event Event reference. + */ + @EventHandler + public void onPluginDisable(PluginDisableEvent event) { + // Make sure the listener is enabled + if(!isEnabled()) + return; + + // Make sure the permissions manager is set + if(this.permissionsManager == null) + return; + + // Call the onPluginDisable method in the permissions manager + permissionsManager.onPluginDisable(event); + } +} diff --git a/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java b/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java index 7246f389..2167fde6 100644 --- a/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java +++ b/src/main/java/fr/xephi/authme/permission/PermissionsSystemType.java @@ -5,32 +5,82 @@ package fr.xephi.authme.permission; */ public enum PermissionsSystemType { - NONE("None"), + /** + * Permissions Ex. + */ + PERMISSIONS_EX("PermissionsEx", "PermissionsEx"), - PERMISSIONS_EX("PermissionsEx"), + /** + * Permissions Bukkit. + */ + PERMISSIONS_BUKKIT("Permissions Bukkit", "PermissionsBukkit"), - PERMISSIONS_BUKKIT("Permissions Bukkit"), + /** + * bPermissions. + */ + B_PERMISSIONS("bPermissions", "bPermissions"), - B_PERMISSIONS("bPermissions"), + /** + * Essentials Group Manager. + */ + ESSENTIALS_GROUP_MANAGER("Essentials Group Manager", "GroupManager"), - ESSENTIALS_GROUP_MANAGER("Essentials Group Manager"), + /** + * zPermissions. + */ + Z_PERMISSIONS("zPermissions", "zPermissions"), - Z_PERMISSIONS("zPermissions"), + /** + * Vault. + */ + VAULT("Vault", "Vault"); - VAULT("Vault"); + /** + * The display name of the permissions system. + */ + public String name; - public final String name; + /** + * The name of the permissions system plugin. + */ + public String pluginName; /** * Constructor for PermissionsSystemType. * - * @param name The name the permissions manager goes by + * @param name Display name of the permissions system. + * @param pluginName Name of the plugin. */ - PermissionsSystemType(String name) { + PermissionsSystemType(String name, String pluginName) { this.name = name; + this.pluginName = pluginName; } + /** + * Get the display name of the permissions system. + * + * @return Display name. + */ public String getName() { return this.name; } + + /** + * Return the plugin name. + * + * @return Plugin name. + */ + public String getPluginName() { + return this.pluginName; + } + + /** + * Cast the permissions system type to a string. + * + * @return The display name of the permissions system. + */ + @Override + public String toString() { + return getName(); + } } diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index 7d00aff7..210cbce9 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -55,10 +55,10 @@ public enum PlayerPermission implements PermissionNode { */ CAN_LOGIN_BE_FORCED("authme.player.canbeforced"), - /** - * Permission to use to see own other accounts. - */ - SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"); + /** + * Permission to use to see own other accounts. + */ + SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"); /** * The permission node. diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 597d927e..4cea16fc 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; @@ -10,7 +11,6 @@ import fr.xephi.authme.process.logout.AsynchronousLogout; import fr.xephi.authme.process.quit.AsynchronousQuit; import fr.xephi.authme.process.register.AsyncRegister; import fr.xephi.authme.process.unregister.AsynchronousUnregister; -import fr.xephi.authme.settings.NewSetting; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; @@ -20,99 +20,51 @@ public class Management { private final AuthMe plugin; private final BukkitScheduler sched; - private final NewSetting settings; + private final ProcessService processService; + private final DataSource dataSource; + private final PlayerCache playerCache; - /** - * 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; } public void performLogin(final Player player, final String password, final boolean forceLogin) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsynchronousLogin(player, password, forceLogin, plugin, plugin.getDataSource(), settings) - .process(); - } - }); + runTask(new AsynchronousLogin(player, password, forceLogin, plugin, dataSource, processService)); } public void performLogout(final Player player) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsynchronousLogout(player, plugin, plugin.getDataSource()).process(); - } - }); + runTask(new AsynchronousLogout(player, plugin, dataSource, processService)); } public void performRegister(final Player player, final String password, final String email) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsyncRegister(player, password, email, plugin, plugin.getDataSource(), settings).process(); - } - }); + runTask(new AsyncRegister(player, password, email, plugin, dataSource, processService)); } public void performUnregister(final Player player, final String password, final boolean force) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsynchronousUnregister(player, password, force, plugin).process(); - } - }); + runTask(new AsynchronousUnregister(player, password, force, plugin, processService)); } 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) { - sched.runTaskAsynchronously(plugin, new Runnable() { - - @Override - public void run() { - new AsynchronousQuit(player, plugin, plugin.getDataSource(), isKick).process(); - } - - }); + runTask(new AsynchronousQuit(player, plugin, dataSource, isKick, processService)); } 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(); - } - }); + runTask(new AsyncChangeEmail(player, oldEmail, newEmail, dataSource, playerCache, processService)); + } + + 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..7f696fe1 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/ProcessService.java @@ -0,0 +1,202 @@ +package fr.xephi.authme.process; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.cache.IpAddressManager; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; +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; + private final DataSource dataSource; + private final IpAddressManager ipAddressManager; + private final PasswordSecurity passwordSecurity; + private final PluginHooks pluginHooks; + private final SpawnLoader spawnLoader; + + public ProcessService(NewSetting settings, Messages messages, AuthMe authMe, DataSource dataSource, + IpAddressManager ipAddressManager, PasswordSecurity passwordSecurity, PluginHooks pluginHooks, + SpawnLoader spawnLoader) { + this.settings = settings; + this.messages = messages; + this.authMe = authMe; + this.dataSource = dataSource; + this.ipAddressManager = ipAddressManager; + this.passwordSecurity = passwordSecurity; + this.pluginHooks = pluginHooks; + this.spawnLoader = spawnLoader; + } + + /** + * Retrieve a property's value. + * + * @param property the property to retrieve + * @param the property type + * @return the property's value + */ + public T getProperty(Property property) { + return settings.getProperty(property); + } + + /** + * Return the settings manager. + * + * @return settings manager + */ + public NewSetting getSettings() { + return settings; + } + + /** + * Send a message to the command sender. + * + * @param sender the command sender + * @param key the message key + */ + public void send(CommandSender sender, MessageKey key) { + messages.send(sender, key); + } + + /** + * Send a message to the command sender with the given replacements. + * + * @param sender the command sender + * @param key the message key + * @param replacements the replacements to apply to the message + */ + public void send(CommandSender sender, MessageKey key, String... replacements) { + messages.send(sender, key, replacements); + } + + /** + * Retrieve a message. + * + * @param key the key of the message + * @return the message, split by line + */ + public String[] retrieveMessage(MessageKey key) { + return messages.retrieve(key); + } + + /** + * Retrieve a message as one piece. + * + * @param key the key of the message + * @return the message + */ + public String retrieveSingleMessage(MessageKey key) { + return messages.retrieveSingle(key); + } + + /** + * Run a task. + * + * @param task the task to run + * @return the assigned task id + */ + public BukkitTask runTask(Runnable task) { + return authMe.getServer().getScheduler().runTask(authMe, task); + } + + /** + * Run a task at a later time. + * + * @param task the task to run + * @param delay the delay before running the task + * @return the assigned task id + */ + public BukkitTask runTaskLater(Runnable task, long delay) { + return authMe.getServer().getScheduler().runTaskLater(authMe, task, delay); + } + + /** + * Schedule a synchronous delayed task. + * + * @param task the task to schedule + * @return the task id + */ + public int scheduleSyncDelayedTask(Runnable task) { + return authMe.getServer().getScheduler().scheduleSyncDelayedTask(authMe, task); + } + + /** + * Emit an event. + * + * @param event the event to emit + */ + public void callEvent(Event event) { + authMe.getServer().getPluginManager().callEvent(event); + } + + /** + * Return the plugin instance. + * + * @return AuthMe instance + */ + public AuthMe getAuthMe() { + return authMe; + } + + /** + * Return the IP address manager. + * + * @return the ip address manager + */ + public IpAddressManager getIpAddressManager() { + return ipAddressManager; + } + + /** + * Compute the hash for the given password. + * + * @param password the password to hash + * @param username the user to hash for + * @return the resulting hash + */ + public HashedPassword computeHash(String password, String username) { + return passwordSecurity.computeHash(password, username); + } + + /** + * Return the PluginHooks manager. + * + * @return PluginHooks instance + */ + public PluginHooks getPluginHooks() { + return pluginHooks; + } + + /** + * Return the spawn manager. + * + * @return SpawnLoader instance + */ + public SpawnLoader getSpawnLoader() { + return spawnLoader; + } + + /** + * Return the plugin's datasource. + * + * @return the datasource + */ + public DataSource getDataSource() { + return dataSource; + } + +} 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/email/AsyncChangeEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java index f9bbbd07..132decd5 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java @@ -1,54 +1,52 @@ package fr.xephi.authme.process.email; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.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 for changing the email. */ -public class AsyncChangeEmail { +public class AsyncChangeEmail implements Process { private final Player player; private final String oldEmail; private final String newEmail; - private final Messages m; - private final NewSetting settings; + private final ProcessService service; private final PlayerCache playerCache; private final DataSource dataSource; - public AsyncChangeEmail(Player player, AuthMe plugin, String oldEmail, String newEmail, DataSource dataSource, - PlayerCache playerCache, NewSetting settings) { - this.m = plugin.getMessages(); + public AsyncChangeEmail(Player player, String oldEmail, String newEmail, DataSource dataSource, + PlayerCache playerCache, ProcessService service) { this.player = player; this.oldEmail = oldEmail; this.newEmail = newEmail; this.playerCache = playerCache; this.dataSource = dataSource; - this.settings = settings; + this.service = service; } - public void process() { + @Override + public void run() { String playerName = player.getName().toLowerCase(); if (playerCache.isAuthenticated(playerName)) { PlayerAuth auth = playerCache.getAuth(playerName); final String currentEmail = auth.getEmail(); if (currentEmail == null) { - m.send(player, MessageKey.USAGE_ADD_EMAIL); - } else if (newEmail == null || !Utils.isEmailCorrect(newEmail, settings)) { - m.send(player, MessageKey.INVALID_NEW_EMAIL); + service.send(player, MessageKey.USAGE_ADD_EMAIL); + } else if (newEmail == null || !Utils.isEmailCorrect(newEmail, service.getSettings())) { + service.send(player, MessageKey.INVALID_NEW_EMAIL); } else if (!oldEmail.equals(currentEmail)) { - m.send(player, MessageKey.INVALID_OLD_EMAIL); + service.send(player, MessageKey.INVALID_OLD_EMAIL); } else if (dataSource.isEmailStored(newEmail)) { - m.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); + service.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } else { saveNewEmail(auth); } @@ -61,20 +59,20 @@ public class AsyncChangeEmail { auth.setEmail(newEmail); if (dataSource.updateEmail(auth)) { playerCache.updatePlayer(auth); - m.send(player, MessageKey.EMAIL_CHANGED_SUCCESS); + service.send(player, MessageKey.EMAIL_CHANGED_SUCCESS); } else { - m.send(player, MessageKey.ERROR); + service.send(player, MessageKey.ERROR); auth.setEmail(newEmail); } } private void outputUnloggedMessage() { if (dataSource.isAuthAvailable(player.getName())) { - m.send(player, MessageKey.LOGIN_MESSAGE); - } else if (Settings.emailRegistration) { - m.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 { - m.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 e1b6096b..838ac0e6 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,64 @@ 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) { - plugin.getVerygamesIp(player); + if (service.getProperty(HooksSettings.DISABLE_SOCIAL_SPY)) { + service.getPluginHooks().setEssentialsSocialSpyStatus(player, false); } - if (plugin.ess != null && Settings.disableSocialSpy) { - 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() { - + final String ip = service.getIpAddressManager().getPlayerIp(player); + 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.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR)); if (Settings.banUnsafeIp) { plugin.getServer().banIP(ip); } @@ -78,38 +76,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.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 = service.getSpawnLoader().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 +117,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 +131,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 +148,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 +167,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,28 +177,23 @@ 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.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); - LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); + int msgInterval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); + if (registrationTimeout > 0) { + BukkitTask id = service.runTaskLater(new TimeoutTask(plugin, name, player), registrationTimeout); + LimboCache.getInstance().getLimboPlayer(name).setTimeoutTask(id); } MessageKey msg; @@ -219,29 +205,30 @@ public class AsynchronousJoin { : MessageKey.REGISTER_MESSAGE; } if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) { - BukkitTask msgTask = sched.runTask(plugin, new MessageTask(plugin, name, msg, msgInterval)); - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask); + BukkitTask msgTask = service.runTask(new MessageTask(plugin, name, msg, msgInterval)); + LimboCache.getInstance().getLimboPlayer(name).setMessageTask(msgTask); } } private boolean needFirstSpawn() { - if (player.hasPlayedBefore()) + if (player.hasPlayedBefore()) { return false; - Location firstSpawn = Spawn.getInstance().getFirstSpawn(); - if (firstSpawn == null || firstSpawn.getWorld() == null) + } + Location firstSpawn = service.getSpawnLoader().getFirstSpawn(); + if (firstSpawn == null) { return false; + } + FirstSpawnTeleportEvent tpEvent = new FirstSpawnTeleportEvent(player, player.getLocation(), firstSpawn); plugin.getServer().getPluginManager().callEvent(tpEvent); 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 +236,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 +266,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 +291,14 @@ public class AsynchronousJoin { return nameFound; } + private boolean hasJoinedIp(String name, String ip, NewSetting settings) { + int count = 0; + for (Player player : Utils.getOnlinePlayers()) { + if (ip.equalsIgnoreCase(service.getIpAddressManager().getPlayerIp(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 fcc64ab0..bb039bf1 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -5,17 +5,21 @@ import fr.xephi.authme.ConsoleLogger; 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.DataSource; import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.security.RandomString; -import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; @@ -27,7 +31,7 @@ import java.util.List; /** */ -public class AsynchronousLogin { +public class AsynchronousLogin implements Process { private final Player player; private final String name; @@ -36,23 +40,11 @@ public class AsynchronousLogin { private final boolean forceLogin; private final AuthMe plugin; private final DataSource database; - private final Messages m; private final String ip; - private final NewSetting settings; + private final ProcessService service; - /** - * Constructor for AsynchronousLogin. - * - * @param player Player - * @param password String - * @param forceLogin boolean - * @param plugin AuthMe - * @param data DataSource - * @param settings The settings - */ public AsynchronousLogin(Player player, String password, boolean forceLogin, AuthMe plugin, DataSource data, - NewSetting settings) { - this.m = plugin.getMessages(); + ProcessService service) { this.player = player; this.name = player.getName().toLowerCase(); this.password = password; @@ -60,12 +52,12 @@ public class AsynchronousLogin { this.forceLogin = forceLogin; this.plugin = plugin; this.database = data; - this.ip = plugin.getIP(player); - this.settings = settings; + this.ip = service.getIpAddressManager().getPlayerIp(player); + this.service = service; } private boolean needsCaptcha() { - if (Settings.useCaptcha) { + if (service.getProperty(SecuritySettings.USE_CAPTCHA)) { if (!plugin.captcha.containsKey(name)) { plugin.captcha.putIfAbsent(name, 1); } else { @@ -75,7 +67,7 @@ public class AsynchronousLogin { } if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) > Settings.maxLoginTry) { plugin.cap.putIfAbsent(name, RandomString.generate(Settings.captchaLength)); - m.send(player, MessageKey.USAGE_CAPTCHA, plugin.cap.get(name)); + service.send(player, MessageKey.USAGE_CAPTCHA, plugin.cap.get(name)); return true; } } @@ -90,30 +82,29 @@ public class AsynchronousLogin { */ private PlayerAuth preAuth() { if (PlayerCache.getInstance().isAuthenticated(name)) { - m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return null; } PlayerAuth pAuth = database.getAuth(name); if (pAuth == null) { - m.send(player, MessageKey.USER_NOT_REGISTERED); - if (LimboCache.getInstance().hasLimboPlayer(name)) { - LimboCache.getInstance().getLimboPlayer(name).getMessageTaskId().cancel(); - String[] msg; - if (Settings.emailRegistration) { - msg = m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE); - } else { - msg = m.retrieve(MessageKey.REGISTER_MESSAGE); - } - BukkitTask msgT = Bukkit.getScheduler().runTask(plugin, - new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); + service.send(player, MessageKey.USER_NOT_REGISTERED); + + LimboPlayer limboPlayer = LimboCache.getInstance().getLimboPlayer(name); + if (limboPlayer != null) { + limboPlayer.getMessageTask().cancel(); + String[] msg = service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION) + ? service.retrieveMessage(MessageKey.REGISTER_EMAIL_MESSAGE) + : service.retrieveMessage(MessageKey.REGISTER_MESSAGE); + BukkitTask messageTask = service.runTask( + new MessageTask(plugin, name, msg, service.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); + limboPlayer.setMessageTask(messageTask); } return null; } - if (!Settings.getMySQLColumnGroup.isEmpty() && pAuth.getGroupId() == Settings.getNonActivatedGroup) { - m.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); + if (!service.getProperty(DatabaseSettings.MYSQL_COL_GROUP).isEmpty() && pAuth.getGroupId() == Settings.getNonActivatedGroup) { + service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); return null; } @@ -121,7 +112,7 @@ public class AsynchronousLogin { && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) && !ip.equalsIgnoreCase("127.0.0.1") && !ip.equalsIgnoreCase("localhost")) { if (plugin.isLoggedIp(name, ip)) { - m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return null; } } @@ -134,13 +125,14 @@ public class AsynchronousLogin { return pAuth; } - public void process() { + @Override + public void run() { PlayerAuth pAuth = preAuth(); if (pAuth == null || needsCaptcha()) { return; } - if (pAuth.getIp().equals("127.0.0.1") && !pAuth.getIp().equals(ip)) { + if ("127.0.0.1".equals(pAuth.getIp()) && !pAuth.getIp().equals(ip)) { pAuth.setIp(ip); database.updateIp(pAuth.getNickname(), ip); } @@ -159,7 +151,7 @@ public class AsynchronousLogin { .build(); database.updateSession(auth); - if (Settings.useCaptcha) { + if (service.getProperty(SecuritySettings.USE_CAPTCHA)) { if (plugin.captcha.containsKey(name)) { plugin.captcha.remove(name); } @@ -170,12 +162,12 @@ public class AsynchronousLogin { player.setNoDamageTicks(0); if (!forceLogin) - m.send(player, MessageKey.LOGIN_SUCCESS); + service.send(player, MessageKey.LOGIN_SUCCESS); displayOtherAccounts(auth); if (Settings.recallEmail && (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email))) { - m.send(player, MessageKey.EMAIL_ADDED_SUCCESS); + service.send(player, MessageKey.ADD_EMAIL_MESSAGE); } if (!Settings.noConsoleSpam) { @@ -185,35 +177,35 @@ 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 // so that we can be sure, and have not to care if it might be // processed in other order. - ProcessSyncPlayerLogin syncPlayerLogin = new ProcessSyncPlayerLogin(player, plugin, database, settings); + ProcessSyncPlayerLogin syncPlayerLogin = new ProcessSyncPlayerLogin( + player, plugin, database, service.getSettings()); if (syncPlayerLogin.getLimbo() != null) { - if (syncPlayerLogin.getLimbo().getTimeoutTaskId() != null) { - syncPlayerLogin.getLimbo().getTimeoutTaskId().cancel(); + if (syncPlayerLogin.getLimbo().getTimeoutTask() != null) { + syncPlayerLogin.getLimbo().getTimeoutTask().cancel(); } - if (syncPlayerLogin.getLimbo().getMessageTaskId() != null) { - syncPlayerLogin.getLimbo().getMessageTaskId().cancel(); + if (syncPlayerLogin.getLimbo().getMessageTask() != null) { + syncPlayerLogin.getLimbo().getMessageTask().cancel(); } } Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, syncPlayerLogin); } else if (player.isOnline()) { - if (!Settings.noConsoleSpam) + if (!service.getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) { ConsoleLogger.info(realName + " used the wrong password"); - if (Settings.isKickOnWrongPasswordEnabled) { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { - + } + if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { - player.kickPlayer(m.retrieveSingle(MessageKey.WRONG_PASSWORD)); + player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD)); } }); } else { - m.send(player, MessageKey.WRONG_PASSWORD); + service.send(player, MessageKey.WRONG_PASSWORD); } } else { ConsoleLogger.showError("Player " + name + " wasn't online during login process, aborted... "); @@ -232,8 +224,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..b454f370 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(); @@ -69,7 +67,7 @@ public class ProcessSyncPlayerLogin implements Runnable { } private void restoreOpState() { - player.setOp(limbo.getOperator()); + player.setOp(limbo.isOperator()); } private void packQuitLocation() { diff --git a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java index 3e719060..a882ab30 100644 --- a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java @@ -6,22 +6,22 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils.GroupType; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitScheduler; /** */ -public class AsynchronousLogout { +public class AsynchronousLogout implements Process { - protected final Player player; - protected final String name; - protected final AuthMe plugin; - protected final DataSource database; - protected boolean canLogout = true; - private final Messages m; + private final Player player; + private final String name; + private final AuthMe plugin; + private final DataSource database; + private boolean canLogout = true; + private final ProcessService service; /** * Constructor for AsynchronousLogout. @@ -29,29 +29,30 @@ public class AsynchronousLogout { * @param player Player * @param plugin AuthMe * @param database DataSource + * @param service The process service */ - public AsynchronousLogout(Player player, AuthMe plugin, DataSource database) { - this.m = plugin.getMessages(); + public AsynchronousLogout(Player player, AuthMe plugin, DataSource database, ProcessService service) { this.player = player; this.plugin = plugin; this.database = database; this.name = player.getName().toLowerCase(); + this.service = service; } private void preLogout() { if (!PlayerCache.getInstance().isAuthenticated(name)) { - m.send(player, MessageKey.NOT_LOGGED_IN); + service.send(player, MessageKey.NOT_LOGGED_IN); canLogout = false; } } - public void process() { + @Override + public void run() { preLogout(); if (!canLogout) { return; } final Player p = player; - BukkitScheduler scheduler = p.getServer().getScheduler(); PlayerAuth auth = PlayerCache.getInstance().getAuth(name); database.updateSession(auth); auth.setQuitLocX(p.getLocation().getX()); @@ -62,7 +63,7 @@ public class AsynchronousLogout { PlayerCache.getInstance().removePlayer(name); database.setUnlogged(name); - scheduler.scheduleSyncDelayedTask(plugin, new Runnable() { + service.scheduleSyncDelayedTask(new Runnable() { @Override public void run() { Utils.teleportToSpawn(p); @@ -73,6 +74,6 @@ public class AsynchronousLogout { } LimboCache.getInstance().addLimboPlayer(player); Utils.setGroup(player, GroupType.NOTLOGGEDIN); - scheduler.scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerLogout(p, plugin)); + service.scheduleSyncDelayedTask(new ProcessSynchronousPlayerLogout(p, plugin, service)); } } diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java b/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java similarity index 67% rename from src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java rename to src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java index 64c300ef..bc2ea16d 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/ProcessSynchronousPlayerLogout.java @@ -7,37 +7,40 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.events.LogoutEvent; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.Settings; +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 org.bukkit.Bukkit; 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 ProcessSyncronousPlayerLogout implements Runnable { +public class ProcessSynchronousPlayerLogout implements Process { - protected final Player player; - protected final AuthMe plugin; - protected final String name; - private final Messages m; + private final Player player; + private final AuthMe plugin; + private final String name; + private final ProcessService service; /** - * Constructor for ProcessSyncronousPlayerLogout. + * Constructor for ProcessSynchronousPlayerLogout. * * @param player Player * @param plugin AuthMe + * @param service The process service */ - public ProcessSyncronousPlayerLogout(Player player, AuthMe plugin) { - this.m = plugin.getMessages(); + public ProcessSynchronousPlayerLogout(Player player, AuthMe plugin, ProcessService service) { this.player = player; this.plugin = plugin; this.name = player.getName().toLowerCase(); + this.service = service; } protected void sendBungeeMessage() { @@ -56,11 +59,6 @@ public class ProcessSyncronousPlayerLogout implements Runnable { } } - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { if (plugin.sessions.containsKey(name)) { @@ -70,19 +68,18 @@ public class ProcessSyncronousPlayerLogout implements Runnable { if (Settings.protectInventoryBeforeLogInEnabled) { plugin.inventoryProtector.sendBlankInventoryPacket(player); } - int timeOut = Settings.getRegistrationTimeout * 20; - int interval = Settings.getWarnMessageInterval; - BukkitScheduler sched = player.getServer().getScheduler(); + int timeOut = service.getProperty(RestrictionSettings.TIMEOUT) * 20; + int interval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); if (timeOut != 0) { - BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); - LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); + BukkitTask id = service.runTaskLater(new TimeoutTask(plugin, name, player), timeOut); + LimboCache.getInstance().getLimboPlayer(name).setTimeoutTask(id); } - BukkitTask msgT = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); + BukkitTask msgT = service.runTask(new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); + LimboCache.getInstance().getLimboPlayer(name).setMessageTask(msgT); if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); } - if (Settings.applyBlindEffect) { + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); } player.setOp(false); @@ -92,7 +89,7 @@ public class ProcessSyncronousPlayerLogout implements Runnable { if (Settings.bungee) { sendBungeeMessage(); } - m.send(player, MessageKey.LOGOUT_SUCCESS); + service.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 7f716e7e..cb2a79a9 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -7,15 +7,17 @@ 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.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RestrictionSettings; 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 { +public class AsynchronousQuit implements Process { private final AuthMe plugin; private final DataSource database; @@ -23,33 +25,40 @@ public class AsynchronousQuit { private final String name; private boolean isOp = false; private boolean needToChange = false; - private boolean isKick = false; + private final boolean isKick; + private final ProcessService service; - public AsynchronousQuit(Player p, AuthMe plugin, DataSource database, - boolean isKick) { + public AsynchronousQuit(Player p, AuthMe plugin, DataSource database, boolean isKick, ProcessService service) { this.player = p; this.plugin = plugin; this.database = database; this.name = p.getName().toLowerCase(); this.isKick = isKick; + this.service = service; } - public void process() { + @Override + public void run() { if (player == null || Utils.isUnrestricted(player)) { return; } - String ip = plugin.getIP(player); + String ip = service.getIpAddressManager().getPlayerIp(player); if (PlayerCache.getInstance().isAuthenticated(name)) { - if (Settings.isSaveQuitLocationEnabled) { + if (service.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) { Location loc = player.getLocation(); 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()); + PlayerAuth auth = PlayerAuth.builder() + .name(name) + .realName(player.getName()) + .ip(ip) + .lastLogin(System.currentTimeMillis()) + .build(); database.updateSession(auth); } @@ -59,7 +68,7 @@ public class AsynchronousQuit { Utils.addNormal(player, limbo.getGroup()); } needToChange = true; - isOp = limbo.getOperator(); + isOp = limbo.isOperator(); LimboCache.getInstance().deleteLimboPlayer(name); } if (Settings.isSessionsEnabled && !isKick) { @@ -76,7 +85,7 @@ public class AsynchronousQuit { plugin.sessions.put(name, task); } else { - //plugin is disable we canno schedule more tasks so run it directly here + //plugin is disabled; we cannot schedule more tasks so run it directly here postLogout(); } } @@ -85,9 +94,9 @@ public class AsynchronousQuit { database.setUnlogged(name); } - plugin.realIp.remove(name); + service.getIpAddressManager().removeCache(player.getName()); if (plugin.isEnabled()) { - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); + service.scheduleSyncDelayedTask(new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); } // remove player from cache if (database instanceof CacheDataSource) { 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..47b715a3 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -5,20 +5,23 @@ 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.permission.PlayerStatePermission; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.TwoFactor; -import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.StringUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.List; + /** */ -public class AsyncRegister { +public class AsyncRegister implements Process { private final Player player; private final String name; @@ -27,65 +30,68 @@ public class AsyncRegister { private final String email; private final AuthMe plugin; private final DataSource database; - private final Messages m; - private final NewSetting settings; + private final ProcessService service; public AsyncRegister(Player player, String password, String email, AuthMe plugin, DataSource data, - NewSetting settings) { - this.m = plugin.getMessages(); + ProcessService service) { this.player = player; this.password = password; this.name = player.getName().toLowerCase(); this.email = email; this.plugin = plugin; this.database = data; - this.ip = plugin.getIP(player); - this.settings = settings; + this.ip = service.getIpAddressManager().getPlayerIp(player); + this.service = service; } private boolean preRegisterCheck() { String passLow = password.toLowerCase(); if (PlayerCache.getInstance().isAuthenticated(name)) { - m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); + service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); return false; } else if (!Settings.isRegistrationEnabled) { - m.send(player, MessageKey.REGISTRATION_DISABLED); + service.send(player, MessageKey.REGISTRATION_DISABLED); return false; } //check the password safety only if it's not a automatically generated password - if (Settings.getPasswordHash != HashAlgorithm.TWO_FACTOR) { + if (service.getProperty(SecuritySettings.PASSWORD_HASH) != HashAlgorithm.TWO_FACTOR) { if (!passLow.matches(Settings.getPassRegex)) { - m.send(player, MessageKey.PASSWORD_MATCH_ERROR); + service.send(player, MessageKey.PASSWORD_MATCH_ERROR); return false; } else if (passLow.equalsIgnoreCase(player.getName())) { - m.send(player, MessageKey.PASSWORD_IS_USERNAME_ERROR); + service.send(player, MessageKey.PASSWORD_IS_USERNAME_ERROR); return false; } else if (password.length() < Settings.getPasswordMinLen || password.length() > Settings.passwordMaxLength) { - m.send(player, MessageKey.INVALID_PASSWORD_LENGTH); + service.send(player, MessageKey.INVALID_PASSWORD_LENGTH); return false; } else if (!Settings.unsafePasswords.isEmpty() && Settings.unsafePasswords.contains(password.toLowerCase())) { - m.send(player, MessageKey.PASSWORD_UNSAFE_ERROR); + service.send(player, MessageKey.PASSWORD_UNSAFE_ERROR); return false; } } - //check this in both possiblities so don't use 'else if' + //check this in both possibilities so don't use 'else if' if (database.isAuthAvailable(name)) { - m.send(player, MessageKey.NAME_ALREADY_REGISTERED); + service.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)) { + int maxReg = Settings.getmaxRegPerIp; + List otherAccounts = database.getAllAuthsByIp(ip); + if (otherAccounts.size() >= maxReg) { + service.send(player, MessageKey.MAX_REGISTER_EXCEEDED, Integer.toString(maxReg), + Integer.toString(otherAccounts.size()), StringUtils.join(", ", otherAccounts.toString())); + return false; + } } return true; } - public void process() { + @Override + public void run() { if (preRegisterCheck()) { if (!StringUtils.isEmpty(email)) { emailRegister(); @@ -97,12 +103,17 @@ 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; + && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)) { + int maxReg = Settings.getmaxRegPerEmail; + int otherAccounts = database.countAuthsByEmail(email); + if (otherAccounts >= maxReg) { + service.send(player, MessageKey.MAX_REGISTER_EXCEEDED, Integer.toString(maxReg), + Integer.toString(otherAccounts), "@"); + return; + } } - final HashedPassword hashedPassword = plugin.getPasswordSecurity().computeHash(password, name); + + final HashedPassword hashedPassword = service.computeHash(password, name); PlayerAuth auth = PlayerAuth.builder() .name(name) .realName(player.getName()) @@ -113,19 +124,19 @@ public class AsyncRegister { .build(); if (!database.saveAuth(auth)) { - m.send(player, MessageKey.ERROR); + service.send(player, MessageKey.ERROR); return; } database.updateEmail(auth); database.updateSession(auth); plugin.mail.main(auth, password); - ProcessSyncEmailRegister sync = new ProcessSyncEmailRegister(player, plugin); - plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, sync); + ProcessSyncEmailRegister sync = new ProcessSyncEmailRegister(player, service); + service.scheduleSyncDelayedTask(sync); } private void passwordRegister() { - final HashedPassword hashedPassword = plugin.getPasswordSecurity().computeHash(password, name); + final HashedPassword hashedPassword = service.computeHash(password, name); PlayerAuth auth = PlayerAuth.builder() .name(name) .realName(player.getName()) @@ -135,7 +146,7 @@ public class AsyncRegister { .build(); if (!database.saveAuth(auth)) { - m.send(player, MessageKey.ERROR); + service.send(player, MessageKey.ERROR); return; } @@ -146,14 +157,13 @@ 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); + ProcessSyncPasswordRegister sync = new ProcessSyncPasswordRegister(player, plugin, service); + service.scheduleSyncDelayedTask(sync); //give the user the secret code to setup their app code generation - if (Settings.getPasswordHash == HashAlgorithm.TWO_FACTOR) { + if (service.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { String qrCodeUrl = TwoFactor.getQRBarcodeURL(player.getName(), Bukkit.getIp(), hashedPassword.getHash()); - m.send(player, MessageKey.TWO_FACTOR_CREATE, hashedPassword.getHash(), qrCodeUrl); + service.send(player, MessageKey.TWO_FACTOR_CREATE, hashedPassword.getHash(), qrCodeUrl); } } } 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 47264c0f..a58fdded 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncEmailRegister.java @@ -1,70 +1,64 @@ package fr.xephi.authme.process.register; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; /** */ -public class ProcessSyncEmailRegister implements Runnable { +public class ProcessSyncEmailRegister implements Process { - protected final Player player; - protected final String name; - private final AuthMe plugin; - private final Messages m; + private final Player player; + private final String name; + private final ProcessService service; /** * Constructor for ProcessSyncEmailRegister. * - * @param player Player - * @param plugin AuthMe + * @param player The player to process an email registration for + * @param service The process service */ - public ProcessSyncEmailRegister(Player player, AuthMe plugin) { - this.m = plugin.getMessages(); + public ProcessSyncEmailRegister(Player player, ProcessService service) { this.player = player; this.name = player.getName().toLowerCase(); - this.plugin = plugin; + this.service = service; } - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); if (!Settings.getRegisteredGroup.isEmpty()) { Utils.setGroup(player, Utils.GroupType.REGISTERED); } - m.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); - int time = Settings.getRegistrationTimeout * 20; - int msgInterval = Settings.getWarnMessageInterval; - - BukkitScheduler sched = plugin.getServer().getScheduler(); + service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); + int time = service.getProperty(RestrictionSettings.TIMEOUT) * 20; + int msgInterval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); if (limbo != null) { if (time != 0) { - BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), time); - limbo.setTimeoutTaskId(id); + BukkitTask id = service.runTaskLater(new TimeoutTask(service.getAuthMe(), name, player), time); + limbo.setTimeoutTask(id); } - BukkitTask nwMsg = sched.runTask(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); - limbo.setMessageTaskId(nwMsg); + BukkitTask messageTask = service.runTask(new MessageTask( + service.getAuthMe(), name, service.retrieveMessage(MessageKey.LOGIN_MESSAGE), msgInterval)); + limbo.setMessageTask(messageTask); } player.saveData(); - if (!Settings.noConsoleSpam) { - ConsoleLogger.info(player.getName() + " registered " + plugin.getIP(player)); + if (!service.getProperty(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE)) { + ConsoleLogger.info(player.getName() + " registered " + service.getIpAddressManager().getPlayerIp(player)); } } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 9f9f8919..c77a7d03 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -9,42 +9,34 @@ import fr.xephi.authme.cache.limbo.LimboPlayer; 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.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.properties.HooksSettings; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.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; /** */ -public class ProcessSyncPasswordRegister implements Runnable { +public class ProcessSyncPasswordRegister implements Process { protected final Player player; protected final String name; private final AuthMe plugin; - private final Messages m; - private final NewSetting settings; + private final ProcessService service; - /** - * Constructor for ProcessSyncPasswordRegister. - * - * @param player Player - * @param plugin AuthMe - * @param settings The plugin settings - */ - public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) { - this.m = plugin.getMessages(); + public ProcessSyncPasswordRegister(Player player, AuthMe plugin, ProcessService service) { this.player = player; this.name = player.getName().toLowerCase(); this.plugin = plugin; - this.settings = settings; + this.service = service; } private void sendBungeeMessage() { @@ -70,16 +62,15 @@ public class ProcessSyncPasswordRegister implements Runnable { Utils.teleportToSpawn(player); LimboCache cache = LimboCache.getInstance(); cache.updateLimboPlayer(player); - int delay = Settings.getRegistrationTimeout * 20; - int interval = Settings.getWarnMessageInterval; - BukkitScheduler sched = plugin.getServer().getScheduler(); + int delay = service.getProperty(RestrictionSettings.TIMEOUT) * 20; + int interval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); BukkitTask task; if (delay != 0) { - task = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), delay); - cache.getLimboPlayer(name).setTimeoutTaskId(task); + task = service.runTaskLater(new TimeoutTask(service.getAuthMe(), name, player), delay); + cache.getLimboPlayer(name).setTimeoutTask(task); } - task = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); - cache.getLimboPlayer(name).setMessageTaskId(task); + task = service.runTask(new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval)); + cache.getLimboPlayer(name).setMessageTask(task); if (player.isInsideVehicle() && player.getVehicle() != null) { player.getVehicle().eject(); } @@ -106,13 +97,13 @@ public class ProcessSyncPasswordRegister implements Runnable { Utils.setGroup(player, Utils.GroupType.REGISTERED); } - m.send(player, MessageKey.REGISTER_SUCCESS); + service.send(player, MessageKey.REGISTER_SUCCESS); if (!Settings.getmailAccount.isEmpty()) { - m.send(player, MessageKey.ADD_EMAIL_MESSAGE); + service.send(player, MessageKey.ADD_EMAIL_MESSAGE); } - if (Settings.applyBlindEffect) { + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { player.removePotionEffect(PotionEffectType.BLINDNESS); } @@ -121,23 +112,23 @@ public class ProcessSyncPasswordRegister implements Runnable { player.saveData(); if (!Settings.noConsoleSpam) { - ConsoleLogger.info(player.getName() + " registered " + plugin.getIP(player)); + ConsoleLogger.info(player.getName() + " registered " + service.getIpAddressManager().getPlayerIp(player)); } // Kick Player after Registration is enabled, kick the player if (Settings.forceRegKick) { - player.kickPlayer(m.retrieveSingle(MessageKey.REGISTER_SUCCESS)); + player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS)); return; } // Register is finish and player is logged, display welcome message - if (Settings.useWelcomeMessage) { - if (Settings.broadcastWelcomeMessage) { - for (String s : settings.getWelcomeMessage()) { + if (service.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) { + if (service.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) { + for (String s : service.getSettings().getWelcomeMessage()) { plugin.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); } } else { - for (String s : settings.getWelcomeMessage()) { + for (String s : service.getSettings().getWelcomeMessage()) { player.sendMessage(plugin.replaceAllInfo(s, player)); } } @@ -160,10 +151,10 @@ public class ProcessSyncPasswordRegister implements Runnable { } private void sendTo() { - if (!settings.getProperty(HooksSettings.BUNGEECORD_SERVER).isEmpty()) { + if (!service.getProperty(HooksSettings.BUNGEECORD_SERVER).isEmpty()) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("Connect"); - out.writeUTF(settings.getProperty(HooksSettings.BUNGEECORD_SERVER)); + out.writeUTF(service.getProperty(HooksSettings.BUNGEECORD_SERVER)); player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); } } 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 7da80c39..29c2b8c3 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -8,8 +8,11 @@ import fr.xephi.authme.cache.backup.JsonCache; import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; +import fr.xephi.authme.process.Process; +import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.settings.Settings; +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; @@ -20,47 +23,46 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; -public class AsynchronousUnregister { +public class AsynchronousUnregister implements Process { private final Player player; private final String name; private final String password; private final boolean force; private final AuthMe plugin; - private final Messages m; private final JsonCache playerCache; + private final ProcessService service; /** - * Constructor for AsynchronousUnregister. + * Constructor. * - * @param player Player - * @param password String - * @param force boolean - * @param plugin AuthMe + * @param player The player to perform the action for + * @param password The password + * @param force True to bypass password validation + * @param plugin The plugin instance + * @param service The process service */ - public AsynchronousUnregister(Player player, String password, boolean force, AuthMe plugin) { - this.m = plugin.getMessages(); + public AsynchronousUnregister(Player player, String password, boolean force, AuthMe plugin, + ProcessService service) { this.player = player; this.name = player.getName().toLowerCase(); this.password = password; this.force = force; this.plugin = plugin; this.playerCache = new JsonCache(); + this.service = service; } - protected String getIp() { - return plugin.getIP(player); - } - - public void process() { + @Override + public void run() { PlayerAuth cachedAuth = PlayerCache.getInstance().getAuth(name); if (force || plugin.getPasswordSecurity().comparePassword( password, cachedAuth.getPassword(), player.getName())) { - if (!plugin.getDataSource().removeAuth(name)) { - m.send(player, MessageKey.ERROR); + if (!service.getDataSource().removeAuth(name)) { + service.send(player, MessageKey.ERROR); return; } - int timeOut = Settings.getRegistrationTimeout * 20; + int timeOut = service.getProperty(RestrictionSettings.TIMEOUT) * 20; if (Settings.isForcedRegistrationEnabled) { Utils.teleportToSpawn(player); player.saveData(); @@ -70,15 +72,15 @@ public class AsynchronousUnregister { } LimboCache.getInstance().addLimboPlayer(player); LimboPlayer limboPlayer = LimboCache.getInstance().getLimboPlayer(name); - int interval = Settings.getWarnMessageInterval; + int interval = service.getProperty(RegistrationSettings.MESSAGE_INTERVAL); BukkitScheduler scheduler = plugin.getServer().getScheduler(); if (timeOut != 0) { BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut); - limboPlayer.setTimeoutTaskId(id); + limboPlayer.setTimeoutTask(id); } - limboPlayer.setMessageTaskId(scheduler.runTask(plugin, + limboPlayer.setMessageTask(scheduler.runTask(plugin, new MessageTask(plugin, name, MessageKey.REGISTER_MESSAGE, interval))); - m.send(player, MessageKey.UNREGISTERED_SUCCESS); + service.send(player, MessageKey.UNREGISTERED_SUCCESS); ConsoleLogger.info(player.getDisplayName() + " unregistered himself"); return; } @@ -92,14 +94,14 @@ public class AsynchronousUnregister { playerCache.removeCache(player); } // Apply blind effect - if (Settings.applyBlindEffect) { + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2)); } - m.send(player, MessageKey.UNREGISTERED_SUCCESS); + service.send(player, MessageKey.UNREGISTERED_SUCCESS); ConsoleLogger.info(player.getDisplayName() + " unregistered himself"); Utils.teleportToSpawn(player); } else { - m.send(player, MessageKey.WRONG_PASSWORD); + service.send(player, MessageKey.WRONG_PASSWORD); } } } diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java index 1a8278e1..da506b6b 100644 --- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java +++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java @@ -4,6 +4,8 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.plugin.PluginManager; /** @@ -11,17 +13,16 @@ import org.bukkit.plugin.PluginManager; */ public class PasswordSecurity { + private HashAlgorithm algorithm; + private boolean supportOldAlgorithm; private final DataSource dataSource; - private final HashAlgorithm algorithm; private final PluginManager pluginManager; - private final boolean supportOldAlgorithm; - public PasswordSecurity(DataSource dataSource, HashAlgorithm algorithm, - PluginManager pluginManager, boolean supportOldAlgorithm) { + public PasswordSecurity(DataSource dataSource, NewSetting settings, PluginManager pluginManager) { + this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); + this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH); this.dataSource = dataSource; - this.algorithm = algorithm; this.pluginManager = pluginManager; - this.supportOldAlgorithm = supportOldAlgorithm; } public HashedPassword computeHash(String password, String playerName) { @@ -46,6 +47,11 @@ public class PasswordSecurity { || supportOldAlgorithm && compareWithAllEncryptionMethods(password, hashedPassword, playerLowerCase); } + public void reload(NewSetting settings) { + this.algorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH); + this.supportOldAlgorithm = settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH); + } + /** * Compare the given hash with all available encryption methods to support * the migration to a new encryption method. Upon a successful match, the password diff --git a/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java b/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java index 003b50bd..daadcae8 100644 --- a/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java +++ b/src/main/java/fr/xephi/authme/security/pbkdf2/BinTools.java @@ -106,8 +106,9 @@ public class BinTools { throw new IllegalArgumentException("Input string may only contain hex digits, but found '" + c + "'"); } - // TODO ljacqu 20151219: Move to a BinToolsTest class - private static void testUtils(String[] args) { + // Note ljacqu 20160313: This appears to be a test method that was present in the third-party source. + // We can keep it for troubleshooting in the future. + private static void testUtils() { byte b[] = new byte[256]; byte bb = 0; for (int i = 0; i < 256; i++) { diff --git a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java deleted file mode 100644 index 3defffe4..00000000 --- a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java +++ /dev/null @@ -1,94 +0,0 @@ -package fr.xephi.authme.settings; - -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.ConsoleLogger; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -/** - */ -public abstract class CustomConfiguration extends YamlConfiguration { - - private final File configFile; - - /** - * Constructor for CustomConfiguration. - * - * @param file the config file - */ - public CustomConfiguration(File file) { - this.configFile = file; - load(); - } - - public void load() { - try { - super.load(configFile); - } catch (FileNotFoundException e) { - ConsoleLogger.showError("Could not find " + configFile.getName() + ", creating new one..."); - reLoad(); - } catch (IOException e) { - ConsoleLogger.showError("Could not load " + configFile.getName()); - } catch (InvalidConfigurationException e) { - ConsoleLogger.showError(configFile.getName() + " is no valid configuration file"); - } - } - - public boolean reLoad() { - boolean out = true; - if (!configFile.exists()) { - out = loadResource(configFile); - } - if (out) - load(); - return out; - } - - public void save() { - try { - super.save(configFile); - } catch (IOException ex) { - ConsoleLogger.showError("Could not save config to " + configFile.getName()); - } - } - - public File getConfigFile() { - return configFile; - } - - private boolean loadResource(File file) { - if (!file.exists()) { - try { - if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { - return false; - } - int i = file.getPath().indexOf("AuthMe"); - if (i > -1) { - String path = file.getPath().substring(i + 6).replace('\\', '/'); - InputStream is = AuthMe.class.getResourceAsStream(path); - Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING); - return true; - } - } catch (Exception e) { - ConsoleLogger.logException("Failed to load config from JAR", e); - } - } - 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/NewSetting.java b/src/main/java/fr/xephi/authme/settings/NewSetting.java index 79557d9f..89717a8c 100644 --- a/src/main/java/fr/xephi/authme/settings/NewSetting.java +++ b/src/main/java/fr/xephi/authme/settings/NewSetting.java @@ -6,7 +6,6 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; -import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; @@ -24,7 +23,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import static fr.xephi.authme.settings.SettingsMigrationService.copyFileFromResource; +import static fr.xephi.authme.util.FileUtils.copyFileFromResource; /** * The new settings manager. @@ -33,6 +32,8 @@ public class NewSetting { private final File pluginFolder; private final File configFile; + private final PropertyMap propertyMap; + private final SettingsMigrationService migrationService; private FileConfiguration configuration; /** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */ private File messagesFile; @@ -44,11 +45,16 @@ public class NewSetting { * * @param configFile The configuration file * @param pluginFolder The AuthMe plugin folder + * @param propertyMap Collection of all available settings + * @param migrationService Migration service to check the settings file with */ - public NewSetting(File configFile, File pluginFolder) { + public NewSetting(File configFile, File pluginFolder, PropertyMap propertyMap, + SettingsMigrationService migrationService) { this.configuration = YamlConfiguration.loadConfiguration(configFile); this.configFile = configFile; this.pluginFolder = pluginFolder; + this.propertyMap = propertyMap; + this.migrationService = migrationService; validateAndLoadOptions(); } @@ -57,16 +63,21 @@ public class NewSetting { * * @param configuration The FileConfiguration object to use * @param configFile The file to write to + * @param pluginFolder The plugin folder * @param propertyMap The property map whose properties should be verified for presence, or null to skip this + * @param migrationService Migration service, or null to skip migration checks */ @VisibleForTesting - NewSetting(FileConfiguration configuration, File configFile, PropertyMap propertyMap) { + NewSetting(FileConfiguration configuration, File configFile, File pluginFolder, PropertyMap propertyMap, + SettingsMigrationService migrationService) { this.configuration = configuration; this.configFile = configFile; - this.pluginFolder = new File(""); + this.pluginFolder = pluginFolder; + this.propertyMap = propertyMap; + this.migrationService = migrationService; - if (propertyMap != null && SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { - save(propertyMap); + if (propertyMap != null && migrationService != null) { + validateAndLoadOptions(); } } @@ -92,13 +103,6 @@ public class NewSetting { configuration.set(property.getPath(), value); } - /** - * Save the config file. Use after migrating one or more settings. - */ - public void save() { - save(SettingsFieldRetriever.getAllPropertyFields()); - } - /** * Return the messages file based on the messages language config. * @@ -133,7 +137,10 @@ public class NewSetting { validateAndLoadOptions(); } - private void save(PropertyMap propertyMap) { + /** + * Save the config file. Use after migrating one or more settings. + */ + public void save() { try (FileWriter writer = new FileWriter(configFile)) { Yaml simpleYaml = newYaml(false); Yaml singleQuoteYaml = newYaml(true); @@ -186,11 +193,10 @@ public class NewSetting { } private void validateAndLoadOptions() { - PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); - if (SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { + if (migrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { ConsoleLogger.info("Merged new config options"); ConsoleLogger.info("Please check your config.yml file for new settings!"); - save(propertyMap); + save(); } messagesFile = buildMessagesFile(); @@ -205,18 +211,20 @@ public class NewSetting { private File buildMessagesFile() { String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE); - File messagesFile = buildMessagesFileFromCode(languageCode); - if (messagesFile.exists()) { + + String filePath = buildMessagesFilePathFromCode(languageCode); + File messagesFile = new File(pluginFolder, filePath); + if (copyFileFromResource(messagesFile, filePath)) { return messagesFile; } - return copyFileFromResource(messagesFile, buildMessagesFilePathFromCode(languageCode)) - ? messagesFile - : buildMessagesFileFromCode("en"); - } + // File doesn't exist or couldn't be copied - try again with default, "en" + String defaultFilePath = buildMessagesFilePathFromCode("en"); + File defaultFile = new File(pluginFolder, defaultFilePath); + copyFileFromResource(defaultFile, defaultFilePath); - private File buildMessagesFileFromCode(String language) { - return new File(pluginFolder, buildMessagesFilePathFromCode(language)); + // No matter the result, need to return a file + return defaultFile; } private static String buildMessagesFilePathFromCode(String language) { @@ -243,7 +251,7 @@ public class NewSetting { final Charset charset = Charset.forName("UTF-8"); if (copyFileFromResource(emailFile, "email.html")) { try { - return StringUtils.join("", Files.readLines(emailFile, charset)); + return StringUtils.join("\n", Files.readLines(emailFile, charset)); } catch (IOException e) { ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e); } 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..5989e216 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -1,10 +1,8 @@ package fr.xephi.authme.settings; import fr.xephi.authme.AuthMe; -import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.RegistrationSettings; @@ -41,13 +39,9 @@ public final class Settings { public static List forceRegisterCommands; public static List forceRegisterCommandsAsConsole; public static List unsafePasswords; - public static List emailBlacklist; - public static List emailWhitelist; - public static DataSourceType getDataSource; public static HashAlgorithm getPasswordHash; public static Pattern nickPattern; public static boolean useLogging = false; - public static int purgeDelay = 60; public static boolean isChatAllowed, isPermissionCheckEnabled, isRegistrationEnabled, isForcedRegistrationEnabled, isTeleportToSpawnEnabled, isSessionsEnabled, isAllowRestrictedIp, @@ -57,34 +51,27 @@ public final class Settings { isKickOnWrongPasswordEnabled, enablePasswordConfirmation, protectInventoryBeforeLogInEnabled, isStopEnabled, reloadSupport, rakamakUseIp, noConsoleSpam, removePassword, displayOtherAccounts, - useCaptcha, emailRegistration, multiverse, bungee, + emailRegistration, multiverse, bungee, banUnsafeIp, doubleEmailCheck, sessionExpireOnIpChange, - disableSocialSpy, useEssentialsMotd, usePurge, - purgePlayerDat, purgeEssentialsFile, supportOldPassword, - purgeLimitedCreative, purgeAntiXray, purgePermissions, - enableProtection, enableAntiBot, recallEmail, useWelcomeMessage, + disableSocialSpy, useEssentialsMotd, + enableProtection, recallEmail, useWelcomeMessage, broadcastWelcomeMessage, forceRegKick, forceRegLogin, checkVeryGames, removeJoinMessage, removeLeaveMessage, delayJoinMessage, - noTeleport, applyBlindEffect, hideTablistBeforeLogin, denyTabcompleteBeforeLogin, + noTeleport, hideTablistBeforeLogin, denyTabcompleteBeforeLogin, kickPlayersBeforeStopping, allowAllCommandsIfRegIsOptional, - customAttributes, generateImage, isRemoveSpeedEnabled, preventOtherCase; + customAttributes, isRemoveSpeedEnabled, preventOtherCase; public static String getNickRegex, getUnloggedinGroup, - getMySQLColumnGroup, unRegisteredGroup, + 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, - antiBotSensibility, antiBotDuration, delayRecall, getMaxLoginPerIp, - getMaxJoinPerIp; + getmaxRegPerEmail, bCryptLog2Rounds, getMaxLoginPerIp, getMaxJoinPerIp; protected static FileConfiguration configFile; - private static AuthMe plugin; - private static Settings instance; /** * Constructor for Settings. @@ -92,29 +79,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); @@ -128,8 +113,6 @@ public final class Settings { getmaxRegPerIp = configFile.getInt("settings.restrictions.maxRegPerIp", 1); getPasswordHash = load(SecuritySettings.PASSWORD_HASH); getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP); - getDataSource = load(DatabaseSettings.BACKEND); - getMySQLColumnGroup = configFile.getString("ExternalBoardOptions.mySQLColumnGroup", ""); getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1); unRegisteredGroup = configFile.getString("GroupOptions.UnregisteredPlayerGroup", ""); @@ -163,16 +146,15 @@ public final class Settings { rakamakUsers = configFile.getString("Converter.Rakamak.fileName", "users.rak"); rakamakUsersIp = configFile.getString("Converter.Rakamak.ipFileName", "UsersIp.rak"); rakamakUseIp = configFile.getBoolean("Converter.Rakamak.useIp", false); - noConsoleSpam = configFile.getBoolean("Security.console.noConsoleSpam", false); + noConsoleSpam = load(SecuritySettings.REMOVE_SPAM_FROM_CONSOLE); removePassword = configFile.getBoolean("Security.console.removePassword", true); getmailAccount = configFile.getString("Email.mailAccount", ""); getMailPort = configFile.getInt("Email.mailPort", 465); getRecoveryPassLength = configFile.getInt("Email.RecoveryPasswordLength", 8); displayOtherAccounts = configFile.getBoolean("settings.restrictions.displayOtherAccounts", true); - 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); @@ -185,50 +167,31 @@ public final class Settings { disableSocialSpy = configFile.getBoolean("Hooks.disableSocialSpy", true); bCryptLog2Rounds = configFile.getInt("ExternalBoardOptions.bCryptLog2Round", 10); useEssentialsMotd = configFile.getBoolean("Hooks.useEssentialsMotd", false); - usePurge = configFile.getBoolean("Purge.useAutoPurge", false); - purgeDelay = configFile.getInt("Purge.daysBeforeRemovePlayer", 60); - 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); enableProtection = configFile.getBoolean("Protection.enableProtection", false); countries = configFile.getStringList("Protection.countries"); - enableAntiBot = configFile.getBoolean("Protection.enableAntiBot", false); - antiBotSensibility = configFile.getInt("Protection.antiBotSensibility", 5); - antiBotDuration = configFile.getInt("Protection.antiBotDuration", 10); forceCommands = configFile.getStringList("settings.forceCommands"); forceCommandsAsConsole = configFile.getStringList("settings.forceCommandsAsConsole"); recallEmail = configFile.getBoolean("Email.recallPlayers", false); - delayRecall = configFile.getInt("Email.delayRecall", 5); - useWelcomeMessage = configFile.getBoolean("settings.useWelcomeMessage", true); + useWelcomeMessage = load(RegistrationSettings.USE_WELCOME_MESSAGE); unsafePasswords = configFile.getStringList("settings.security.unsafePasswords"); countriesBlacklist = configFile.getStringList("Protection.countriesBlacklist"); - broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false); + broadcastWelcomeMessage = load(RegistrationSettings.BROADCAST_WELCOME_MESSAGE); 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); - emailBlacklist = configFile.getStringList("Email.emailBlacklisted"); - emailWhitelist = configFile.getStringList("Email.emailWhitelisted"); forceRegisterCommands = configFile.getStringList("settings.forceRegisterCommands"); forceRegisterCommandsAsConsole = configFile.getStringList("settings.forceRegisterCommandsAsConsole"); customAttributes = configFile.getBoolean("Hooks.customAttributes"); - generateImage = configFile.getBoolean("Email.generateImage", false); preventOtherCase = configFile.getBoolean("settings.preventOtherCase", false); kickPlayersBeforeStopping = configFile.getBoolean("Security.stop.kickPlayersBeforeStopping", true); sendPlayerTo = configFile.getString("Hooks.sendPlayerTo", ""); diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index dc448027..a3d70dd1 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -1,7 +1,5 @@ package fr.xephi.authme.settings; -import com.google.common.annotations.VisibleForTesting; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.propertymap.PropertyMap; @@ -10,37 +8,32 @@ import org.bukkit.configuration.file.FileConfiguration; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; import static fr.xephi.authme.settings.properties.RegistrationSettings.DELAY_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_JOIN_MESSAGE; import static fr.xephi.authme.settings.properties.RegistrationSettings.REMOVE_LEAVE_MESSAGE; import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS; -import static java.lang.String.format; /** * Service for verifying that the configuration is up-to-date. */ -public final class SettingsMigrationService { - - private SettingsMigrationService() { - } +public class SettingsMigrationService { /** - * Checks the config file and does any necessary migrations. + * Checks the config file and performs any necessary migrations. * * @param configuration The file configuration to check and migrate * @param propertyMap The property map of all existing properties * @param pluginFolder The plugin folder * @return True if there is a change and the config must be saved, false if the config is up-to-date */ - public static boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { - return performMigrations(configuration, pluginFolder) || hasDeprecatedProperties(configuration) + public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + return performMigrations(configuration, pluginFolder) + || hasDeprecatedProperties(configuration) || !containsAllSettings(configuration, propertyMap); } - private static boolean performMigrations(FileConfiguration configuration, File pluginFolder) { + private boolean performMigrations(FileConfiguration configuration, File pluginFolder) { boolean changes = false; if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) { configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*"); @@ -54,8 +47,7 @@ public final class SettingsMigrationService { | migrateJoinLeaveMessages(configuration); } - @VisibleForTesting - static boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { + public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { for (Property property : propertyMap.keySet()) { if (!property.isPresent(configuration)) { return false; @@ -84,16 +76,16 @@ public final class SettingsMigrationService { * Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet. * * @param configuration The file configuration to verify - * @param dataFolder The plugin data folder + * @param pluginFolder The plugin data folder * @return True if a migration has been completed, false otherwise */ - private static boolean performMailTextToFileMigration(FileConfiguration configuration, File dataFolder) { + private static boolean performMailTextToFileMigration(FileConfiguration configuration, File pluginFolder) { final String oldSettingPath = "Email.mailText"; if (!configuration.contains(oldSettingPath)) { return false; } - final File emailFile = new File(dataFolder, "email.html"); + final File emailFile = new File(pluginFolder, "email.html"); final String mailText = configuration.getString(oldSettingPath) .replace("", "") .replace("", "") @@ -131,41 +123,4 @@ public final class SettingsMigrationService { return false; } - - // ------- - // Utilities - // ------- - - /** - * Copy a resource file (from the JAR) to the given file if it doesn't exist. - * - * @param destinationFile The file to check and copy to (outside of JAR) - * @param resourcePath Absolute path to the resource file (path to file within JAR) - * @return False if the file does not exist and could not be copied, true otherwise - */ - public static boolean copyFileFromResource(File destinationFile, String resourcePath) { - if (destinationFile.exists()) { - return true; - } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) { - ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'"); - return false; - } - - // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' - final String normalizedPath = resourcePath.replace("\\", "/"); - try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { - if (is == null) { - ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource", - resourcePath, destinationFile.getPath())); - } else { - Files.copy(is, destinationFile.toPath()); - return true; - } - } catch (IOException e) { - ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':", - resourcePath, destinationFile.getPath()), e); - } - return false; - } - } diff --git a/src/main/java/fr/xephi/authme/settings/Spawn.java b/src/main/java/fr/xephi/authme/settings/Spawn.java deleted file mode 100644 index e891e47f..00000000 --- a/src/main/java/fr/xephi/authme/settings/Spawn.java +++ /dev/null @@ -1,148 +0,0 @@ -package fr.xephi.authme.settings; - -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import fr.xephi.authme.AuthMe; -import fr.xephi.authme.cache.auth.PlayerCache; -import fr.xephi.authme.util.StringUtils; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import java.io.File; - -/** - * @author Xephi59 - * @version $Revision: 1.0 $ - */ -public class Spawn extends CustomConfiguration { - - private static Spawn spawn; - private static String[] spawnPriority; - - private Spawn() { - super(new File(Settings.PLUGIN_FOLDER, "spawn.yml")); - load(); - save(); - spawnPriority = Settings.spawnPriority.split(","); - } - - public static void reload() { - spawn = new Spawn(); - } - - /** - * Method getInstance. - * - * @return Spawn - */ - public static Spawn getInstance() { - if (spawn == null) { - spawn = new Spawn(); - } - return spawn; - } - - public boolean setSpawn(Location location) { - if (location == null || location.getWorld() == null) { - return false; - } - set("spawn.world", location.getWorld().getName()); - set("spawn.x", location.getX()); - set("spawn.y", location.getY()); - set("spawn.z", location.getZ()); - set("spawn.yaw", location.getYaw()); - set("spawn.pitch", location.getPitch()); - save(); - return true; - } - - public boolean setFirstSpawn(Location location) { - if (location == null || location.getWorld() == null) { - return false; - } - set("firstspawn.world", location.getWorld().getName()); - set("firstspawn.x", location.getX()); - set("firstspawn.y", location.getY()); - set("firstspawn.z", location.getZ()); - set("firstspawn.yaw", location.getYaw()); - set("firstspawn.pitch", location.getPitch()); - save(); - return true; - } - - public Location getSpawn() { - 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 new Location( - world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"), - Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch")) - ); - } - } - return null; - } - - public Location getFirstSpawn() { - 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 null; - } - - // Return the spawn location of a player - public Location getSpawnLocation(Player player) { - AuthMe plugin = AuthMe.getInstance(); - if (plugin == null || player == null || player.getWorld() == null) { - return null; - } - - World world = player.getWorld(); - Location spawnLoc = null; - for (String priority : spawnPriority) { - switch (priority.toLowerCase()) { - case "default": - if (world.getSpawnLocation() != null) { - spawnLoc = world.getSpawnLocation(); - } - break; - case "multiverse": - if (Settings.multiverse && plugin.multiverse != null) { - MVWorldManager manager = plugin.multiverse.getMVWorldManager(); - if (manager.isMVWorld(world)) { - spawnLoc = manager.getMVWorld(world).getSpawnLocation(); - } - } - break; - case "essentials": - spawnLoc = plugin.essentialsSpawn; - break; - case "authme": - String playerNameLower = player.getName().toLowerCase(); - if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) { - spawnLoc = getSpawn(); - } else if ((getFirstSpawn() != null) && (!player.hasPlayedBefore() || - (!plugin.getDataSource().isAuthAvailable(playerNameLower)))) { - spawnLoc = getFirstSpawn(); - } else { - spawnLoc = getSpawn(); - } - break; - } - if (spawnLoc != null) { - return spawnLoc; - } - } - return world.getSpawnLocation(); // return default location - } -} diff --git a/src/main/java/fr/xephi/authme/settings/SpawnLoader.java b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java new file mode 100644 index 00000000..59693a98 --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/SpawnLoader.java @@ -0,0 +1,260 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.util.FileUtils; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; + +/** + * Manager for spawn points. It loads spawn definitions from AuthMe and third-party plugins + * and is responsible for returning the correct spawn point as per the settings. + *

+ * The spawn priority setting defines from which sources and in which order the spawn point + * should be taken from. In AuthMe, we can distinguish between the regular spawn and a "first spawn", + * to which players will be teleported who have joined for the first time. + */ +public class SpawnLoader { + + private final File authMeConfigurationFile; + private final PluginHooks pluginHooks; + private FileConfiguration authMeConfiguration; + private String[] spawnPriority; + private Location essentialsSpawn; + + /** + * Constructor. + * + * @param pluginFolder The AuthMe data folder + * @param settings The setting instance + * @param pluginHooks The plugin hooks instance + */ + public SpawnLoader(File pluginFolder, NewSetting settings, PluginHooks pluginHooks) { + File spawnFile = new File(pluginFolder, "spawn.yml"); + // TODO ljacqu 20160312: Check if resource could be copied and handle the case if not + FileUtils.copyFileFromResource(spawnFile, "spawn.yml"); + this.authMeConfigurationFile = new File(pluginFolder, "spawn.yml"); + this.pluginHooks = pluginHooks; + initialize(settings); + } + + /** + * Retrieve the relevant settings and load the AuthMe spawn.yml file. + * + * @param settings The settings instance + */ + public void initialize(NewSetting settings) { + spawnPriority = settings.getProperty(RestrictionSettings.SPAWN_PRIORITY).split(","); + authMeConfiguration = YamlConfiguration.loadConfiguration(authMeConfigurationFile); + loadEssentialsSpawn(); + } + + /** + * Return the AuthMe spawn location. + * + * @return The location of the regular AuthMe spawn point + */ + public Location getSpawn() { + return getLocationFromConfiguration(authMeConfiguration, "spawn"); + } + + /** + * Set the AuthMe spawn point. + * + * @param location The location to use + * @return True upon success, false otherwise + */ + public boolean setSpawn(Location location) { + return setLocation("spawn", location); + } + + /** + * Return the AuthMe first spawn location. + * + * @return The location of the AuthMe spawn point for first timers + */ + public Location getFirstSpawn() { + return getLocationFromConfiguration(authMeConfiguration, "firstspawn"); + } + + /** + * Set the AuthMe first spawn location. + * + * @param location The location to use + * @return True upon success, false otherwise + */ + public boolean setFirstSpawn(Location location) { + return setLocation("firstspawn", location); + } + + /** + * Load the spawn point defined in EssentialsSpawn. + */ + public void loadEssentialsSpawn() { + // EssentialsSpawn cannot run without Essentials, so it's fine to get the Essentials data folder + File essentialsFolder = pluginHooks.getEssentialsDataFolder(); + if (essentialsFolder == null) { + return; + } + + File essentialsSpawnFile = new File(essentialsFolder, "spawn.yml"); + if (essentialsSpawnFile.exists()) { + essentialsSpawn = getLocationFromConfiguration( + YamlConfiguration.loadConfiguration(essentialsSpawnFile), "spawns.default"); + } else { + essentialsSpawn = null; + ConsoleLogger.info("Essentials spawn file not found: '" + essentialsSpawnFile.getAbsolutePath() + "'"); + } + } + + /** + * Unset the spawn point defined in EssentialsSpawn. + */ + public void unloadEssentialsSpawn() { + essentialsSpawn = null; + } + + /** + * Return the spawn location for the given player. The source of the spawn location varies + * depending on the spawn priority setting. + * + * @param player The player to retrieve the spawn point for + * @return The spawn location, or the default spawn location upon failure + * @see RestrictionSettings#SPAWN_PRIORITY + */ + public Location getSpawnLocation(Player player) { + AuthMe plugin = AuthMe.getInstance(); + if (plugin == null || player == null || player.getWorld() == null) { + return null; + } + + World world = player.getWorld(); + Location spawnLoc = null; + for (String priority : spawnPriority) { + switch (priority.toLowerCase().trim()) { + case "default": + if (world.getSpawnLocation() != null) { + spawnLoc = world.getSpawnLocation(); + } + break; + case "multiverse": + if (Settings.multiverse) { + spawnLoc = pluginHooks.getMultiverseSpawn(world); + } + break; + case "essentials": + spawnLoc = essentialsSpawn; + break; + case "authme": + String playerNameLower = player.getName().toLowerCase(); + if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) { + spawnLoc = getSpawn(); + } else if (getFirstSpawn() != null && (!player.hasPlayedBefore() || + !plugin.getDataSource().isAuthAvailable(playerNameLower))) { + spawnLoc = getFirstSpawn(); + } else { + spawnLoc = getSpawn(); + } + break; + } + if (spawnLoc != null) { + return spawnLoc; + } + } + return world.getSpawnLocation(); // return default location + } + + /** + * Save the location under the given prefix. + * + * @param prefix The prefix to save the spawn under + * @param location The location to persist + * @return True upon success, false otherwise + */ + private boolean setLocation(String prefix, Location location) { + if (location != null && location.getWorld() != null) { + authMeConfiguration.set(prefix + ".world", location.getWorld().getName()); + authMeConfiguration.set(prefix + ".x", location.getX()); + authMeConfiguration.set(prefix + ".y", location.getY()); + authMeConfiguration.set(prefix + ".z", location.getZ()); + authMeConfiguration.set(prefix + ".yaw", location.getYaw()); + authMeConfiguration.set(prefix + ".pitch", location.getPitch()); + return saveAuthMeConfig(); + } + return false; + } + + private boolean saveAuthMeConfig() { + // TODO ljacqu 20160312: Investigate whether this utility should be put in a Utils class + try { + authMeConfiguration.save(authMeConfigurationFile); + return true; + } catch (IOException e) { + ConsoleLogger.logException("Could not save spawn config (" + authMeConfigurationFile + ")", e); + } + return false; + } + + /** + * Build a {@link Location} object from the given path in the file configuration. + * + * @param configuration The file configuration to read from + * @param pathPrefix The path to get the spawn point from + * @return Location corresponding to the values in the path + */ + private static Location getLocationFromConfiguration(FileConfiguration configuration, String pathPrefix) { + if (containsAllSpawnFields(configuration, pathPrefix)) { + String prefix = pathPrefix + "."; + String worldName = configuration.getString(prefix + "world"); + World world = Bukkit.getWorld(worldName); + if (!StringUtils.isEmpty(worldName) && world != null) { + return new Location(world, configuration.getDouble(prefix + "x"), + configuration.getDouble(prefix + "y"), configuration.getDouble(prefix + "z"), + getFloat(configuration, prefix + "yaw"), getFloat(configuration, prefix + "pitch")); + } + } + return null; + } + + /** + * Return whether the file configuration contains all fields necessary to define a spawn + * under the given path. + * + * @param configuration The file configuration to use + * @param pathPrefix The path to verify + * @return True if all spawn fields are present, false otherwise + */ + private static boolean containsAllSpawnFields(FileConfiguration configuration, String pathPrefix) { + String[] fields = {"world", "x", "y", "z", "yaw", "pitch"}; + for (String field : fields) { + if (!configuration.contains(pathPrefix + "." + field)) { + return false; + } + } + return true; + } + + /** + * Retrieve a property as a float from the given file configuration. + * + * @param configuration The file configuration to use + * @param path The path of the property to retrieve + * @return The float + */ + private static float getFloat(FileConfiguration configuration, String path) { + Object value = configuration.get(path); + // This behavior is consistent with FileConfiguration#getDouble + return (value instanceof Number) ? ((Number) value).floatValue() : 0; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/domain/Comment.java b/src/main/java/fr/xephi/authme/settings/domain/Comment.java index 07f20e25..0e69ec97 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Comment.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Comment.java @@ -12,6 +12,6 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) public @interface Comment { - String[] value(); + String[] value(); } diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java new file mode 100644 index 00000000..14d09329 --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/domain/EnumProperty.java @@ -0,0 +1,49 @@ +package fr.xephi.authme.settings.domain; + +import org.bukkit.configuration.file.FileConfiguration; +import org.yaml.snakeyaml.Yaml; + +/** + * Enum property. + * + * @param The enum class + */ +class EnumProperty> extends Property { + + private Class clazz; + + public EnumProperty(Class clazz, String path, E defaultValue) { + super(path, defaultValue); + this.clazz = clazz; + } + + @Override + public E getFromFile(FileConfiguration configuration) { + String textValue = configuration.getString(getPath()); + if (textValue == null) { + return getDefaultValue(); + } + E mappedValue = mapToEnum(textValue); + return mappedValue != null ? mappedValue : getDefaultValue(); + } + + @Override + public boolean isPresent(FileConfiguration configuration) { + return super.isPresent(configuration) && mapToEnum(configuration.getString(getPath())) != null; + } + + @Override + public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { + E value = getFromFile(configuration); + return singleQuoteYaml.dump(value.name()); + } + + private E mapToEnum(String value) { + for (E entry : clazz.getEnumConstants()) { + if (entry.name().equalsIgnoreCase(value)) { + return entry; + } + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java deleted file mode 100644 index d38d0649..00000000 --- a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java +++ /dev/null @@ -1,48 +0,0 @@ -package fr.xephi.authme.settings.domain; - -import org.bukkit.configuration.file.FileConfiguration; -import org.yaml.snakeyaml.Yaml; - -/** - * Enum property type. - * - * @param The enum class - */ -class EnumPropertyType> extends PropertyType { - - private Class clazz; - - public EnumPropertyType(Class clazz) { - this.clazz = clazz; - } - - @Override - public E getFromFile(Property property, FileConfiguration configuration) { - String textValue = configuration.getString(property.getPath()); - if (textValue == null) { - return property.getDefaultValue(); - } - E mappedValue = mapToEnum(textValue); - return mappedValue != null ? mappedValue : property.getDefaultValue(); - } - - @Override - public boolean contains(Property property, FileConfiguration configuration) { - return super.contains(property, configuration) - && mapToEnum(configuration.getString(property.getPath())) != null; - } - - @Override - public String toYaml(E value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return singleQuoteYaml.dump(value.name()); - } - - private E mapToEnum(String value) { - for (E entry : clazz.getEnumConstants()) { - if (entry.name().equalsIgnoreCase(value)) { - return entry; - } - } - return null; - } -} diff --git a/src/main/java/fr/xephi/authme/settings/domain/Property.java b/src/main/java/fr/xephi/authme/settings/domain/Property.java index f9637a7b..1d5100af 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Property.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Property.java @@ -10,45 +10,27 @@ import java.util.Objects; /** * Property class, representing a setting that is read from the config.yml file. */ -public class Property { +public abstract class Property { - private final PropertyType type; private final String path; private final T defaultValue; - private Property(PropertyType type, String path, T defaultValue) { + protected Property(String path, T defaultValue) { Objects.requireNonNull(defaultValue); - this.type = type; this.path = path; this.defaultValue = defaultValue; } /** - * Create a new property. See also {@link #newProperty(PropertyType, String, Object[])} for lists and - * {@link #newProperty(Class, String, Enum)}. + * Create a new string list property. * - * @param type The property type * @param path The property's path - * @param defaultValue The default value - * @param The type of the property - * @return The created property - */ - public static Property newProperty(PropertyType type, String path, T defaultValue) { - return new Property<>(type, path, defaultValue); - } - - /** - * Create a new list property. - * - * @param type The list type of the property - * @param path The property's path - * @param defaultValues The default value's items - * @param The list type + * @param defaultValues The items in the default list * @return The created list property */ - @SafeVarargs - public static Property> newProperty(PropertyType> type, String path, U... defaultValues) { - return new Property<>(type, path, Arrays.asList(defaultValues)); + public static Property> newListProperty(String path, String... defaultValues) { + // does not have the same name as not to clash with #newProperty(String, String) + return new StringListProperty(path, defaultValues); } /** @@ -61,36 +43,28 @@ public class Property { * @return The created enum property */ public static > Property newProperty(Class clazz, String path, E defaultValue) { - return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue); + return new EnumProperty<>(clazz, path, defaultValue); } - // ----- - // Overloaded convenience methods for specific types - // ----- public static Property newProperty(String path, boolean defaultValue) { - return new Property<>(PropertyType.BOOLEAN, path, defaultValue); + return new BooleanProperty(path, defaultValue); } public static Property newProperty(String path, int defaultValue) { - return new Property<>(PropertyType.INTEGER, path, defaultValue); + return new IntegerProperty(path, defaultValue); } public static Property newProperty(String path, String defaultValue) { - return new Property<>(PropertyType.STRING, path, defaultValue); + return new StringProperty(path, defaultValue); } - // ----- - // Hooks to the PropertyType methods - // ----- /** * Get the property value from the given configuration – guaranteed to never return null. * * @param configuration The configuration to read the value from * @return The value, or default if not present */ - public T getFromFile(FileConfiguration configuration) { - return type.getFromFile(this, configuration); - } + public abstract T getFromFile(FileConfiguration configuration); /** * Return whether or not the given configuration file contains the property. @@ -99,7 +73,7 @@ public class Property { * @return True if the property is present, false otherwise */ public boolean isPresent(FileConfiguration configuration) { - return type.contains(this, configuration); + return configuration.contains(path); } /** @@ -111,12 +85,9 @@ public class Property { * @return The generated YAML */ public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { - return type.toYaml(getFromFile(configuration), simpleYaml, singleQuoteYaml); + return simpleYaml.dump(getFromFile(configuration)); } - // ----- - // Trivial getters - // ----- /** * Return the default value of the property. * @@ -140,4 +111,89 @@ public class Property { return "Property '" + path + "'"; } + + /** + * Boolean property. + */ + private static final class BooleanProperty extends Property { + + public BooleanProperty(String path, Boolean defaultValue) { + super(path, defaultValue); + } + + @Override + public Boolean getFromFile(FileConfiguration configuration) { + return configuration.getBoolean(getPath(), getDefaultValue()); + } + } + + /** + * Integer property. + */ + private static final class IntegerProperty extends Property { + + public IntegerProperty(String path, Integer defaultValue) { + super(path, defaultValue); + } + + @Override + public Integer getFromFile(FileConfiguration configuration) { + return configuration.getInt(getPath(), getDefaultValue()); + } + } + + /** + * String property. + */ + private static final class StringProperty extends Property { + + public StringProperty(String path, String defaultValue) { + super(path, defaultValue); + } + + @Override + public String getFromFile(FileConfiguration configuration) { + return configuration.getString(getPath(), getDefaultValue()); + } + + @Override + public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { + return singleQuoteYaml.dump(getFromFile(configuration)); + } + } + + /** + * String list property. + */ + private static final class StringListProperty extends Property> { + + public StringListProperty(String path, String[] defaultValues) { + super(path, Arrays.asList(defaultValues)); + } + + @Override + public List getFromFile(FileConfiguration configuration) { + if (!configuration.isList(getPath())) { + return getDefaultValue(); + } + return configuration.getStringList(getPath()); + } + + @Override + public boolean isPresent(FileConfiguration configuration) { + return configuration.isList(getPath()); + } + + @Override + public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { + List value = getFromFile(configuration); + String yaml = singleQuoteYaml.dump(value); + // If the property is a non-empty list we need to append a new line because it will be + // something like the following, which requires a new line: + // - 'item 1' + // - 'second item in list' + return value.isEmpty() ? yaml : "\n" + yaml; + } + } + } diff --git a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java deleted file mode 100644 index 28a505cf..00000000 --- a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java +++ /dev/null @@ -1,116 +0,0 @@ -package fr.xephi.authme.settings.domain; - -import org.bukkit.configuration.file.FileConfiguration; -import org.yaml.snakeyaml.Yaml; - -import java.util.List; - -/** - * Handles a certain property type and provides type-specific functionality. - * - * @param The value of the property - * @see Property - */ -public abstract class PropertyType { - - public static final PropertyType BOOLEAN = new BooleanProperty(); - public static final PropertyType INTEGER = new IntegerProperty(); - public static final PropertyType STRING = new StringProperty(); - public static final PropertyType> STRING_LIST = new StringListProperty(); - - /** - * Get the property's value from the given YAML configuration. - * - * @param property The property to retrieve - * @param configuration The YAML configuration to read from - * @return The read value, or the default value if absent - */ - public abstract T getFromFile(Property property, FileConfiguration configuration); - - /** - * Return whether the property is present in the given configuration. - * - * @param property The property to search for - * @param configuration The configuration to verify - * @return True if the property is present, false otherwise - */ - public boolean contains(Property property, FileConfiguration configuration) { - return configuration.contains(property.getPath()); - } - - /** - * Format the value as YAML. - * - * @param value The value to export - * @param simpleYaml YAML object (default) - * @param singleQuoteYaml YAML object set to use single quotes - * @return The generated YAML - */ - public String toYaml(T value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return simpleYaml.dump(value); - } - - - /** - * Boolean property. - */ - private static final class BooleanProperty extends PropertyType { - @Override - public Boolean getFromFile(Property property, FileConfiguration configuration) { - return configuration.getBoolean(property.getPath(), property.getDefaultValue()); - } - } - - /** - * Integer property. - */ - private static final class IntegerProperty extends PropertyType { - @Override - public Integer getFromFile(Property property, FileConfiguration configuration) { - return configuration.getInt(property.getPath(), property.getDefaultValue()); - } - } - - /** - * String property. - */ - private static final class StringProperty extends PropertyType { - @Override - public String getFromFile(Property property, FileConfiguration configuration) { - return configuration.getString(property.getPath(), property.getDefaultValue()); - } - @Override - public String toYaml(String value, Yaml simpleYaml, Yaml singleQuoteYaml) { - return singleQuoteYaml.dump(value); - } - } - - /** - * String list property. - */ - private static final class StringListProperty extends PropertyType> { - @Override - public List getFromFile(Property> property, FileConfiguration configuration) { - if (!configuration.isList(property.getPath())) { - return property.getDefaultValue(); - } - return configuration.getStringList(property.getPath()); - } - - @Override - public boolean contains(Property> property, FileConfiguration configuration) { - return configuration.contains(property.getPath()) && configuration.isList(property.getPath()); - } - - @Override - public String toYaml(List value, Yaml simpleYaml, Yaml singleQuoteYaml) { - String yaml = singleQuoteYaml.dump(value); - // If the property is a non-empty list we need to append a new line because it will be - // something like the following, which requires a new line: - // - 'item 1' - // - 'second item in list' - return value.isEmpty() ? yaml : "\n" + yaml; - } - } - -} diff --git a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java index 9dc4ad60..35e9cef9 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java @@ -5,26 +5,24 @@ import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.SettingsClass; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.STRING; public class ConverterSettings implements SettingsClass { @Comment("Rakamak file name") public static final Property RAKAMAK_FILE_NAME = - newProperty(STRING, "Converter.Rakamak.fileName", "users.rak"); + newProperty("Converter.Rakamak.fileName", "users.rak"); @Comment("Rakamak use IP?") public static final Property RAKAMAK_USE_IP = - newProperty(BOOLEAN, "Converter.Rakamak.useIP", false); + newProperty("Converter.Rakamak.useIP", false); @Comment("Rakamak IP file name") public static final Property RAKAMAK_IP_FILE_NAME = - newProperty(STRING, "Converter.Rakamak.ipFileName", "UsersIp.rak"); + newProperty("Converter.Rakamak.ipFileName", "UsersIp.rak"); @Comment("CrazyLogin database file name") public static final Property CRAZYLOGIN_FILE_NAME = - newProperty(STRING, "Converter.CrazyLogin.fileName", "accounts.db"); + newProperty("Converter.CrazyLogin.fileName", "accounts.db"); private ConverterSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java index 7b7ee3ce..c6b6959d 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java @@ -6,29 +6,26 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.INTEGER; -import static fr.xephi.authme.settings.domain.PropertyType.STRING; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class EmailSettings implements SettingsClass { @Comment("Email SMTP server host") public static final Property SMTP_HOST = - newProperty(STRING, "Email.mailSMTP", "smtp.gmail.com"); + newProperty("Email.mailSMTP", "smtp.gmail.com"); @Comment("Email SMTP server port") public static final Property SMTP_PORT = - newProperty(INTEGER, "Email.mailPort", 465); + newProperty("Email.mailPort", 465); @Comment("Email account which sends the mails") public static final Property MAIL_ACCOUNT = - newProperty(STRING, "Email.mailAccount", ""); + newProperty("Email.mailAccount", ""); @Comment("Email account password") public static final Property MAIL_PASSWORD = - newProperty(STRING, "Email.mailPassword", ""); + newProperty("Email.mailPassword", ""); @Comment("Custom sender name, replacing the mailAccount name in the email") public static final Property MAIL_SENDER_NAME = @@ -36,39 +33,39 @@ public class EmailSettings implements SettingsClass { @Comment("Recovery password length") public static final Property RECOVERY_PASSWORD_LENGTH = - newProperty(INTEGER, "Email.RecoveryPasswordLength", 8); + newProperty("Email.RecoveryPasswordLength", 8); @Comment("Mail Subject") public static final Property RECOVERY_MAIL_SUBJECT = - newProperty(STRING, "Email.mailSubject", "Your new AuthMe password"); + newProperty("Email.mailSubject", "Your new AuthMe password"); @Comment("Like maxRegPerIP but with email") public static final Property MAX_REG_PER_EMAIL = - newProperty(INTEGER, "Email.maxRegPerEmail", 1); + newProperty("Email.maxRegPerEmail", 1); @Comment("Recall players to add an email?") public static final Property RECALL_PLAYERS = - newProperty(BOOLEAN, "Email.recallPlayers", false); + newProperty("Email.recallPlayers", false); @Comment("Delay in minute for the recall scheduler") public static final Property DELAY_RECALL = - newProperty(INTEGER, "Email.delayRecall", 5); + newProperty("Email.delayRecall", 5); @Comment("Blacklist these domains for emails") public static final Property> DOMAIN_BLACKLIST = - newProperty(STRING_LIST, "Email.emailBlacklisted", "10minutemail.com"); + newListProperty("Email.emailBlacklisted", "10minutemail.com"); @Comment("Whitelist ONLY these domains for emails") public static final Property> DOMAIN_WHITELIST = - newProperty(STRING_LIST, "Email.emailWhitelisted"); + newListProperty("Email.emailWhitelisted"); @Comment("Send the new password drawn in an image?") public static final Property PASSWORD_AS_IMAGE = - newProperty(BOOLEAN, "Email.generateImage", false); + newProperty("Email.generateImage", false); @Comment("The OAuth2 token") public static final Property OAUTH2_TOKEN = - newProperty(STRING, "Email.emailOauth2Token", ""); + newProperty("Email.emailOauth2Token", ""); private EmailSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java index b20d2868..95d91f85 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java @@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.domain.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class HooksSettings implements SettingsClass { @@ -48,7 +48,7 @@ public class HooksSettings implements SettingsClass { @Comment("Other MySQL columns where we need to put the username (case-sensitive)") public static final Property> MYSQL_OTHER_USERNAME_COLS = - newProperty(PropertyType.STRING_LIST, "ExternalBoardOptions.mySQLOtherUsernameColumns"); + newListProperty("ExternalBoardOptions.mySQLOtherUsernameColumns"); @Comment("How much log2 rounds needed in BCrypt (do not change if you do not know what it does)") public static final Property BCRYPT_LOG2_ROUND = diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index f5a51215..a4792f91 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -6,39 +6,37 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.INTEGER; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class ProtectionSettings implements SettingsClass { @Comment("Enable some servers protection (country based login, antibot)") public static final Property ENABLE_PROTECTION = - newProperty(BOOLEAN, "Protection.enableProtection", false); + newProperty("Protection.enableProtection", false); @Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes", "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_WHITELIST = - newProperty(STRING_LIST, "Protection.countries", "US", "GB", "A1"); + newListProperty("Protection.countries", "US", "GB", "A1"); @Comment({"Countries not allowed to join the server and register", "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_BLACKLIST = - newProperty(STRING_LIST, "Protection.countriesBlacklist"); + newListProperty("Protection.countriesBlacklist"); @Comment("Do we need to enable automatic antibot system?") public static final Property ENABLE_ANTIBOT = - newProperty(BOOLEAN, "Protection.enableAntiBot", false); + newProperty("Protection.enableAntiBot", false); - @Comment("Max number of player allowed to login in 5 secs before enable AntiBot system automatically") + @Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically") public static final Property ANTIBOT_SENSIBILITY = - newProperty(INTEGER, "Protection.antiBotSensibility", 5); + newProperty("Protection.antiBotSensibility", 5); @Comment("Duration in minutes of the antibot automatic system") public static final Property ANTIBOT_DURATION = - newProperty(INTEGER, "Protection.antiBotDuration", 10); + newProperty("Protection.antiBotDuration", 10); private ProtectionSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java index b4de9abf..2ec55799 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java @@ -5,43 +5,40 @@ import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.SettingsClass; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.BOOLEAN; -import static fr.xephi.authme.settings.domain.PropertyType.INTEGER; -import static fr.xephi.authme.settings.domain.PropertyType.STRING; public class PurgeSettings implements SettingsClass { @Comment("If enabled, AuthMe automatically purges old, unused accounts") public static final Property USE_AUTO_PURGE = - newProperty(BOOLEAN, "Purge.useAutoPurge", false); + newProperty("Purge.useAutoPurge", false); @Comment("Number of Days an account become Unused") public static final Property DAYS_BEFORE_REMOVE_PLAYER = - newProperty(INTEGER, "Purge.daysBeforeRemovePlayer", 60); + newProperty("Purge.daysBeforeRemovePlayer", 60); @Comment("Do we need to remove the player.dat file during purge process?") public static final Property REMOVE_PLAYER_DAT = - newProperty(BOOLEAN, "Purge.removePlayerDat", false); + newProperty("Purge.removePlayerDat", false); @Comment("Do we need to remove the Essentials/users/player.yml file during purge process?") public static final Property REMOVE_ESSENTIALS_FILES = - newProperty(BOOLEAN, "Purge.removeEssentialsFile", false); + newProperty("Purge.removeEssentialsFile", false); @Comment("World where are players.dat stores") public static final Property DEFAULT_WORLD = - newProperty(STRING, "Purge.defaultWorld", "world"); + newProperty("Purge.defaultWorld", "world"); @Comment("Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?") public static final Property REMOVE_LIMITED_CREATIVE_INVENTORIES = - newProperty(BOOLEAN, "Purge.removeLimitedCreativesInventories", false); + newProperty("Purge.removeLimitedCreativesInventories", false); @Comment("Do we need to remove the AntiXRayData/PlayerData/player file during purge process?") public static final Property REMOVE_ANTI_XRAY_FILE = - newProperty(BOOLEAN, "Purge.removeAntiXRayFile", false); + newProperty("Purge.removeAntiXRayFile", false); @Comment("Do we need to remove permissions?") public static final Property REMOVE_PERMISSIONS = - newProperty(BOOLEAN, "Purge.removePermissions", false); + newProperty("Purge.removePermissions", false); private PurgeSettings() { } 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..c2c2bbe3 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.domain.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class RegistrationSettings implements SettingsClass { @@ -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); @@ -50,21 +50,21 @@ public class RegistrationSettings implements SettingsClass { @Comment("Force these commands after /login, without any '/', use %p to replace with player name") public static final Property> FORCE_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.forceCommands"); + newListProperty("settings.forceCommands"); @Comment("Force these commands after /login as service console, without any '/'. " + "Use %p to replace with player name") public static final Property> FORCE_COMMANDS_AS_CONSOLE = - newProperty(PropertyType.STRING_LIST, "settings.forceCommandsAsConsole"); + newListProperty("settings.forceCommandsAsConsole"); @Comment("Force these commands after /register, without any '/', use %p to replace with player name") public static final Property> FORCE_REGISTER_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommands"); + newListProperty("settings.forceRegisterCommands"); @Comment("Force these commands after /register as a server console, without any '/'. " + "Use %p to replace with player name") public static final Property> FORCE_REGISTER_COMMANDS_AS_CONSOLE = - newProperty(PropertyType.STRING_LIST, "settings.forceRegisterCommandsAsConsole"); + newListProperty("settings.forceRegisterCommandsAsConsole"); @Comment({ "Enable to display the welcome message (welcome.txt) after a registration or a login", 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..c2b2cb68 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -2,11 +2,11 @@ package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.domain.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; public class RestrictionSettings implements SettingsClass { @@ -26,7 +26,7 @@ public class RestrictionSettings implements SettingsClass { @Comment("Allowed commands for unauthenticated players") public static final Property> ALLOW_COMMANDS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands", + newListProperty("settings.restrictions.allowCommands", "login", "register", "l", "reg", "email", "captcha"); @Comment("Max number of allowed registrations per IP") @@ -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); @@ -75,7 +75,7 @@ public class RestrictionSettings implements SettingsClass { " AllowedRestrictedUser:", " - playername;127.0.0.1"}) public static final Property> ALLOWED_RESTRICTED_USERS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.AllowedRestrictedUser"); + newListProperty("settings.restrictions.AllowedRestrictedUser"); @Comment("Should unregistered players be kicked immediately?") public static final Property KICK_NON_REGISTERED = @@ -148,7 +148,7 @@ public class RestrictionSettings implements SettingsClass { "WorldNames where we need to force the spawn location for ForceSpawnLocOnJoinEnabled", "Case-sensitive!"}) public static final Property> FORCE_SPAWN_ON_WORLDS = - newProperty(PropertyType.STRING_LIST, "settings.restrictions.ForceSpawnOnTheseWorlds", + newListProperty("settings.restrictions.ForceSpawnOnTheseWorlds", "world", "world_nether", "world_the_end"); @Comment("Ban ip when the ip is not the ip registered in database") @@ -189,7 +189,7 @@ public class RestrictionSettings implements SettingsClass { "It is case-sensitive!" }) public static final Property> UNRESTRICTED_NAMES = - newProperty(PropertyType.STRING_LIST, "settings.unrestrictions.UnrestrictedName"); + newListProperty("settings.unrestrictions.UnrestrictedName"); private RestrictionSettings() { diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 6a2ace71..9a52ca82 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -7,8 +7,8 @@ import fr.xephi.authme.settings.domain.SettingsClass; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; -import static fr.xephi.authme.settings.domain.PropertyType.STRING_LIST; public class SecuritySettings implements SettingsClass { @@ -98,8 +98,7 @@ public class SecuritySettings implements SettingsClass { "- '123456'", "- 'password'"}) public static final Property> UNSAFE_PASSWORDS = - newProperty(STRING_LIST, "settings.security.unsafePasswords", - "123456", "password", "qwerty", "12345", "54321"); + newListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321"); private SecuritySettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java index 70d8ce23..5586a578 100644 --- a/src/main/java/fr/xephi/authme/settings/propertymap/Node.java +++ b/src/main/java/fr/xephi/authme/settings/propertymap/Node.java @@ -1,5 +1,7 @@ package fr.xephi.authme.settings.propertymap; +import fr.xephi.authme.ConsoleLogger; + import java.util.ArrayList; import java.util.List; @@ -37,14 +39,13 @@ final class Node { } /** - * Add a node to the root, creating any intermediary children that don't exist. + * Add a child node, creating any intermediary children that don't exist. * - * @param root The root to add the path to * @param fullPath The entire path of the node to add, separate by periods */ - public static void addNode(Node root, String fullPath) { + public void addNode(String fullPath) { String[] pathParts = fullPath.split("\\."); - Node parent = root; + Node parent = this; for (String part : pathParts) { Node child = parent.getChild(part); if (child == null) { @@ -59,17 +60,16 @@ final class Node { * Compare two nodes by this class' sorting behavior (insertion order). * Note that this method assumes that both supplied paths exist in the tree. * - * @param root The root of the tree * @param fullPath1 The full path to the first node * @param fullPath2 The full path to the second node * @return The comparison result, in the same format as {@link Comparable#compareTo} */ - public static int compare(Node root, String fullPath1, String fullPath2) { + public int compare(String fullPath1, String fullPath2) { String[] path1 = fullPath1.split("\\."); String[] path2 = fullPath2.split("\\."); int commonCount = 0; - Node commonNode = root; + Node commonNode = this; while (commonCount < path1.length && commonCount < path2.length && path1[commonCount].equals(path2[commonCount]) && commonNode != null) { commonNode = commonNode.getChild(path1[commonCount]); @@ -77,7 +77,7 @@ final class Node { } if (commonNode == null) { - System.err.println("Could not find common node for '" + fullPath1 + "' at index " + commonCount); + ConsoleLogger.showError("Could not find common node for '" + fullPath1 + "' at index " + commonCount); return fullPath1.compareTo(fullPath2); // fallback } else if (commonCount >= path1.length || commonCount >= path2.length) { return Integer.compare(path1.length, path2.length); diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java index 77f5f60e..a7ec1cb3 100644 --- a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java +++ b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java @@ -23,12 +23,12 @@ final class PropertyMapComparator implements Comparator { * @param property The property that is being added */ public void add(Property property) { - Node.addNode(parent, property.getPath()); + parent.addNode(property.getPath()); } @Override public int compare(Property p1, Property p2) { - return Node.compare(parent, p1.getPath(), p2.getPath()); + return parent.compare(p1.getPath(), p2.getPath()); } } 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 92310f37..56d1493d 100644 --- a/src/main/java/fr/xephi/authme/task/MessageTask.java +++ b/src/main/java/fr/xephi/authme/task/MessageTask.java @@ -22,13 +22,13 @@ public class MessageTask implements Runnable { * * @param plugin AuthMe * @param name String - * @param strings String[] + * @param lines String[] * @param interval int */ - public MessageTask(AuthMe plugin, String name, String[] strings, int interval) { + public MessageTask(AuthMe plugin, String name, String[] lines, int interval) { this.plugin = plugin; this.name = name; - this.msg = strings; + this.msg = lines; this.interval = interval; } @@ -49,7 +49,7 @@ public class MessageTask implements Runnable { } BukkitTask nextTask = plugin.getServer().getScheduler().runTaskLater(plugin, this, interval * 20); if (LimboCache.getInstance().hasLimboPlayer(name)) { - LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nextTask); + LimboCache.getInstance().getLimboPlayer(name).setMessageTask(nextTask); } return; } diff --git a/src/main/java/fr/xephi/authme/util/BukkitService.java b/src/main/java/fr/xephi/authme/util/BukkitService.java new file mode 100644 index 00000000..053fe789 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/BukkitService.java @@ -0,0 +1,57 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.AuthMe; +import org.bukkit.Bukkit; + +/** + * Service for operations requiring server entities, such as for scheduling. + */ +public class BukkitService { + + /** Number of ticks per second in the Bukkit main thread. */ + public static final int TICKS_PER_SECOND = 20; + /** Number of ticks per minute. */ + public static final int TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND; + + private final AuthMe authMe; + + public BukkitService(AuthMe authMe) { + this.authMe = authMe; + } + + /** + * Schedules a once off task to occur as soon as possible. + *

+ * This task will be executed by the main server thread. + * + * @param task Task to be executed + * @return Task id number (-1 if scheduling failed) + */ + public int scheduleSyncDelayedTask(Runnable task) { + return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task); + } + + /** + * Schedules a once off task to occur after a delay. + *

+ * This task will be executed by the main server thread. + * + * @param task Task to be executed + * @param delay Delay in server ticks before executing task + * @return Task id number (-1 if scheduling failed) + */ + public int scheduleSyncDelayedTask(Runnable task, long delay) { + return Bukkit.getScheduler().scheduleSyncDelayedTask(authMe, task, delay); + } + + /** + * Broadcast a message to all players. + * + * @param message the message + * @return the number of players + */ + public int broadcastMessage(String message) { + return Bukkit.broadcastMessage(message); + } + +} diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java new file mode 100644 index 00000000..d34c41f8 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -0,0 +1,52 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import static java.lang.String.format; + +/** + * File utilities. + */ +public class FileUtils { + + private FileUtils() { + } + + /** + * Copy a resource file (from the JAR) to the given file if it doesn't exist. + * + * @param destinationFile The file to check and copy to (outside of JAR) + * @param resourcePath Absolute path to the resource file (path to file within JAR) + * @return False if the file does not exist and could not be copied, true otherwise + */ + public static boolean copyFileFromResource(File destinationFile, String resourcePath) { + if (destinationFile.exists()) { + return true; + } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) { + ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'"); + return false; + } + + // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' + final String normalizedPath = resourcePath.replace("\\", "/"); + try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { + if (is == null) { + ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource", + resourcePath, destinationFile.getPath())); + } else { + Files.copy(is, destinationFile.toPath()); + return true; + } + } catch (IOException e) { + ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':", + resourcePath, destinationFile.getPath()), e); + } + return false; + } +} diff --git a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java index f878d85a..d5cba2d7 100644 --- a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java +++ b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java @@ -22,8 +22,11 @@ public class GeoLiteAPI { private static LookupService lookupService; private static Thread downloadTask; + private GeoLiteAPI() { + } + /** - * Download (if absent) the GeoIpLite data file and then try to load it. + * Download (if absent or old) the GeoIpLite data file and then try to load it. * * @return True if the data is available, false otherwise. */ diff --git a/src/main/java/fr/xephi/authme/util/MigrationService.java b/src/main/java/fr/xephi/authme/util/MigrationService.java index 6da4d95e..86f98e4c 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; @@ -29,6 +31,7 @@ public final class MigrationService { * @param dataSource The data source * @param authmeSha256 Instance to the AuthMe SHA256 encryption method implementation */ + @SuppressWarnings("deprecation") public static void changePlainTextToSha256(NewSetting settings, DataSource dataSource, SHA256 authmeSha256) { if (HashAlgorithm.PLAINTEXT == settings.getProperty(SecuritySettings.PASSWORD_HASH)) { @@ -54,16 +57,22 @@ public final class MigrationService { * @param dataSource The data source * @return The converted datasource (SQLite), or null if no migration was necessary */ + @SuppressWarnings("deprecation") public static DataSource convertFlatfileToSqlite(NewSetting settings, DataSource dataSource) { if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) { ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated; it will be changed " + "to SQLite... Connection will be impossible until conversion is done!"); - ForceFlatToSqlite converter = new ForceFlatToSqlite(dataSource, settings); - DataSource result = converter.run(); - if (result == null) { - throw new IllegalStateException("Error during conversion from flatfile to SQLite"); - } else { - return result; + 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..c399f655 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -9,10 +9,10 @@ import fr.xephi.authme.events.AuthMeTeleportEvent; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; - import fr.xephi.authme.settings.properties.EmailSettings; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.entity.Player; @@ -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(); } @@ -242,8 +240,7 @@ public final class Utils { } public static boolean isNPC(Player player) { - return player.hasMetadata("NPC") || plugin.combatTagPlus != null - && plugin.combatTagPlus.getNpcPlayerHelper().isNpc(player); + return player.hasMetadata("NPC") || plugin.getPluginHooks().isNpcInCombatTagPlus(player); } public static void teleportToSpawn(Player player) { @@ -281,8 +278,16 @@ public final class Utils { return false; } - /** - */ + public static String getUUIDorName(OfflinePlayer player) { + String uuidOrName; + try { + uuidOrName = player.getUniqueId().toString(); + } catch (Exception ignore) { + uuidOrName = player.getName(); + } + return uuidOrName; + } + public enum GroupType { UNREGISTERED, REGISTERED, diff --git a/src/main/java/fr/xephi/authme/util/Wrapper.java b/src/main/java/fr/xephi/authme/util/Wrapper.java index 77f65cff..bdd1d5a2 100644 --- a/src/main/java/fr/xephi/authme/util/Wrapper.java +++ b/src/main/java/fr/xephi/authme/util/Wrapper.java @@ -8,7 +8,6 @@ import org.bukkit.Server; import org.bukkit.scheduler.BukkitScheduler; import java.io.File; -import java.util.logging.Logger; /** * Wrapper for the retrieval of common singletons used throughout the application. @@ -48,10 +47,6 @@ public class Wrapper { return getAuthMe().getServer(); } - public Logger getLogger() { - return getAuthMe().getLogger(); - } - public Messages getMessages() { return getAuthMe().getMessages(); } 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/AntiBotTest.java b/src/test/java/fr/xephi/authme/AntiBotTest.java index 25e250fd..4dace294 100644 --- a/src/test/java/fr/xephi/authme/AntiBotTest.java +++ b/src/test/java/fr/xephi/authme/AntiBotTest.java @@ -1,35 +1,212 @@ package fr.xephi.authme; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.util.WrapperMock; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PlayerStatePermission; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.ProtectionSettings; +import fr.xephi.authme.util.BukkitService; +import org.bukkit.entity.Player; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import java.util.List; + +import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; +import static fr.xephi.authme.util.BukkitService.TICKS_PER_SECOND; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** * Test for {@link AntiBot}. */ +@RunWith(MockitoJUnitRunner.class) public class AntiBotTest { - private WrapperMock wrapper; + @Mock + private NewSetting settings; + @Mock + private Messages messages; + @Mock + private PermissionsManager permissionsManager; + @Mock + private BukkitService bukkitService; @Before - public void setUpMocks() { - wrapper = WrapperMock.createInstance(); + public void setDefaultSettingValues() { + given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true); } @Test - public void shouldNotEnableAntiBot() { - // given - Settings.enableAntiBot = false; - - // when - AntiBot.setupAntiBotService(); + public void shouldKeepAntiBotDisabled() { + // given / when + given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(false); + AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService); // then - verify(wrapper.getScheduler(), never()).scheduleSyncDelayedTask(any(AuthMe.class), any(Runnable.class)); + verify(bukkitService, never()).scheduleSyncDelayedTask(any(Runnable.class), anyLong()); + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.DISABLED)); } + + @Test + public void shouldTransitionToListening() { + // given / when + AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService); + TestHelper.runSyncDelayedTaskWithDelay(bukkitService); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING)); + } + + @Test + public void shouldSetStatusToActive() { + // given + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.overrideAntiBotStatus(true); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.ACTIVE)); + } + + @Test + public void shouldSetStatusToListening() { + // given + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.overrideAntiBotStatus(false); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING)); + } + + @Test + public void shouldRemainDisabled() { + // given + given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(false); + AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService); + + // when + antiBot.overrideAntiBotStatus(true); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.DISABLED)); + } + + @Test + public void shouldActivateAntiBot() { + // given + given(messages.retrieve(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)) + .willReturn(new String[]{"Test line #1", "Test line #2"}); + int duration = 300; + given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(duration); + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.activateAntiBot(); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.ACTIVE)); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(bukkitService, times(2)).broadcastMessage(captor.capture()); + assertThat(captor.getAllValues(), contains("Test line #1", "Test line #2")); + long expectedTicks = duration * TICKS_PER_MINUTE; + verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq(expectedTicks)); + } + + @Test + public void shouldDisableAntiBotAfterSetDuration() { + // given + given(messages.retrieve(MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE)).willReturn(new String[0]); + given(messages.retrieve(MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE)) + .willReturn(new String[]{"Disabled...", "Placeholder: %m."}); + given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(4); + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.activateAntiBot(); + TestHelper.runSyncDelayedTaskWithDelay(bukkitService); + + // then + assertThat(antiBot.getAntiBotStatus(), equalTo(AntiBot.AntiBotStatus.LISTENING)); + verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq((long) 4800)); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(bukkitService, times(2)).broadcastMessage(captor.capture()); + assertThat(captor.getAllValues(), contains("Disabled...", "Placeholder: 4.")); + } + + @Test + public void shouldCheckPlayerAndRemoveHimLater() { + // given + Player player = mock(Player.class); + given(player.getName()).willReturn("Plaer"); + given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(false); + given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(10); + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.checkAntiBot(player); + + // then + List playerList = (List) ReflectionTestUtils + .getFieldValue(AntiBot.class, antiBot, "antibotPlayers"); + assertThat(playerList, hasSize(1)); + verify(bukkitService).scheduleSyncDelayedTask(any(Runnable.class), eq((long) 15 * TICKS_PER_SECOND)); + + // Follow-up: Check that player will be removed from list again by running the Runnable + // given (2) + // Add another player to the list + playerList.add("other_player"); + + // when (2) + TestHelper.runSyncDelayedTaskWithDelay(bukkitService); + + // then (2) + assertThat(playerList, contains("other_player")); + } + + @Test + public void shouldNotUpdateListForPlayerWithByPassPermission() { + // given + Player player = mock(Player.class); + given(permissionsManager.hasPermission(player, PlayerStatePermission.BYPASS_ANTIBOT)).willReturn(true); + given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(3); + AntiBot antiBot = createListeningAntiBot(); + + // when + antiBot.checkAntiBot(player); + + // then + List playerList = (List) ReflectionTestUtils.getFieldValue(AntiBot.class, antiBot, "antibotPlayers"); + assertThat(playerList, empty()); + verify(bukkitService, never()).scheduleSyncDelayedTask(any(Runnable.class), anyLong()); + } + + private AntiBot createListeningAntiBot() { + AntiBot antiBot = new AntiBot(settings, messages, permissionsManager, bukkitService); + TestHelper.runSyncDelayedTaskWithDelay(bukkitService); + // Make BukkitService forget about all interactions up to here + reset(bukkitService); + return antiBot; + } + } 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/TestHelper.java b/src/test/java/fr/xephi/authme/TestHelper.java index 06ff06a4..5f1db10d 100644 --- a/src/test/java/fr/xephi/authme/TestHelper.java +++ b/src/test/java/fr/xephi/authme/TestHelper.java @@ -1,10 +1,17 @@ package fr.xephi.authme; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.util.BukkitService; +import org.mockito.ArgumentCaptor; + import java.io.File; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.verify; + /** * AuthMe test utilities. */ @@ -47,4 +54,46 @@ public final class TestHelper { return url; } + /** + * Execute a {@link Runnable} passed to a mock's {@link CommandService#runTaskAsynchronously} method. + * Note that calling this method expects that there be a runnable sent to the method and will fail + * otherwise. + * + * @param service The mock service + */ + public static void runInnerRunnable(CommandService service) { + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); + verify(service).runTaskAsynchronously(captor.capture()); + Runnable runnable = captor.getValue(); + runnable.run(); + } + + /** + * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable)} method. + * Note that calling this method expects that there be a runnable sent to the method and will fail + * otherwise. + * + * @param service The mock service + */ + public static void runSyncDelayedTask(BukkitService service) { + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); + verify(service).scheduleSyncDelayedTask(captor.capture()); + Runnable runnable = captor.getValue(); + runnable.run(); + } + + /** + * Execute a {@link Runnable} passed to a mock's {@link BukkitService#scheduleSyncDelayedTask(Runnable, long)} + * method. Note that calling this method expects that there be a runnable sent to the method and will fail + * otherwise. + * + * @param service The mock service + */ + public static void runSyncDelayedTaskWithDelay(BukkitService service) { + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); + verify(service).scheduleSyncDelayedTask(captor.capture(), anyLong()); + Runnable runnable = captor.getValue(); + runnable.run(); + } + } diff --git a/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java b/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java new file mode 100644 index 00000000..96489155 --- /dev/null +++ b/src/test/java/fr/xephi/authme/cache/CaptchaManagerTest.java @@ -0,0 +1,63 @@ +package fr.xephi.authme.cache; + +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link CaptchaManager}. + */ +public class CaptchaManagerTest { + + @Test + public void shouldAddCounts() { + // given + NewSetting settings = mockSettings(3, 4); + CaptchaManager manager = new CaptchaManager(settings); + String player = "tester"; + + // when + for (int i = 0; i < 2; ++i) { + manager.increaseCount(player); + } + + // then + assertThat(manager.isCaptchaRequired(player), equalTo(false)); + manager.increaseCount(player); + assertThat(manager.isCaptchaRequired(player.toUpperCase()), equalTo(true)); + assertThat(manager.isCaptchaRequired("otherPlayer"), equalTo(false)); + } + + @Test + public void shouldCreateAndCheckCaptcha() { + // given + String player = "Miner"; + NewSetting settings = mockSettings(1, 4); + CaptchaManager manager = new CaptchaManager(settings); + String captchaCode = manager.getCaptchaCode(player); + + // when + boolean badResult = manager.checkCode(player, "wrong_code"); + boolean goodResult = manager.checkCode(player, captchaCode); + + // then + assertThat(captchaCode.length(), equalTo(4)); + assertThat(badResult, equalTo(false)); + assertThat(goodResult, equalTo(true)); + // Supplying correct code should clear the entry, and any code should be valid if no entry is present + assertThat(manager.checkCode(player, "bogus"), equalTo(true)); + } + + + private static NewSetting mockSettings(int maxTries, int captchaLength) { + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA)).willReturn(maxTries); + given(settings.getProperty(SecuritySettings.CAPTCHA_LENGTH)).willReturn(captchaLength); + return settings; + } +} diff --git a/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java b/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java new file mode 100644 index 00000000..50c73d30 --- /dev/null +++ b/src/test/java/fr/xephi/authme/cache/IpAddressManagerTest.java @@ -0,0 +1,64 @@ +package fr.xephi.authme.cache; + +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.HooksSettings; +import org.bukkit.entity.Player; +import org.junit.Test; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link IpAddressManager}. + */ +public class IpAddressManagerTest { + + @Test + public void shouldRetrieveFromCache() { + // given + IpAddressManager ipAddressManager = new IpAddressManager(mockSettings(true)); + ipAddressManager.addCache("Test", "my test IP"); + + // when + String result = ipAddressManager.getPlayerIp(mockPlayer("test", "123.123.123.123")); + + // then + assertThat(result, equalTo("my test IP")); + } + + @Test + public void shouldReturnPlainIp() { + // given + IpAddressManager ipAddressManager = new IpAddressManager(mockSettings(false)); + + // when + String result = ipAddressManager.getPlayerIp(mockPlayer("bobby", "8.8.8.8")); + + // then + assertThat(result, equalTo("8.8.8.8")); + } + + + + private static NewSetting mockSettings(boolean useVeryGames) { + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(HooksSettings.ENABLE_VERYGAMES_IP_CHECK)).willReturn(useVeryGames); + return settings; + } + + private static Player mockPlayer(String name, String ip) { + Player player = mock(Player.class); + given(player.getName()).willReturn(name); + InetAddress inetAddress = mock(InetAddress.class); + given(inetAddress.getHostAddress()).willReturn(ip); + InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 8093); + given(player.getAddress()).willReturn(inetSocketAddress); + return player; + } + +} 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/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index 929fd26a..d34ee633 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -1,23 +1,29 @@ package fr.xephi.authme.command; +import fr.xephi.authme.AntiBot; import fr.xephi.authme.AuthMe; +import fr.xephi.authme.cache.IpAddressManager; import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.hooks.PluginHooks; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.NewSetting; -import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.settings.SpawnLoader; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; -import java.io.File; import java.util.Arrays; import java.util.List; @@ -31,28 +37,37 @@ import static org.mockito.Mockito.verify; /** * Test for {@link CommandService}. */ +@RunWith(MockitoJUnitRunner.class) public class CommandServiceTest { - private AuthMe authMe; - private CommandMapper commandMapper; - private HelpProvider helpProvider; - private Messages messages; - private PasswordSecurity passwordSecurity; private CommandService commandService; + @Mock + private AuthMe authMe; + @Mock + private CommandMapper commandMapper; + @Mock + private HelpProvider helpProvider; + @Mock + private Messages messages; + @Mock + private PasswordSecurity passwordSecurity; + @Mock private PermissionsManager permissionsManager; + @Mock private NewSetting settings; + @Mock + private IpAddressManager ipAddressManager; + @Mock + private PluginHooks pluginHooks; + @Mock + private SpawnLoader spawnLoader; + @Mock + private AntiBot antiBot; @Before public void setUpService() { - authMe = mock(AuthMe.class); - commandMapper = mock(CommandMapper.class); - helpProvider = mock(HelpProvider.class); - messages = mock(Messages.class); - passwordSecurity = mock(PasswordSecurity.class); - permissionsManager = mock(PermissionsManager.class); - settings = mock(NewSetting.class); - commandService = new CommandService( - authMe, commandMapper, helpProvider, messages, passwordSecurity, permissionsManager, settings); + commandService = new CommandService(authMe, commandMapper, helpProvider, messages, passwordSecurity, + permissionsManager, settings, ipAddressManager, pluginHooks, spawnLoader, antiBot); } @Test @@ -187,18 +202,6 @@ public class CommandServiceTest { verify(settings).getProperty(property); } - @Test - public void shouldReloadMessages() { - // given - File file = new File("some/bogus-file.test"); - - // when - commandService.reloadMessages(file); - - // then - verify(messages).reload(file); - } - @Test public void shouldReturnSettings() { // given/when @@ -216,4 +219,13 @@ public class CommandServiceTest { // then assertThat(result, equalTo(authMe)); } + + @Test + public void shouldReturnIpAddressManager() { + // given/when + IpAddressManager ipManager = commandService.getIpAddressManager(); + + // then + assertThat(ipManager, equalTo(ipAddressManager)); + } } diff --git a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java index 0f4dff18..e3970fa6 100644 --- a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java @@ -30,7 +30,7 @@ public class CommandUtilsTest { @Test public void shouldPrintEmptyStringForNoArguments() { // given - List parts = Collections.EMPTY_LIST; + List parts = Collections.emptyList(); // when String str = CommandUtils.labelsToString(parts); diff --git a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java index 3fcc05ca..4afee564 100644 --- a/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/PlayerCommandTest.java @@ -28,7 +28,7 @@ public class PlayerCommandTest { PlayerCommandImpl command = new PlayerCommandImpl(); // when - command.executeCommand(sender, Collections.EMPTY_LIST, mock(CommandService.class)); + command.executeCommand(sender, Collections. emptyList(), mock(CommandService.class)); // then ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); @@ -58,7 +58,7 @@ public class PlayerCommandTest { PlayerCommandWithAlt command = new PlayerCommandWithAlt(); // when - command.executeCommand(sender, Collections.EMPTY_LIST, mock(CommandService.class)); + command.executeCommand(sender, Collections. emptyList(), mock(CommandService.class)); // then ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java index 92bcf4fc..82469635 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/AccountsCommandTest.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import static fr.xephi.authme.TestHelper.runInnerRunnable; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.BDDMockito.given; @@ -46,7 +47,7 @@ public class AccountsCommandTest { public void shouldGetAccountsOfCurrentUser() { // given given(sender.getName()).willReturn("Tester"); - List arguments = Collections.EMPTY_LIST; + List arguments = Collections.emptyList(); given(dataSource.getAuth("tester")).willReturn(authWithIp("123.45.67.89")); given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Arrays.asList("Toaster", "Pester")); @@ -80,7 +81,7 @@ public class AccountsCommandTest { // given List arguments = Collections.singletonList("SomeUser"); given(dataSource.getAuth("someuser")).willReturn(mock(PlayerAuth.class)); - given(dataSource.getAllAuthsByIp(anyString())).willReturn(Collections.EMPTY_LIST); + given(dataSource.getAllAuthsByIp(anyString())).willReturn(Collections. emptyList()); // when command.executeCommand(sender, arguments, service); @@ -114,7 +115,7 @@ public class AccountsCommandTest { public void shouldReturnIpUnknown() { // given List arguments = Collections.singletonList("123.45.67.89"); - given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Collections.EMPTY_LIST); + given(dataSource.getAllAuthsByIp("123.45.67.89")).willReturn(Collections. emptyList()); // when command.executeCommand(sender, arguments, service); @@ -156,13 +157,6 @@ public class AccountsCommandTest { assertThat(messages[1], containsString("Tester, Lester, Taster")); } - private static void runInnerRunnable(CommandService service) { - ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); - verify(service).runTaskAsynchronously(captor.capture()); - Runnable runnable = captor.getValue(); - runnable.run(); - } - private static String[] getMessagesSentToSender(CommandSender sender, int expectedCount) { ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(sender, times(expectedCount)).sendMessage(captor.capture()); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java new file mode 100644 index 00000000..8be1185a --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/AuthMeCommandTest.java @@ -0,0 +1,38 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import org.bukkit.command.CommandSender; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; + +import static org.hamcrest.CoreMatchers.containsString; +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 AuthMeCommand}. + */ +public class AuthMeCommandTest { + + @Test + public void shouldDisplayInformation() { + // given + ExecutableCommand command = new AuthMeCommand(); + CommandSender sender = mock(CommandSender.class); + CommandService service = mock(CommandService.class); + + // when + command.executeCommand(sender, Collections. emptyList(), service); + + // then + ArgumentCaptor messagesCaptor = ArgumentCaptor.forClass(String.class); + verify(sender, times(3)).sendMessage(messagesCaptor.capture()); + assertThat(messagesCaptor.getAllValues().get(1), containsString("/authme help")); + assertThat(messagesCaptor.getAllValues().get(2), containsString("/authme about")); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java new file mode 100644 index 00000000..28a6a3f8 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -0,0 +1,253 @@ +package fr.xephi.authme.command.executable.authme; + +import com.google.common.base.Strings; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.bukkit.command.CommandSender; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Arrays; + +import static fr.xephi.authme.TestHelper.runInnerRunnable; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link ChangePasswordAdminCommand}. + */ +public class ChangePasswordAdminCommandTest { + + private CommandService service; + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void setUpServiceMock() { + service = mock(CommandService.class); + given(service.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).willReturn("[a-zA-Z]+"); + given(service.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).willReturn(3); + given(service.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).willReturn(20); + given(service.getProperty(SecuritySettings.UNSAFE_PASSWORDS)) + .willReturn(Arrays.asList("unsafe", "otherUnsafe")); + } + + @Test + public void shouldRejectPasswordSameAsUsername() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Arrays.asList("bobby", "Bobby"), service); + + // then + verify(service).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR); + verify(service, never()).getDataSource(); + } + + @Test + public void shouldRejectPasswordNotMatchingPattern() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + // service mock returns pattern a-zA-Z -> numbers should not be accepted + String invalidPassword = "invalid1234"; + + // when + command.executeCommand(sender, Arrays.asList("myPlayer123", invalidPassword), service); + + // then + verify(service).send(sender, MessageKey.PASSWORD_MATCH_ERROR); + verify(service, never()).getDataSource(); + } + + @Test + public void shouldRejectTooShortPassword() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Arrays.asList("player", "ab"), service); + + // then + verify(service).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); + verify(service, never()).getDataSource(); + } + + @Test + public void shouldRejectTooLongPassword() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Arrays.asList("player", Strings.repeat("a", 30)), service); + + // then + verify(service).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); + verify(service, never()).getDataSource(); + } + + @Test + public void shouldRejectUnsafePassword() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Arrays.asList("player", "unsafe"), service); + + // then + verify(service).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR); + verify(service, never()).getDataSource(); + } + + @Test + public void shouldRejectCommandForUnknownUser() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + String player = "player"; + + PlayerCache playerCache = mock(PlayerCache.class); + given(playerCache.isAuthenticated(player)).willReturn(false); + given(service.getPlayerCache()).willReturn(playerCache); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(player)).willReturn(null); + given(service.getDataSource()).willReturn(dataSource); + + // when + command.executeCommand(sender, Arrays.asList(player, "password"), service); + runInnerRunnable(service); + + // then + verify(service).send(sender, MessageKey.UNKNOWN_USER); + verify(dataSource, never()).updatePassword(any(PlayerAuth.class)); + } + + @Test + public void shouldUpdatePasswordOfLoggedInUser() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + String player = "my_user12"; + String password = "passPass"; + PlayerAuth auth = mock(PlayerAuth.class); + + PlayerCache playerCache = mock(PlayerCache.class); + given(playerCache.isAuthenticated(player)).willReturn(true); + given(playerCache.getAuth(player)).willReturn(auth); + given(service.getPlayerCache()).willReturn(playerCache); + + PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(service.getPasswordSecurity()).willReturn(passwordSecurity); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.updatePassword(auth)).willReturn(true); + given(service.getDataSource()).willReturn(dataSource); + + // when + command.executeCommand(sender, Arrays.asList(player, password), service); + runInnerRunnable(service); + + // then + verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + verify(passwordSecurity).computeHash(password, player); + verify(auth).setPassword(hashedPassword); + verify(dataSource).updatePassword(auth); + } + + @Test + public void shouldUpdatePasswordOfOfflineUser() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + String player = "my_user12"; + String password = "passPass"; + PlayerAuth auth = mock(PlayerAuth.class); + + PlayerCache playerCache = mock(PlayerCache.class); + given(playerCache.isAuthenticated(player)).willReturn(false); + given(service.getPlayerCache()).willReturn(playerCache); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.isAuthAvailable(player)).willReturn(true); + given(dataSource.getAuth(player)).willReturn(auth); + given(dataSource.updatePassword(auth)).willReturn(true); + given(service.getDataSource()).willReturn(dataSource); + + PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(service.getPasswordSecurity()).willReturn(passwordSecurity); + + // when + command.executeCommand(sender, Arrays.asList(player, password), service); + runInnerRunnable(service); + + // then + verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + verify(passwordSecurity).computeHash(password, player); + verify(auth).setPassword(hashedPassword); + verify(dataSource).updatePassword(auth); + } + + @Test + public void shouldReportWhenSaveFailed() { + // given + ExecutableCommand command = new ChangePasswordAdminCommand(); + CommandSender sender = mock(CommandSender.class); + + String player = "my_user12"; + String password = "passPass"; + PlayerAuth auth = mock(PlayerAuth.class); + + PlayerCache playerCache = mock(PlayerCache.class); + given(playerCache.isAuthenticated(player)).willReturn(true); + given(playerCache.getAuth(player)).willReturn(auth); + given(service.getPlayerCache()).willReturn(playerCache); + + PasswordSecurity passwordSecurity = mock(PasswordSecurity.class); + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(service.getPasswordSecurity()).willReturn(passwordSecurity); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.updatePassword(auth)).willReturn(false); + given(service.getDataSource()).willReturn(dataSource); + + // when + command.executeCommand(sender, Arrays.asList(player, password), service); + runInnerRunnable(service); + + // then + verify(service).send(sender, MessageKey.ERROR); + verify(passwordSecurity).computeHash(password, player); + verify(auth).setPassword(hashedPassword); + verify(dataSource).updatePassword(auth); + } + +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java new file mode 100644 index 00000000..aa4f7b74 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommandTest.java @@ -0,0 +1,65 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.settings.SpawnLoader; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link FirstSpawnCommand}. + */ +public class FirstSpawnCommandTest { + + @Test + public void shouldTeleportToFirstSpawn() { + // given + Location firstSpawn = mock(Location.class); + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.getFirstSpawn()).willReturn(firstSpawn); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + Player player = mock(Player.class); + ExecutableCommand command = new FirstSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(player).teleport(firstSpawn); + verify(spawnLoader, atLeastOnce()).getFirstSpawn(); + } + + @Test + public void shouldHandleMissingFirstSpawn() { + // given + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.getFirstSpawn()).willReturn(null); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + Player player = mock(Player.class); + ExecutableCommand command = new FirstSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(player).sendMessage(captor.capture()); + assertThat(captor.getValue(), containsString("spawn has failed")); + verify(player, never()).teleport(any(Location.class)); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java new file mode 100644 index 00000000..247c43ac --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -0,0 +1,68 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; +import org.bukkit.command.CommandSender; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link GetEmailCommand}. + */ +public class GetEmailCommandTest { + + @Test + public void shouldReportUnknownUser() { + // given + String user = "myTestUser"; + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(user)).willReturn(null); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new GetEmailCommand(); + + // when + command.executeCommand(sender, Collections.singletonList(user), service); + + // then + verify(service).send(sender, MessageKey.UNKNOWN_USER); + } + + @Test + public void shouldReturnEmail() { + // given + String user = "userToView"; + String email = "user.email@example.org"; + PlayerAuth auth = mock(PlayerAuth.class); + given(auth.getEmail()).willReturn(email); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(user)).willReturn(auth); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new GetEmailCommand(); + + // when + command.executeCommand(sender, Collections.singletonList(user), service); + + // then + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(sender).sendMessage(captor.capture()); + assertThat(captor.getValue(), containsString(email)); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java new file mode 100644 index 00000000..96cf8844 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/LastLoginCommandTest.java @@ -0,0 +1,119 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; +import org.bukkit.command.CommandSender; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; +import java.util.Date; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link LastLoginCommand}. + */ +public class LastLoginCommandTest { + + private static final long HOUR_IN_MSEC = 3600 * 1000; + private static final long DAY_IN_MSEC = 24 * HOUR_IN_MSEC; + + @Test + public void shouldRejectNonExistentUser() { + // given + String player = "tester"; + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(player)).willReturn(null); + + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new LastLoginCommand(); + + // when + command.executeCommand(sender, Collections.singletonList(player), service); + + // then + verify(dataSource).getAuth(player); + verify(service).send(sender, MessageKey.USER_NOT_REGISTERED); + } + + @Test + public void shouldDisplayLastLoginOfUser() { + // given + String player = "SomePlayer"; + long lastLogin = System.currentTimeMillis() - + (412 * DAY_IN_MSEC + 10 * HOUR_IN_MSEC - 9000); + PlayerAuth auth = mock(PlayerAuth.class); + given(auth.getLastLogin()).willReturn(lastLogin); + given(auth.getIp()).willReturn("123.45.66.77"); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(player)).willReturn(auth); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new LastLoginCommand(); + + // when + command.executeCommand(sender, Collections.singletonList(player), service); + + // then + verify(dataSource).getAuth(player); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(sender, times(3)).sendMessage(captor.capture()); + String lastLoginString = new Date(lastLogin).toString(); + assertThat(captor.getAllValues().get(0), + allOf(containsString(player), containsString(lastLoginString))); + assertThat(captor.getAllValues().get(1), containsString("412 days 9 hours")); + assertThat(captor.getAllValues().get(2), containsString("123.45.66.77")); + } + + @Test + public void shouldDisplayLastLoginOfCommandSender() { + // given + String name = "CommandSender"; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn(name); + + long lastLogin = System.currentTimeMillis() - + (412 * DAY_IN_MSEC + 10 * HOUR_IN_MSEC - 9000); + PlayerAuth auth = mock(PlayerAuth.class); + given(auth.getLastLogin()).willReturn(lastLogin); + given(auth.getIp()).willReturn("123.45.66.77"); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(name)).willReturn(auth); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + + ExecutableCommand command = new LastLoginCommand(); + + // when + command.executeCommand(sender, Collections. emptyList(), service); + + // then + verify(dataSource).getAuth(name); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(sender, times(3)).sendMessage(captor.capture()); + String lastLoginString = new Date(lastLogin).toString(); + assertThat(captor.getAllValues().get(0), + allOf(containsString(name), containsString(lastLoginString))); + assertThat(captor.getAllValues().get(1), containsString("412 days 9 hours")); + assertThat(captor.getAllValues().get(2), containsString("123.45.66.77")); + } + +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java new file mode 100644 index 00000000..9f207866 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/PurgeLastPositionCommandTest.java @@ -0,0 +1,124 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; +import org.bukkit.command.CommandSender; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link PurgeLastPositionCommand}. + */ +public class PurgeLastPositionCommandTest { + + @Test + public void shouldPurgeLastPosOfUser() { + // given + String player = "_Bobby"; + PlayerAuth auth = mock(PlayerAuth.class); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(player)).willReturn(auth); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new PurgeLastPositionCommand(); + + // when + command.executeCommand(sender, Collections.singletonList(player), service); + + // then + verify(dataSource).getAuth(player); + verifyPositionWasReset(auth); + verify(sender).sendMessage(argThat(containsString("last position location is now reset"))); + } + + @Test + public void shouldPurgePositionOfCommandSender() { + // given + String player = "_Bobby"; + CommandSender sender = mock(CommandSender.class); + given(sender.getName()).willReturn(player); + + PlayerAuth auth = mock(PlayerAuth.class); + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAuth(player)).willReturn(auth); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + ExecutableCommand command = new PurgeLastPositionCommand(); + + // when + command.executeCommand(sender, Collections. emptyList(), service); + + // then + verify(dataSource).getAuth(player); + verifyPositionWasReset(auth); + verify(sender).sendMessage(argThat(containsString("position location is now reset"))); + } + + @Test + public void shouldHandleNonExistentUser() { + // given + DataSource dataSource = mock(DataSource.class); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + ExecutableCommand command = new PurgeLastPositionCommand(); + CommandSender sender = mock(CommandSender.class); + String name = "invalidPlayer"; + + // when + command.executeCommand(sender, Collections.singletonList(name), service); + + // then + verify(dataSource).getAuth(name); + verify(service).send(sender, MessageKey.UNKNOWN_USER); + } + + @Test + public void shouldResetAllLastPositions() { + // given + PlayerAuth auth1 = mock(PlayerAuth.class); + PlayerAuth auth2 = mock(PlayerAuth.class); + PlayerAuth auth3 = mock(PlayerAuth.class); + + DataSource dataSource = mock(DataSource.class); + given(dataSource.getAllAuths()).willReturn(Arrays.asList(auth1, auth2, auth3)); + CommandService service = mock(CommandService.class); + given(service.getDataSource()).willReturn(dataSource); + + ExecutableCommand command = new PurgeLastPositionCommand(); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Collections.singletonList("*"), service); + + // then + verify(dataSource).getAllAuths(); + verifyPositionWasReset(auth1); + verifyPositionWasReset(auth2); + verifyPositionWasReset(auth3); + verify(sender).sendMessage(argThat(containsString("last position locations are now reset"))); + } + + + private static void verifyPositionWasReset(PlayerAuth auth) { + verify(auth).setQuitLocX(0); + verify(auth).setQuitLocY(0); + verify(auth).setQuitLocZ(0); + verify(auth).setWorld("world"); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java new file mode 100644 index 00000000..873108ee --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ReloadCommandTest.java @@ -0,0 +1,65 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.output.MessageKey; +import org.bukkit.command.CommandSender; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Collections; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.matches; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link ReloadCommand}. + */ +public class ReloadCommandTest { + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Test + public void shouldReload() throws Exception { + // given + AuthMe authMe = mock(AuthMe.class); + CommandService service = mock(CommandService.class); + given(service.getAuthMe()).willReturn(authMe); + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new ReloadCommand(); + + // when + command.executeCommand(sender, Collections. emptyList(), service); + + // then + verify(authMe).reload(); + verify(service).send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); + } + + @Test + public void shouldHandleReloadError() throws Exception { + // given + AuthMe authMe = mock(AuthMe.class); + doThrow(IllegalStateException.class).when(authMe).reload(); + CommandService service = mock(CommandService.class); + given(service.getAuthMe()).willReturn(authMe); + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new ReloadCommand(); + + // when + command.executeCommand(sender, Collections. emptyList(), service); + + // then + verify(authMe).reload(); + verify(sender).sendMessage(matches("Error occurred.*")); + verify(authMe).stopOrUnload(); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java new file mode 100644 index 00000000..307831ce --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetFirstSpawnCommandTest.java @@ -0,0 +1,66 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.settings.SpawnLoader; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.Test; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link SetFirstSpawnCommand}. + */ +public class SetFirstSpawnCommandTest { + + @Test + public void shouldSetFirstSpawn() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + given(player.getLocation()).willReturn(location); + + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.setFirstSpawn(location)).willReturn(true); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + + ExecutableCommand command = new SetFirstSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(spawnLoader).setFirstSpawn(location); + verify(player).sendMessage(argThat(containsString("defined new first spawn"))); + } + + @Test + public void shouldHandleError() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + given(player.getLocation()).willReturn(location); + + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.setFirstSpawn(location)).willReturn(false); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + + ExecutableCommand command = new SetFirstSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(spawnLoader).setFirstSpawn(location); + verify(player).sendMessage(argThat(containsString("has failed"))); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java new file mode 100644 index 00000000..11c28d32 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SetSpawnCommandTest.java @@ -0,0 +1,66 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.settings.SpawnLoader; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.Test; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link SetSpawnCommand}. + */ +public class SetSpawnCommandTest { + + @Test + public void shouldSetSpawn() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + given(player.getLocation()).willReturn(location); + + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.setSpawn(location)).willReturn(true); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + + ExecutableCommand command = new SetSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(spawnLoader).setSpawn(location); + verify(player).sendMessage(argThat(containsString("defined new spawn"))); + } + + @Test + public void shouldHandleError() { + // given + Player player = mock(Player.class); + Location location = mock(Location.class); + given(player.getLocation()).willReturn(location); + + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.setSpawn(location)).willReturn(false); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + + ExecutableCommand command = new SetSpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(spawnLoader).setSpawn(location); + verify(player).sendMessage(argThat(containsString("has failed"))); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java new file mode 100644 index 00000000..7b76ad02 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SpawnCommandTest.java @@ -0,0 +1,62 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.settings.SpawnLoader; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.Test; + +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link SpawnCommand}. + */ +public class SpawnCommandTest { + + @Test + public void shouldTeleportToSpawn() { + // given + Location spawn = mock(Location.class); + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.getSpawn()).willReturn(spawn); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + Player player = mock(Player.class); + ExecutableCommand command = new SpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(player).teleport(spawn); + verify(spawnLoader, atLeastOnce()).getSpawn(); + } + + @Test + public void shouldHandleMissingSpawn() { + // given + SpawnLoader spawnLoader = mock(SpawnLoader.class); + given(spawnLoader.getSpawn()).willReturn(null); + CommandService service = mock(CommandService.class); + given(service.getSpawnLoader()).willReturn(spawnLoader); + Player player = mock(Player.class); + ExecutableCommand command = new SpawnCommand(); + + // when + command.executeCommand(player, Collections. emptyList(), service); + + // then + verify(player).sendMessage(argThat(containsString("Spawn has failed"))); + verify(player, never()).teleport(any(Location.class)); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java new file mode 100644 index 00000000..3f9767f9 --- /dev/null +++ b/src/test/java/fr/xephi/authme/command/executable/authme/SwitchAntiBotCommandTest.java @@ -0,0 +1,99 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.AntiBot; +import fr.xephi.authme.command.CommandService; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.command.FoundCommandResult; +import fr.xephi.authme.command.help.HelpProvider; +import org.bukkit.command.CommandSender; +import org.junit.Test; + +import java.util.Collections; + +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.containsString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link SwitchAntiBotCommand}. + */ +public class SwitchAntiBotCommandTest { + + @Test + public void shouldReturnAntiBotState() { + // given + AntiBot antiBot = mock(AntiBot.class); + given(antiBot.getAntiBotStatus()).willReturn(AntiBot.AntiBotStatus.ACTIVE); + CommandService service = mock(CommandService.class); + given(service.getAntiBot()).willReturn(antiBot); + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new SwitchAntiBotCommand(); + + // when + command.executeCommand(sender, Collections.emptyList(), service); + + // then + verify(sender).sendMessage(argThat(containsString("status: ACTIVE"))); + } + + @Test + public void shouldActivateAntiBot() { + // given + AntiBot antiBot = mock(AntiBot.class); + CommandService service = mock(CommandService.class); + given(service.getAntiBot()).willReturn(antiBot); + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new SwitchAntiBotCommand(); + + // when + command.executeCommand(sender, Collections.singletonList("on"), service); + + // then + verify(antiBot).overrideAntiBotStatus(true); + verify(sender).sendMessage(argThat(containsString("enabled"))); + } + + @Test + public void shouldDeactivateAntiBot() { + // given + AntiBot antiBot = mock(AntiBot.class); + CommandService service = mock(CommandService.class); + given(service.getAntiBot()).willReturn(antiBot); + CommandSender sender = mock(CommandSender.class); + ExecutableCommand command = new SwitchAntiBotCommand(); + + // when + command.executeCommand(sender, Collections.singletonList("Off"), service); + + // then + verify(antiBot).overrideAntiBotStatus(false); + verify(sender).sendMessage(argThat(containsString("disabled"))); + } + + @Test + public void shouldShowHelpForUnknownState() { + // given + CommandSender sender = mock(CommandSender.class); + + AntiBot antiBot = mock(AntiBot.class); + FoundCommandResult foundCommandResult = mock(FoundCommandResult.class); + CommandService service = mock(CommandService.class); + given(service.getAntiBot()).willReturn(antiBot); + given(service.mapPartsToCommand(sender, asList("authme", "antibot"))).willReturn(foundCommandResult); + + ExecutableCommand command = new SwitchAntiBotCommand(); + + // when + command.executeCommand(sender, Collections.singletonList("wrong"), service); + + // then + verify(antiBot, never()).overrideAntiBotStatus(anyBoolean()); + verify(sender).sendMessage(argThat(containsString("Invalid"))); + verify(service).outputHelp(sender, foundCommandResult, HelpProvider.SHOW_ARGUMENTS); + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java index 3936e506..213dba4c 100644 --- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java @@ -7,8 +7,6 @@ import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.ChangePasswordTask; -import fr.xephi.authme.util.WrapperMock; -import org.bukkit.Server; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -21,6 +19,7 @@ import java.util.Arrays; import java.util.Collections; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.any; @@ -35,21 +34,17 @@ import static org.mockito.Mockito.when; */ public class ChangePasswordCommandTest { - private WrapperMock wrapperMock; - private PlayerCache cacheMock; private CommandService commandService; @Before public void setUpMocks() { - wrapperMock = WrapperMock.createInstance(); - cacheMock = wrapperMock.getPlayerCache(); commandService = mock(CommandService.class); when(commandService.getProperty(SecuritySettings.MIN_PASSWORD_LENGTH)).thenReturn(2); when(commandService.getProperty(SecuritySettings.MAX_PASSWORD_LENGTH)).thenReturn(50); // Only allow passwords with alphanumerical characters for the test when(commandService.getProperty(RestrictionSettings.ALLOWED_PASSWORD_REGEX)).thenReturn("[a-zA-Z0-9]+"); - when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections.EMPTY_LIST); + when(commandService.getProperty(SecuritySettings.UNSAFE_PASSWORDS)).thenReturn(Collections. emptyList()); } @Test @@ -62,7 +57,9 @@ public class ChangePasswordCommandTest { command.executeCommand(sender, new ArrayList(), commandService); // then - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(sender).sendMessage(captor.capture()); + assertThat(captor.getValue(), containsString("only for players")); } @Test @@ -76,7 +73,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.NOT_LOGGED_IN); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @Test @@ -90,7 +86,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.PASSWORD_MATCH_ERROR); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @@ -105,7 +100,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @Test @@ -120,7 +114,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @Test @@ -135,7 +128,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @Test @@ -151,7 +143,6 @@ public class ChangePasswordCommandTest { // then verify(commandService).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR); - assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false)); } @Test @@ -175,7 +166,9 @@ public class ChangePasswordCommandTest { private Player initPlayerWithName(String name, boolean loggedIn) { Player player = mock(Player.class); when(player.getName()).thenReturn(name); - when(cacheMock.isAuthenticated(name)).thenReturn(loggedIn); + PlayerCache playerCache = mock(PlayerCache.class); + when(playerCache.isAuthenticated(name)).thenReturn(loggedIn); + when(commandService.getPlayerCache()).thenReturn(playerCache); return player; } diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index 45e4d424..6568a773 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -253,7 +253,7 @@ public class HelpProviderTest { public void shouldHandleUnboundFoundCommandResult() { // given FoundCommandResult result = new FoundCommandResult(null, Arrays.asList("authme", "test"), - Collections.EMPTY_LIST, 0.0, FoundResultStatus.UNKNOWN_LABEL); + Collections. emptyList(), 0.0, FoundResultStatus.UNKNOWN_LABEL); // when List lines = helpProvider.printHelp(sender, result, ALL_OPTIONS); @@ -317,7 +317,7 @@ public class HelpProviderTest { * @return The generated FoundCommandResult object */ private static FoundCommandResult newFoundResult(CommandDescription command, List labels) { - return new FoundCommandResult(command, labels, Collections.EMPTY_LIST, 0.0, FoundResultStatus.SUCCESS); + return new FoundCommandResult(command, labels, Collections. emptyList(), 0.0, FoundResultStatus.SUCCESS); } private static String removeColors(String str) { 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 abe1d521..077209f4 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -7,9 +7,9 @@ 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; @@ -129,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 @@ -292,4 +286,18 @@ public abstract class AbstractDataSourceIntegrationTest { 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..69bb0512 --- /dev/null +++ b/src/test/java/fr/xephi/authme/datasource/AbstractResourceClosingTest.java @@ -0,0 +1,322 @@ +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. */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @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/MySqlIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java index 586ca0ad..81e2e122 100644 --- a/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/MySqlIntegrationTest.java @@ -38,6 +38,7 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest { /** * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. */ + @SuppressWarnings({ "unchecked", "rawtypes" }) @BeforeClass public static void initializeSettings() throws IOException, ClassNotFoundException { // Check that we have an H2 driver 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/SQLiteIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java index 715d2ce9..2ef4e15a 100644 --- a/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/SQLiteIntegrationTest.java @@ -37,6 +37,7 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest { /** * Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}. */ + @SuppressWarnings({ "unchecked", "rawtypes" }) @BeforeClass public static void initializeSettings() throws IOException, ClassNotFoundException { // Check that we have an implementation for SQLite 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/hooks/PluginHooksTest.java b/src/test/java/fr/xephi/authme/hooks/PluginHooksTest.java new file mode 100644 index 00000000..be12afce --- /dev/null +++ b/src/test/java/fr/xephi/authme/hooks/PluginHooksTest.java @@ -0,0 +1,167 @@ +package fr.xephi.authme.hooks; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.User; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.ReflectionTestUtils; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link PluginHooks}. + */ +public class PluginHooksTest { + + @BeforeClass + public static void setLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Test + public void shouldHookIntoEssentials() { + // given + PluginManager pluginManager = mock(PluginManager.class); + PluginHooks pluginHooks = new PluginHooks(pluginManager); + setPluginAvailable(pluginManager, "Essentials", Essentials.class); + assertThat(pluginHooks.isEssentialsAvailable(), equalTo(false)); + + // when + pluginHooks.tryHookToEssentials(); + + // then + assertThat(pluginHooks.isEssentialsAvailable(), equalTo(true)); + } + + // Note ljacqu 20160312: Cannot test with Multiverse or CombatTagPlus because their classes are declared final + + @Test + public void shouldHookIntoEssentialsAtInitialization() { + // given + PluginManager pluginManager = mock(PluginManager.class); + setPluginAvailable(pluginManager, "Essentials", Essentials.class); + + // when + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // then + assertThat(pluginHooks.isEssentialsAvailable(), equalTo(true)); + } + + @Test + public void shouldReturnEssentialsDataFolder() { + // given + Essentials ess = mock(Essentials.class); + File essDataFolder = new File("test/data-folder"); + // Need to set the data folder with reflections because getDataFolder() is declared final + ReflectionTestUtils.setField(JavaPlugin.class, ess, "dataFolder", essDataFolder); + + PluginManager pluginManager = mock(PluginManager.class); + setPluginAvailable(pluginManager, "Essentials", ess); + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // when + File dataFolder = pluginHooks.getEssentialsDataFolder(); + + // then + assertThat(dataFolder, equalTo(essDataFolder)); + } + + @Test + public void shouldReturnNullForUnhookedEssentials() { + // given + PluginManager pluginManager = mock(PluginManager.class); + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // when + File result = pluginHooks.getEssentialsDataFolder(); + + // then + assertThat(result, nullValue()); + } + + @Test + public void shouldSetSocialSpyStatus() { + // given + Player player = mock(Player.class); + + Essentials ess = mock(Essentials.class); + User user = mock(User.class); + given(ess.getUser(player)).willReturn(user); + + PluginManager pluginManager = mock(PluginManager.class); + setPluginAvailable(pluginManager, "Essentials", ess); + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // when + pluginHooks.setEssentialsSocialSpyStatus(player, true); + + // then + verify(ess).getUser(player); + verify(user).setSocialSpyEnabled(true); + } + + @Test + public void shouldNotDoAnythingForUnhookedEssentials() { + // given + PluginHooks pluginHooks = new PluginHooks(mock(PluginManager.class)); + + // when/then + pluginHooks.setEssentialsSocialSpyStatus(mock(Player.class), false); + } + + @Test + public void shouldUnhookEssentials() { + // given + PluginManager pluginManager = mock(PluginManager.class); + setPluginAvailable(pluginManager, "Essentials", Essentials.class); + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // when + pluginHooks.unhookEssentials(); + + // then + assertThat(pluginHooks.isEssentialsAvailable(), equalTo(false)); + } + + @Test + public void shouldHandlePluginRetrievalError() { + // given + PluginManager pluginManager = mock(PluginManager.class); + given(pluginManager.isPluginEnabled(anyString())).willReturn(true); + doThrow(IllegalStateException.class).when(pluginManager).getPlugin(anyString()); + + // when + PluginHooks pluginHooks = new PluginHooks(pluginManager); + + // then + assertThat(pluginHooks.isEssentialsAvailable(), equalTo(false)); + assertThat(pluginHooks.isMultiverseAvailable(), equalTo(false)); + assertThat(pluginHooks.isCombatTagPlusAvailable(), equalTo(false)); + } + + private static void setPluginAvailable(PluginManager managerMock, String pluginName, + Class pluginClass) { + setPluginAvailable(managerMock, pluginName, mock(pluginClass)); + } + + private static void setPluginAvailable(PluginManager managerMock, String pluginName, T plugin) { + given(managerMock.isPluginEnabled(pluginName)).willReturn(true); + given(managerMock.getPlugin(pluginName)).willReturn(plugin); + } + +} diff --git a/src/test/java/fr/xephi/authme/output/MessagesFileConsistencyTest.java b/src/test/java/fr/xephi/authme/output/MessagesFileConsistencyTest.java index 807d54bf..607560d6 100644 --- a/src/test/java/fr/xephi/authme/output/MessagesFileConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/output/MessagesFileConsistencyTest.java @@ -7,37 +7,55 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.junit.Test; import java.io.File; +import java.util.ArrayList; +import java.util.List; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; /** * Tests that the project's default language file contains a text for all message keys. + *

+ * Translators can change the file name in {@link #MESSAGES_FILE} to validate their translation. */ public class MessagesFileConsistencyTest { - private static final String DEFAULT_MESSAGES_FILE = "/messages/messages_en.yml"; + private static final String MESSAGES_FILE = "/messages/messages_en.yml"; @Test public void shouldHaveAllMessages() { - File file = TestHelper.getJarFile(DEFAULT_MESSAGES_FILE); + File file = TestHelper.getJarFile(MESSAGES_FILE); FileConfiguration configuration = YamlConfiguration.loadConfiguration(file); + List errors = new ArrayList<>(); for (MessageKey messageKey : MessageKey.values()) { - verifyHasMessage(messageKey, configuration); + validateMessage(messageKey, configuration, errors); + } + + if (!errors.isEmpty()) { + fail("Validation errors in " + MESSAGES_FILE + ":\n- " + + StringUtils.join("\n- ", errors)); } } - private static void verifyHasMessage(MessageKey messageKey, FileConfiguration configuration) { + private static void validateMessage(MessageKey messageKey, FileConfiguration configuration, List errors) { final String key = messageKey.getKey(); final String message = configuration.getString(key); - assertThat("Default messages file should have message for key '" + key + "'", - StringUtils.isEmpty(message), equalTo(false)); - + if (StringUtils.isEmpty(message)) { + errors.add("Messages file should have message for key '" + key + "'"); + return; + } + List missingTags = new ArrayList<>(); for (String tag : messageKey.getTags()) { - assertThat("The message for key '" + key + "' contains the tag '" + tag + "' in the default messages file", - message.contains(tag), equalTo(true)); + if (!message.contains(tag)) { + missingTags.add(tag); + } + } + + if (!missingTags.isEmpty()) { + String pluralS = missingTags.size() > 1 ? "s" : ""; + errors.add(String.format("Message with key '%s' missing tag%s: %s", key, pluralS, + StringUtils.join(", ", missingTags))); } } } diff --git a/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java b/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java new file mode 100644 index 00000000..fc7b74e5 --- /dev/null +++ b/src/test/java/fr/xephi/authme/output/MessagesFileYamlCheckerTest.java @@ -0,0 +1,75 @@ +package fr.xephi.authme.output; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Test; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.junit.Assert.fail; + +/** + * Tests that all YML message files can be loaded. + */ +public class MessagesFileYamlCheckerTest { + + /** Path in the resources folder where the message files are located. */ + private static final String MESSAGES_FOLDER = "/messages/"; + /** Pattern of the message file names. */ + private static final Pattern MESSAGE_FILE_PATTERN = Pattern.compile("messages_\\w+\\.yml"); + /** Message key that is present in all files. Used to make sure that text is returned. */ + private static final MessageKey MESSAGE_KEY = MessageKey.LOGIN_MESSAGE; + + @Test + public void shouldAllBeValidYaml() { + // given + List messageFiles = getMessageFiles(); + + // when + List errors = new ArrayList<>(); + for (File file : messageFiles) { + String error = null; + try { + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); + if (StringUtils.isEmpty(configuration.getString(MESSAGE_KEY.getKey()))) { + error = "Message for '" + MESSAGE_KEY + "' is empty"; + } + } catch (Exception e) { + error = "Could not load file: " + StringUtils.formatException(e); + } + if (!StringUtils.isEmpty(error)) { + errors.add(file.getName() + ": " + error); + } + } + + // then + if (!errors.isEmpty()) { + fail("Errors during verification of message files:\n-" + StringUtils.join("\n-", errors)); + } + } + + + private List getMessageFiles() { + File folder = TestHelper.getJarFile(MESSAGES_FOLDER); + File[] files = folder.listFiles(); + if (files == null) { + throw new IllegalStateException("Could not read folder '" + folder.getName() + "'"); + } + + List messageFiles = new ArrayList<>(); + for (File file : files) { + if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) { + messageFiles.add(file); + } + } + if (messageFiles.isEmpty()) { + throw new IllegalStateException("Error getting message files: list of files is empty"); + } + return messageFiles; + } + +} diff --git a/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java new file mode 100644 index 00000000..da3c2f8d --- /dev/null +++ b/src/test/java/fr/xephi/authme/process/ProcessServiceTest.java @@ -0,0 +1,202 @@ +package fr.xephi.authme.process; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.cache.IpAddressManager; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.SecuritySettings; +import org.bukkit.command.CommandSender; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link ProcessService}. + */ +public class ProcessServiceTest { + + private ProcessService processService; + private Map, Object> mocks; + + @Before + public void setUpService() { + mocks = new HashMap<>(); + processService = new ProcessService(newMock(NewSetting.class), newMock(Messages.class), newMock(AuthMe.class), + newMock(DataSource.class), newMock(IpAddressManager.class), newMock(PasswordSecurity.class), + newMock(PluginHooks.class), newMock(SpawnLoader.class)); + } + + @Test + public void shouldGetProperty() { + // given + NewSetting settings = getMock(NewSetting.class); + given(settings.getProperty(SecuritySettings.CAPTCHA_LENGTH)).willReturn(8); + + // when + int result = processService.getProperty(SecuritySettings.CAPTCHA_LENGTH); + + // then + verify(settings).getProperty(SecuritySettings.CAPTCHA_LENGTH); + assertThat(result, equalTo(8)); + } + + @Test + public void shouldReturnSettings() { + // given/when + NewSetting settings = processService.getSettings(); + + // then + assertThat(settings, equalTo(getMock(NewSetting.class))); + } + + @Test + public void shouldSendMessageToPlayer() { + // given + Messages messages = getMock(Messages.class); + CommandSender sender = mock(CommandSender.class); + MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; + + // when + processService.send(sender, key); + + // then + verify(messages).send(sender, key); + } + + @Test + public void shouldSendMessageWithReplacements() { + // given + Messages messages = getMock(Messages.class); + CommandSender sender = mock(CommandSender.class); + MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; + String[] replacements = new String[]{"test", "toast"}; + + // when + processService.send(sender, key, replacements); + + // then + verify(messages).send(sender, key, replacements); + } + + @Test + public void shouldRetrieveMessage() { + // given + Messages messages = getMock(Messages.class); + MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; + String[] lines = new String[]{"First message line", "second line"}; + given(messages.retrieve(key)).willReturn(lines); + + // when + String[] result = processService.retrieveMessage(key); + + // then + assertThat(result, equalTo(lines)); + verify(messages).retrieve(key); + } + + @Test + public void shouldRetrieveSingleMessage() { + // given + Messages messages = getMock(Messages.class); + MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; + String text = "Test text"; + given(messages.retrieveSingle(key)).willReturn(text); + + // when + String result = processService.retrieveSingleMessage(key); + + // then + assertThat(result, equalTo(text)); + verify(messages).retrieveSingle(key); + } + + @Test + public void shouldReturnAuthMeInstance() { + // given / when + AuthMe authMe = processService.getAuthMe(); + + // then + assertThat(authMe, equalTo(getMock(AuthMe.class))); + } + + @Test + public void shouldReturnPluginHooks() { + // given / when + PluginHooks pluginHooks = processService.getPluginHooks(); + + // then + assertThat(pluginHooks, equalTo(getMock(PluginHooks.class))); + } + + @Test + public void shouldReturnIpAddressManager() { + // given / when + IpAddressManager ipAddressManager = processService.getIpAddressManager(); + + // then + assertThat(ipAddressManager, equalTo(getMock(IpAddressManager.class))); + } + + @Test + public void shouldReturnSpawnLoader() { + // given / when + SpawnLoader spawnLoader = processService.getSpawnLoader(); + + // then + assertThat(spawnLoader, equalTo(getMock(SpawnLoader.class))); + } + + @Test + public void shouldReturnDatasource() { + // given / when + DataSource dataSource = processService.getDataSource(); + + // then + assertThat(dataSource, equalTo(getMock(DataSource.class))); + } + + @Test + public void shouldComputeHash() { + // given + PasswordSecurity passwordSecurity = getMock(PasswordSecurity.class); + String password = "test123"; + String username = "Username"; + HashedPassword hashedPassword = new HashedPassword("hashedResult", "salt12342"); + given(passwordSecurity.computeHash(password, username)).willReturn(hashedPassword); + + // when + HashedPassword result = processService.computeHash(password, username); + + // then + assertThat(result, equalTo(hashedPassword)); + verify(passwordSecurity).computeHash(password, username); + } + + private T newMock(Class clazz) { + T mock = mock(clazz); + mocks.put(clazz, mock); + return mock; + } + + private T getMock(Class clazz) { + Object mock = mocks.get(clazz); + if (mock == null) { + throw new IllegalArgumentException("No mock of type " + clazz); + } + return clazz.cast(mock); + } +} 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/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java index 041b58d2..bddd0122 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java @@ -1,13 +1,12 @@ package fr.xephi.authme.process.email; -import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.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,9 +26,9 @@ import static org.mockito.Mockito.when; public class AsyncChangeEmailTest { private Player player; - private Messages messages; private PlayerCache playerCache; private DataSource dataSource; + private ProcessService service; private NewSetting settings; @BeforeClass @@ -41,9 +40,10 @@ public class AsyncChangeEmailTest { @After public void cleanFields() { player = null; - messages = null; playerCache = null; dataSource = null; + service = null; + settings = null; } @Test @@ -57,12 +57,12 @@ public class AsyncChangeEmailTest { given(dataSource.updateEmail(auth)).willReturn(true); // when - process.process(); + process.run(); // then verify(dataSource).updateEmail(auth); verify(playerCache).updatePlayer(auth); - verify(messages).send(player, MessageKey.EMAIL_CHANGED_SUCCESS); + verify(service).send(player, MessageKey.EMAIL_CHANGED_SUCCESS); } @Test @@ -76,12 +76,12 @@ public class AsyncChangeEmailTest { given(dataSource.updateEmail(auth)).willReturn(false); // when - process.process(); + process.run(); // then verify(dataSource).updateEmail(auth); verify(playerCache, never()).updatePlayer(auth); - verify(messages).send(player, MessageKey.ERROR); + verify(service).send(player, MessageKey.ERROR); } @Test @@ -94,12 +94,12 @@ public class AsyncChangeEmailTest { given(playerCache.getAuth("bobby")).willReturn(auth); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.USAGE_ADD_EMAIL); + verify(service).send(player, MessageKey.USAGE_ADD_EMAIL); } @Test @@ -112,12 +112,12 @@ public class AsyncChangeEmailTest { given(playerCache.getAuth("bobby")).willReturn(auth); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.INVALID_NEW_EMAIL); + verify(service).send(player, MessageKey.INVALID_NEW_EMAIL); } @Test @@ -130,12 +130,12 @@ public class AsyncChangeEmailTest { given(playerCache.getAuth("bobby")).willReturn(auth); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.INVALID_OLD_EMAIL); + verify(service).send(player, MessageKey.INVALID_OLD_EMAIL); } @Test @@ -149,12 +149,12 @@ public class AsyncChangeEmailTest { given(dataSource.isEmailStored("new@example.com")).willReturn(true); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); + verify(service).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR); } @Test @@ -166,12 +166,12 @@ public class AsyncChangeEmailTest { given(dataSource.isAuthAvailable("Bobby")).willReturn(true); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.LOGIN_MESSAGE); + verify(service).send(player, MessageKey.LOGIN_MESSAGE); } @Test @@ -181,15 +181,15 @@ public class AsyncChangeEmailTest { given(player.getName()).willReturn("Bobby"); given(playerCache.isAuthenticated("bobby")).willReturn(false); given(dataSource.isAuthAvailable("Bobby")).willReturn(false); - Settings.emailRegistration = true; + given(service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.REGISTER_EMAIL_MESSAGE); + verify(service).send(player, MessageKey.REGISTER_EMAIL_MESSAGE); } @Test @@ -199,15 +199,15 @@ public class AsyncChangeEmailTest { given(player.getName()).willReturn("Bobby"); given(playerCache.isAuthenticated("bobby")).willReturn(false); given(dataSource.isAuthAvailable("Bobby")).willReturn(false); - Settings.emailRegistration = false; + given(service.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(false); // when - process.process(); + process.run(); // then verify(dataSource, never()).updateEmail(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); - verify(messages).send(player, MessageKey.REGISTER_MESSAGE); + verify(service).send(player, MessageKey.REGISTER_MESSAGE); } private static PlayerAuth authWithMail(String email) { @@ -218,12 +218,11 @@ public class AsyncChangeEmailTest { private AsyncChangeEmail createProcess(String oldEmail, String newEmail) { player = mock(Player.class); - messages = mock(Messages.class); - AuthMe authMe = mock(AuthMe.class); - when(authMe.getMessages()).thenReturn(messages); playerCache = mock(PlayerCache.class); dataSource = mock(DataSource.class); + service = mock(ProcessService.class); settings = mock(NewSetting.class); - return new AsyncChangeEmail(player, authMe, oldEmail, newEmail, dataSource, playerCache, settings); + given(service.getSettings()).willReturn(settings); + return new AsyncChangeEmail(player, oldEmail, newEmail, dataSource, playerCache, service); } } diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index 81fdebea..de223e6f 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.events.PasswordEncryptionEvent; @@ -7,6 +8,8 @@ import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.JOOMLA; import fr.xephi.authme.security.crypts.PHPBB; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.event.Event; import org.bukkit.plugin.PluginManager; @@ -73,7 +76,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.BCRYPT, pluginManager, false); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.BCRYPT, false), pluginManager); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -96,7 +100,8 @@ public class PasswordSecurityTest { PlayerAuth auth = mock(PlayerAuth.class); given(dataSource.getPassword(playerName)).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.CUSTOM, pluginManager, false); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.CUSTOM, false), pluginManager); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -115,7 +120,8 @@ public class PasswordSecurityTest { String clearTextPass = "tables"; given(dataSource.getPassword(playerName)).willReturn(null); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, false); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.MD5, false), pluginManager); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -142,7 +148,8 @@ public class PasswordSecurityTest { given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password); given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false); given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, true); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.MD5, true), pluginManager); // when boolean result = security.comparePassword(clearTextPass, playerName); @@ -166,7 +173,8 @@ public class PasswordSecurityTest { String usernameLowerCase = username.toLowerCase(); HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__"); given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.JOOMLA, true), pluginManager); // when HashedPassword result = security.computeHash(password, username); @@ -187,7 +195,8 @@ public class PasswordSecurityTest { String username = "someone12"; HashedPassword hashedPassword = new HashedPassword("~T!est#Hash", "__someSalt__"); given(method.computeHash(password, username)).willReturn(hashedPassword); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.JOOMLA, true), pluginManager); // when HashedPassword result = security.computeHash(HashAlgorithm.PHPBB, password, username); @@ -209,7 +218,8 @@ public class PasswordSecurityTest { HashedPassword hashedPassword = new HashedPassword("~T!est#Hash"); given(method.computeHash(password, username)).willReturn(hashedPassword); given(method.hasSeparateSalt()).willReturn(true); - PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.XAUTH, pluginManager, false); + PasswordSecurity security = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.XAUTH, false), pluginManager); // when boolean result = security.comparePassword(password, hashedPassword, username); @@ -221,4 +231,28 @@ public class PasswordSecurityTest { verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString()); } + @Test + public void shouldReloadSettings() { + // given + PasswordSecurity passwordSecurity = + new PasswordSecurity(dataSource, mockSettings(HashAlgorithm.BCRYPT, false), pluginManager); + NewSetting updatedSettings = mockSettings(HashAlgorithm.MD5, true); + + // when + passwordSecurity.reload(updatedSettings); + + // then + assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "algorithm"), + equalTo((Object) HashAlgorithm.MD5)); + assertThat(ReflectionTestUtils.getFieldValue(PasswordSecurity.class, passwordSecurity, "supportOldAlgorithm"), + equalTo((Object) Boolean.TRUE)); + } + + private static NewSetting mockSettings(HashAlgorithm algorithm, boolean supportOldPassword) { + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(algorithm); + given(settings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)).willReturn(supportOldPassword); + return settings; + } + } diff --git a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java index 7c9170f3..6a7d7fe1 100644 --- a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java @@ -35,10 +35,10 @@ public class ConfigFileConsistencyTest { // given File configFile = TestHelper.getJarFile(CONFIG_FILE); FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); + SettingsMigrationService migration = new SettingsMigrationService(); // when - boolean result = SettingsMigrationService.containsAllSettings( - configuration, SettingsFieldRetriever.getAllPropertyFields()); + boolean result = migration.containsAllSettings(configuration, SettingsFieldRetriever.getAllPropertyFields()); // then if (!result) { diff --git a/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java index 2cb0197d..81ad2980 100644 --- a/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java @@ -1,27 +1,31 @@ package fr.xephi.authme.settings; import com.google.common.collect.ImmutableMap; -import fr.xephi.authme.ReflectionTestUtils; +import com.google.common.io.Files; +import fr.xephi.authme.ConsoleLoggerTestInitializer; +import fr.xephi.authme.TestHelper; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestEnum; import fr.xephi.authme.settings.propertymap.PropertyMap; -import fr.xephi.authme.util.WrapperMock; import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import java.io.File; -import java.lang.reflect.Field; +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import static fr.xephi.authme.TestHelper.getJarFile; +import static fr.xephi.authme.settings.TestSettingsMigrationServices.checkAllPropertiesPresent; import static fr.xephi.authme.settings.domain.Property.newProperty; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; -import static org.junit.Assume.assumeThat; /** * Integration test for {@link NewSetting}. @@ -35,16 +39,33 @@ public class NewSettingIntegrationTest { /** File name for testing difficult values. */ private static final String DIFFICULT_FILE = "/config-difficult-values.yml"; - private static PropertyMap propertyMap = generatePropertyMap(); + private static PropertyMap propertyMap = TestConfiguration.generatePropertyMap(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File testPluginFolder; + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void setUpTestPluginFolder() throws IOException { + testPluginFolder = temporaryFolder.newFolder(); + } @Test - public void shouldLoadAndReadAllProperties() { + public void shouldLoadAndReadAllProperties() throws IOException { // given - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(getJarFile(COMPLETE_FILE)); - File file = new File("unused"); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(copyFileFromResources(COMPLETE_FILE)); + // Pass another, non-existent file to check if the settings had to be rewritten + File newFile = temporaryFolder.newFile(); // when / then - NewSetting settings = new NewSetting(configuration, file, propertyMap); + NewSetting settings = new NewSetting(configuration, newFile, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "Custom sys name") @@ -61,23 +82,23 @@ public class NewSettingIntegrationTest { assertThat("Property '" + entry.getKey().getPath() + "' has expected value", settings.getProperty(entry.getKey()), equalTo(entry.getValue())); } - assertThat(file.exists(), equalTo(false)); + assertThat(newFile.length(), equalTo(0L)); } @Test public void shouldWriteMissingProperties() { // given/when - File file = getJarFile(INCOMPLETE_FILE); + File file = copyFileFromResources(INCOMPLETE_FILE); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); - assumeThat(configuration.contains(TestConfiguration.BORING_COLORS.getPath()), equalTo(false)); // Expectation: File is rewritten to since it does not have all configurations - new NewSetting(configuration, file, propertyMap); + new NewSetting(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent()); // Load the settings again -> checks that what we wrote can be loaded again configuration = YamlConfiguration.loadConfiguration(file); // then - NewSetting settings = new NewSetting(configuration, file, propertyMap); + NewSetting settings = new NewSetting(configuration, file, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]") @@ -100,29 +121,28 @@ public class NewSettingIntegrationTest { @Test public void shouldProperlyExportAnyValues() { // given - File file = getJarFile(DIFFICULT_FILE); + File file = copyFileFromResources(DIFFICULT_FILE); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); - assumeThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(false)); // Additional string properties List> additionalProperties = Arrays.asList( newProperty("more.string1", "it's a text with some \\'apostrophes'"), newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test") ); - PropertyMap propertyMap = generatePropertyMap(); for (Property property : additionalProperties) { propertyMap.put(property, new String[0]); } // when - new NewSetting(configuration, file, propertyMap); - // reload the file as settings should hav been rewritten + new NewSetting(configuration, file, testPluginFolder, propertyMap, checkAllPropertiesPresent()); + // reload the file as settings should have been rewritten configuration = YamlConfiguration.loadConfiguration(file); // then // assert that we won't rewrite the settings again! One rewrite should produce a valid, complete configuration File unusedFile = new File("config-difficult-values.unused.yml"); - NewSetting settings = new NewSetting(configuration, unusedFile, propertyMap); + NewSetting settings = new NewSetting(configuration, unusedFile, testPluginFolder, propertyMap, + checkAllPropertiesPresent()); assertThat(unusedFile.exists(), equalTo(false)); assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true)); @@ -147,23 +167,32 @@ public class NewSettingIntegrationTest { } } - /** - * Generate a property map with all properties in {@link TestConfiguration}. - * - * @return The generated property map - */ - private static PropertyMap generatePropertyMap() { - WrapperMock.createInstance(); - PropertyMap propertyMap = new PropertyMap(); - for (Field field : TestConfiguration.class.getDeclaredFields()) { - Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); - if (fieldValue instanceof Property) { - Property property = (Property) fieldValue; - String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; - propertyMap.put(property, comments); - } + @Test + public void shouldReloadSettings() throws IOException { + // given + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(temporaryFolder.newFile()); + File fullConfigFile = copyFileFromResources(COMPLETE_FILE); + NewSetting settings = new NewSetting(configuration, fullConfigFile, testPluginFolder, propertyMap, + TestSettingsMigrationServices.alwaysFulfilled()); + + // when + assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), + equalTo(TestConfiguration.RATIO_ORDER.getDefaultValue())); + settings.reload(); + + // then + assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.FIRST)); + } + + private File copyFileFromResources(String path) { + try { + File source = TestHelper.getJarFile(path); + File destination = temporaryFolder.newFile(); + Files.copy(source, destination); + return destination; + } catch (IOException e) { + throw new IllegalStateException("Could not copy test file", e); } - return propertyMap; } } diff --git a/src/test/java/fr/xephi/authme/settings/NewSettingTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java index 710698e5..0c69919c 100644 --- a/src/test/java/fr/xephi/authme/settings/NewSettingTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java @@ -1,17 +1,30 @@ package fr.xephi.authme.settings; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.TestConfiguration; import fr.xephi.authme.settings.properties.TestEnum; import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.util.List; +import static fr.xephi.authme.settings.properties.PluginSettings.MESSAGES_LANGUAGE; +import static fr.xephi.authme.util.StringUtils.makePath; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.mockito.BDDMockito.given; @@ -21,13 +34,28 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** - * Test for {@link NewSetting}. + * Unit tests for {@link NewSetting}. */ public class NewSettingTest { + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File testPluginFolder; + + @BeforeClass + public static void setUpLogger() { + ConsoleLoggerTestInitializer.setupLogger(); + } + + @Before + public void setUpTestPluginFolder() throws IOException { + testPluginFolder = temporaryFolder.newFolder(); + } + @Test public void shouldLoadAllConfigs() { // given @@ -43,7 +71,7 @@ public class NewSettingTest { setReturnValue(configuration, TestConfiguration.SYSTEM_NAME, "myTestSys"); // when / then - NewSetting settings = new NewSetting(configuration, null, null); + NewSetting settings = new NewSetting(configuration, null, null, null, null); assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20)); assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true)); @@ -59,7 +87,7 @@ public class NewSettingTest { public void shouldReturnDefaultFile() throws IOException { // given YamlConfiguration configuration = mock(YamlConfiguration.class); - NewSetting settings = new NewSetting(configuration, null, null); + NewSetting settings = new NewSetting(configuration, null, null, null, null); // when String defaultFile = settings.getDefaultMessagesFile(); @@ -71,6 +99,99 @@ public class NewSettingTest { assertThat(stream.read(), not(equalTo(0))); } + @Test + public void shouldSetProperty() { + // given + YamlConfiguration configuration = mock(YamlConfiguration.class); + NewSetting settings = new NewSetting(configuration, null, null, null, null); + + // when + settings.setProperty(TestConfiguration.DUST_LEVEL, -4); + + // then + verify(configuration).set(TestConfiguration.DUST_LEVEL.getPath(), -4); + } + + @Test + public void shouldReturnMessagesFile() { + // given + // Use some code that is for sure not present in our JAR + String languageCode = "notinjar"; + File file = new File(testPluginFolder, makePath("messages", "messages_" + languageCode + ".yml")); + createFile(file); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.contains(anyString())).willReturn(true); + setReturnValue(configuration, MESSAGES_LANGUAGE, languageCode); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + File messagesFile = settings.getMessagesFile(); + + // then + assertThat(messagesFile.getPath(), endsWith("messages_" + languageCode + ".yml")); + assertThat(messagesFile.exists(), equalTo(true)); + } + + @Test + public void shouldCopyDefaultForUnknownLanguageCode() { + // given + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.contains(anyString())).willReturn(true); + setReturnValue(configuration, MESSAGES_LANGUAGE, "doesntexist"); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + File messagesFile = settings.getMessagesFile(); + + // then + assertThat(messagesFile.getPath(), endsWith("messages_en.yml")); + assertThat(messagesFile.exists(), equalTo(true)); + } + + @Test + public void shouldLoadWelcomeMessage() throws IOException { + // given + String welcomeMessage = "This is my welcome message for testing\nBye!"; + File welcomeFile = new File(testPluginFolder, "welcome.txt"); + createFile(welcomeFile); + Files.write(welcomeFile.toPath(), welcomeMessage.getBytes()); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + setReturnValue(configuration, RegistrationSettings.USE_WELCOME_MESSAGE, true); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + List result = settings.getWelcomeMessage(); + + // then + assertThat(result, hasSize(2)); + assertThat(result.get(0), equalTo(welcomeMessage.split("\\n")[0])); + assertThat(result.get(1), equalTo(welcomeMessage.split("\\n")[1])); + } + + @Test + public void shouldLoadEmailMessage() throws IOException { + // given + String emailMessage = "Sample email message\nThat's all!"; + File emailFile = new File(testPluginFolder, "email.html"); + createFile(emailFile); + Files.write(emailFile.toPath(), emailMessage.getBytes()); + + YamlConfiguration configuration = mock(YamlConfiguration.class); + NewSetting settings = new NewSetting(configuration, null, testPluginFolder, + TestConfiguration.generatePropertyMap(), TestSettingsMigrationServices.alwaysFulfilled()); + + // when + String result = settings.getEmailMessage(); + + // then + assertThat(result, equalTo(emailMessage)); + } + private static void setReturnValue(YamlConfiguration config, Property property, T value) { if (value instanceof String) { when(config.getString(eq(property.getPath()), anyString())).thenReturn((String) value); @@ -91,4 +212,13 @@ public class NewSettingTest { setting.getProperty(property).equals(property.getDefaultValue()), equalTo(true)); } + private static void createFile(File file) { + try { + file.getParentFile().mkdirs(); + file.createNewFile(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java index cc89a463..a85f86e1 100644 --- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java +++ b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java @@ -42,9 +42,10 @@ public class SettingsMigrationServiceTest { FileConfiguration configuration = YamlConfiguration.loadConfiguration(configTestFile); PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); assumeThat(testFolder.listFiles(), arrayWithSize(1)); + SettingsMigrationService migrationService = new SettingsMigrationService(); // when - boolean result = SettingsMigrationService.checkAndMigrate(configuration, propertyMap, testFolder); + boolean result = migrationService.checkAndMigrate(configuration, propertyMap, testFolder); // then assertThat(result, equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java new file mode 100644 index 00000000..8bd9da4b --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/SpawnLoaderTest.java @@ -0,0 +1,68 @@ +package fr.xephi.authme.settings; + +import com.google.common.io.Files; +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; +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 static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link SpawnLoader}. + */ +public class SpawnLoaderTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private File testFolder; + private NewSetting settings; + + @Before + public void setup() throws IOException { + // Copy test config into a new temporary folder + testFolder = temporaryFolder.newFolder(); + File source = TestHelper.getJarFile("/spawn/spawn-firstspawn.yml"); + File destination = new File(testFolder, "spawn.yml"); + Files.copy(source, destination); + + // Create a settings mock with default values + settings = mock(NewSetting.class); + given(settings.getProperty(RestrictionSettings.SPAWN_PRIORITY)) + .willReturn("authme, essentials, multiverse, default"); + } + + @Test + public void shouldSetSpawn() { + // given + SpawnLoader spawnLoader = new SpawnLoader(testFolder, settings, mock(PluginHooks.class)); + World world = mock(World.class); + given(world.getName()).willReturn("new_world"); + Location newSpawn = new Location(world, 123, 45.0, -67.89); + + // when + boolean result = spawnLoader.setSpawn(newSpawn); + + // then + assertThat(result, equalTo(true)); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(new File(testFolder, "spawn.yml")); + assertThat(configuration.getDouble("spawn.x"), equalTo(123.0)); + assertThat(configuration.getDouble("spawn.y"), equalTo(45.0)); + assertThat(configuration.getDouble("spawn.z"), equalTo(-67.89)); + assertThat(configuration.getString("spawn.world"), equalTo("new_world")); + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java b/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java new file mode 100644 index 00000000..b3c7dd08 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/TestSettingsMigrationServices.java @@ -0,0 +1,49 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.settings.propertymap.PropertyMap; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; + +/** + * Provides {@link SettingsMigrationService} implementations for testing. + */ +public final class TestSettingsMigrationServices { + + private TestSettingsMigrationServices() { + } + + /** + * Returns a settings migration service which always answers that all data is up-to-date. + * + * @return test settings migration service + */ + public static SettingsMigrationService alwaysFulfilled() { + return new SettingsMigrationService() { + @Override + public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + return false; + } + @Override + public boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { + return true; + } + }; + } + + /** + * Returns a simple settings migration service which is fulfilled if all properties are present. + * + * @return test settings migration service + */ + public static SettingsMigrationService checkAllPropertiesPresent() { + return new SettingsMigrationService() { + // See parent javadoc: true = some migration had to be done, false = config file is up-to-date + @Override + public boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + return !super.containsAllSettings(configuration, propertyMap); + } + }; + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java similarity index 75% rename from src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java rename to src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java index 461314e8..5fe55362 100644 --- a/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java +++ b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTest.java @@ -9,20 +9,19 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Test for {@link EnumPropertyType}. + * Test for {@link EnumProperty}. */ -public class EnumPropertyTypeTest { +public class EnumPropertyTest { @Test public void shouldReturnCorrectEnumValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.getString(property.getPath())).willReturn("Entry_B"); // when - TestEnum result = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_B)); @@ -31,13 +30,12 @@ public class EnumPropertyTypeTest { @Test public void shouldFallBackToDefaultForInvalidValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.getString(property.getPath())).willReturn("Bogus"); // when - TestEnum result = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_C)); @@ -46,13 +44,12 @@ public class EnumPropertyTypeTest { @Test public void shouldFallBackToDefaultForNonExistentValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.getString(property.getPath())).willReturn(null); // when - TestEnum result = propertyType.getFromFile(property, configuration); + TestEnum result = property.getFromFile(configuration); // then assertThat(result, equalTo(TestEnum.ENTRY_C)); @@ -61,14 +58,13 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnTrueForContainsCheck() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.contains(property.getPath())).willReturn(true); given(configuration.getString(property.getPath())).willReturn("ENTRY_B"); // when - boolean result = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(true)); @@ -77,13 +73,12 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnFalseForFileWithoutConfig() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.contains(property.getPath())).willReturn(false); // when - boolean result = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(false)); @@ -92,14 +87,13 @@ public class EnumPropertyTypeTest { @Test public void shouldReturnFalseForUnknownValue() { // given - PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); Property property = Property.newProperty(TestEnum.class, "my.test.path", TestEnum.ENTRY_C); YamlConfiguration configuration = mock(YamlConfiguration.class); given(configuration.contains(property.getPath())).willReturn(true); given(configuration.getString(property.getPath())).willReturn("wrong value"); // when - boolean result = propertyType.contains(property, configuration); + boolean result = property.isPresent(configuration); // then assertThat(result, equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java similarity index 82% rename from src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java rename to src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java index df012063..de33c53c 100644 --- a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java +++ b/src/test/java/fr/xephi/authme/settings/domain/PropertyTest.java @@ -3,8 +3,7 @@ package fr.xephi.authme.settings.domain; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.BeforeClass; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; +import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import java.util.Arrays; import java.util.List; @@ -20,9 +19,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Test for {@link PropertyType} and the contained subtypes. + * Test for {@link Property} and the contained subtypes. */ -public class PropertyTypeTest { +public class PropertyTest { private static YamlConfiguration configuration; @@ -31,11 +30,11 @@ public class PropertyTypeTest { configuration = mock(YamlConfiguration.class); when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true); - when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(secondParameter()); + when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(new ReturnsArgumentAt(1)); when(configuration.getInt(eq("int.path.test"), anyInt())).thenReturn(27); - when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(secondParameter()); + when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(new ReturnsArgumentAt(1)); when(configuration.getString(eq("str.path.test"), anyString())).thenReturn("Test value"); - when(configuration.getString(eq("str.path.wrong"), anyString())).thenAnswer(secondParameter()); + when(configuration.getString(eq("str.path.wrong"), anyString())).thenAnswer(new ReturnsArgumentAt(1)); when(configuration.isList("list.path.test")).thenReturn(true); when(configuration.getStringList("list.path.test")).thenReturn(Arrays.asList("test1", "Test2", "3rd test")); when(configuration.isList("list.path.wrong")).thenReturn(false); @@ -120,7 +119,7 @@ public class PropertyTypeTest { @Test public void shouldGetStringListValue() { // given - Property> property = Property.newProperty(PropertyType.STRING_LIST, "list.path.test", "1", "b"); + Property> property = Property.newListProperty("list.path.test", "1", "b"); // when List result = property.getFromFile(configuration); @@ -133,7 +132,7 @@ public class PropertyTypeTest { public void shouldGetStringListDefault() { // given Property> property = - Property.newProperty(PropertyType.STRING_LIST, "list.path.wrong", "default", "list", "elements"); + Property.newListProperty("list.path.wrong", "default", "list", "elements"); // when List result = property.getFromFile(configuration); @@ -142,13 +141,4 @@ public class PropertyTypeTest { assertThat(result, contains("default", "list", "elements")); } - private static Answer secondParameter() { - return new Answer() { - @Override - public T answer(InvocationOnMock invocation) throws Throwable { - // Return the second parameter -> the default - return (T) invocation.getArguments()[1]; - } - }; - } } diff --git a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java index 07510334..c6b00383 100644 --- a/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; @@ -92,6 +93,17 @@ public class SettingsClassConsistencyTest { constructors, arrayWithSize(1)); assertThat("Constructor of " + clazz + " is private", Modifier.isPrivate(constructors[0].getModifiers()), equalTo(true)); + + // Ugly hack to get coverage on the private constructors + // http://stackoverflow.com/questions/14077842/how-to-test-a-private-constructor-in-java-application + try { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + constructor.newInstance(); + } catch (NoSuchMethodException | InstantiationException + | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } } } @@ -100,6 +112,7 @@ public class SettingsClassConsistencyTest { return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); } + @SuppressWarnings("unchecked") private static Class getSettingsClassFromFile(File file) { String fileName = file.getPath(); String className = fileName diff --git a/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java index 72960f6c..0f16e1e8 100644 --- a/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java +++ b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java @@ -1,11 +1,14 @@ package fr.xephi.authme.settings.properties; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.domain.PropertyType; import fr.xephi.authme.settings.domain.SettingsClass; +import fr.xephi.authme.settings.propertymap.PropertyMap; +import java.lang.reflect.Field; import java.util.List; +import static fr.xephi.authme.settings.domain.Property.newListProperty; import static fr.xephi.authme.settings.domain.Property.newProperty; /** @@ -23,7 +26,7 @@ public final class TestConfiguration implements SettingsClass { newProperty(TestEnum.class, "sample.ratio.order", TestEnum.SECOND); public static final Property> RATIO_FIELDS = - newProperty(PropertyType.STRING_LIST, "sample.ratio.fields", "a", "b", "c"); + newListProperty("sample.ratio.fields", "a", "b", "c"); public static final Property VERSION_NUMBER = newProperty("version", 32046); @@ -32,7 +35,7 @@ public final class TestConfiguration implements SettingsClass { newProperty("features.boring.skip", false); public static final Property> BORING_COLORS = - newProperty(PropertyType.STRING_LIST, "features.boring.colors"); + newListProperty("features.boring.colors"); public static final Property DUST_LEVEL = newProperty("features.boring.dustLevel", -1); @@ -41,10 +44,28 @@ public final class TestConfiguration implements SettingsClass { newProperty("features.cool.enabled", false); public static final Property> COOL_OPTIONS = - newProperty(PropertyType.STRING_LIST, "features.cool.options", "Sparks", "Sprinkles"); + newListProperty("features.cool.options", "Sparks", "Sprinkles"); private TestConfiguration() { } + /** + * Generate a property map with all properties in {@link TestConfiguration}. + * + * @return The generated property map + */ + public static PropertyMap generatePropertyMap() { + PropertyMap propertyMap = new PropertyMap(); + for (Field field : TestConfiguration.class.getDeclaredFields()) { + Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); + if (fieldValue instanceof Property) { + Property property = (Property) fieldValue; + String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; + propertyMap.put(property, comments); + } + } + return propertyMap; + } + } diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/src/test/java/fr/xephi/authme/util/UtilsTest.java index 3ed7ee62..639946f6 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -110,8 +110,8 @@ public class UtilsTest { public void shouldAcceptEmailWithEmptyLists() { // given NewSetting settings = mock(NewSetting.class); - given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); - given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections. emptyList()); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections. emptyList()); // when boolean result = Utils.isEmailCorrect("test@example.org", settings); @@ -126,7 +126,7 @@ public class UtilsTest { NewSetting settings = mock(NewSetting.class); given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)) .willReturn(Arrays.asList("domain.tld", "example.com")); - given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections. emptyList()); // when boolean result = Utils.isEmailCorrect("TesT@Example.com", settings); @@ -141,7 +141,7 @@ public class UtilsTest { NewSetting settings = mock(NewSetting.class); given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)) .willReturn(Arrays.asList("domain.tld", "example.com")); - given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections. emptyList()); // when boolean result = Utils.isEmailCorrect("email@other-domain.abc", settings); @@ -154,7 +154,7 @@ public class UtilsTest { public void shouldAcceptEmailNotInBlacklist() { // given NewSetting settings = mock(NewSetting.class); - given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections. emptyList()); given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)) .willReturn(Arrays.asList("Example.org", "a-test-name.tld")); @@ -169,7 +169,7 @@ public class UtilsTest { public void shouldRejectEmailInBlacklist() { // given NewSetting settings = mock(NewSetting.class); - given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections. emptyList()); given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)) .willReturn(Arrays.asList("Example.org", "a-test-name.tld")); diff --git a/src/test/java/fr/xephi/authme/util/WrapperMock.java b/src/test/java/fr/xephi/authme/util/WrapperMock.java index 057f38bd..fbf880ba 100644 --- a/src/test/java/fr/xephi/authme/util/WrapperMock.java +++ b/src/test/java/fr/xephi/authme/util/WrapperMock.java @@ -10,7 +10,6 @@ import org.mockito.Mockito; import java.io.File; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; /** * Class returning mocks for all calls in {@link Wrapper}. @@ -36,11 +35,6 @@ public class WrapperMock extends Wrapper { return instance; } - @Override - public Logger getLogger() { - return getMock(Logger.class); - } - @Override public Server getServer() { return getMock(Server.class); 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 diff --git a/src/test/resources/spawn/spawn-firstspawn.yml b/src/test/resources/spawn/spawn-firstspawn.yml new file mode 100644 index 00000000..dbb7bd89 --- /dev/null +++ b/src/test/resources/spawn/spawn-firstspawn.yml @@ -0,0 +1,15 @@ +# Sample spawn.yml file with both spawn and firstspawn defined +spawn: + world: 'world' + x: 300.12 + y: 120.34 + z: -89.12 + yaw: 0.23 + pitch: 112.25 +firstspawn: + world: 'firstspawn' + x: -30.12 + y: 204.43 + z: 10.32 + yaw: 0.00 + pitch: 10.23