diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 952ed923a..9d8af1449 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -1,20 +1,44 @@ package fr.xephi.authme; -import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; -import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; - -import java.io.File; -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.apache.logging.log4j.LogManager; -import org.bukkit.Bukkit; +import ch.jalu.injector.Injector; +import ch.jalu.injector.InjectorBuilder; +import com.google.common.annotations.VisibleForTesting; +import fr.xephi.authme.api.API; +import fr.xephi.authme.api.NewAPI; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.cache.auth.PlayerCache; +import fr.xephi.authme.cache.backup.PlayerDataStorage; +import fr.xephi.authme.cache.limbo.LimboCache; +import fr.xephi.authme.command.CommandHandler; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.hooks.PluginHooks; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.initialization.Initializer; +import fr.xephi.authme.initialization.MetricsManager; +import fr.xephi.authme.listener.BlockListener; +import fr.xephi.authme.listener.EntityListener; +import fr.xephi.authme.listener.PlayerListener; +import fr.xephi.authme.listener.PlayerListener16; +import fr.xephi.authme.listener.PlayerListener18; +import fr.xephi.authme.listener.PlayerListener19; +import fr.xephi.authme.listener.ServerListener; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.permission.PermissionsSystemType; +import fr.xephi.authme.process.Management; +import fr.xephi.authme.security.crypts.SHA256; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SpawnLoader; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.task.CleanupTask; +import fr.xephi.authme.task.purge.PurgeService; +import fr.xephi.authme.util.BukkitService; +import fr.xephi.authme.util.GeoLiteAPI; +import fr.xephi.authme.util.MigrationService; +import fr.xephi.authme.util.Utils; +import fr.xephi.authme.util.ValidationService; import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.command.Command; @@ -27,71 +51,25 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitWorker; -import com.google.common.annotations.VisibleForTesting; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; -import ch.jalu.injector.Injector; -import ch.jalu.injector.InjectorBuilder; -import fr.xephi.authme.api.API; -import fr.xephi.authme.api.NewAPI; -import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.cache.auth.PlayerCache; -import fr.xephi.authme.cache.backup.PlayerDataStorage; -import fr.xephi.authme.cache.limbo.LimboCache; -import fr.xephi.authme.command.CommandHandler; -import fr.xephi.authme.datasource.CacheDataSource; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.datasource.DataSourceType; -import fr.xephi.authme.datasource.FlatFile; -import fr.xephi.authme.datasource.MySQL; -import fr.xephi.authme.datasource.SQLite; -import fr.xephi.authme.hooks.PluginHooks; -import fr.xephi.authme.initialization.DataFolder; -import fr.xephi.authme.initialization.MetricsManager; -import fr.xephi.authme.listener.BlockListener; -import fr.xephi.authme.listener.EntityListener; -import fr.xephi.authme.listener.PlayerListener; -import fr.xephi.authme.listener.PlayerListener16; -import fr.xephi.authme.listener.PlayerListener18; -import fr.xephi.authme.listener.PlayerListener19; -import fr.xephi.authme.listener.ServerListener; -import fr.xephi.authme.output.ConsoleFilter; -import fr.xephi.authme.output.Log4JFilter; -import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; -import fr.xephi.authme.permission.PermissionsManager; -import fr.xephi.authme.permission.PermissionsSystemType; -import fr.xephi.authme.process.Management; -import fr.xephi.authme.security.crypts.SHA256; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.SettingsMigrationService; -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.PluginSettings; -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.task.CleanupTask; -import fr.xephi.authme.task.purge.PurgeService; -import fr.xephi.authme.util.BukkitService; -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 fr.xephi.authme.util.ValidationService; +import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; +import static fr.xephi.authme.util.Utils.isClassLoaded; /** * The AuthMe main class. */ public class AuthMe extends JavaPlugin { - // Costants + // Constants private static final String PLUGIN_NAME = "AuthMeReloaded"; private static final String LOG_FILENAME = "authme.log"; - private static final String FLATFILE_FILENAME = "auths.db"; - private static final int SQLITE_MAX_SIZE = 4000; private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE; // Default version and build number values; @@ -162,69 +140,12 @@ public class AuthMe extends JavaPlugin { */ @Override public void onEnable() { - // Set the plugin instance and load plugin info from the plugin description. - loadPluginInfo(); - - // Set the Logger instance and log file path - ConsoleLogger.setLogger(getLogger()); - ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME)); - - // Load settings and custom configurations, if it fails, stop the server due to security reasons. - settings = createSettings(); - if (settings == null) { - ConsoleLogger.warning("Could not load the configuration file!" - + "The server is going to shutdown NOW!"); - setEnabled(false); - getServer().shutdown(); - return; - } - - // Apply settings to the logger - ConsoleLogger.setLoggingOptions(settings); - - // Set console filter - setupConsoleFilter(); - - // Connect to the database and setup tables try { - setupDatabase(); + initializeServices(); } catch (Exception e) { - ConsoleLogger.logException("Fatal error occurred during database connection! " - + "Authme initialization aborted!", e); + ConsoleLogger.logException("Aborting initialization of AuthMe:", e); stopOrUnload(); - return; } - // Convert deprecated PLAINTEXT hash entries - MigrationService.changePlainTextToSha256(settings, database, new SHA256()); - - // Injector initialization - injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); - - // Register elements of the Bukkit / JavaPlugin environment - injector.register(AuthMe.class, this); - injector.register(Server.class, getServer()); - injector.register(PluginManager.class, getServer().getPluginManager()); - injector.register(BukkitScheduler.class, getServer().getScheduler()); - injector.provide(DataFolder.class, getDataFolder()); - - // Register elements we instantiate manually - injector.register(Settings.class, settings); - injector.register(DataSource.class, database); - - instantiateServices(injector); - - // Reload support hook - reloadSupportHook(); - - // Do a backup on start - // TODO: maybe create a backup manager? - new PerformBackup(this, settings).doBackup(PerformBackup.BackupCause.START); - - // Register event listeners - registerEventListeners(injector); - - // Start Email recall task if needed - scheduleRecallEmailTask(); // Show settings warnings showSettingsWarnings(); @@ -234,6 +155,9 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.warning("Warning! This server uses PermissionsBukkit for permissions. Some permissions features may not be supported!"); } + // Do a backup on start + new PerformBackup(this, settings).doBackup(PerformBackup.BackupCause.START); + // Set up Metrics MetricsManager.sendMetrics(this, settings); @@ -253,6 +177,55 @@ public class AuthMe extends JavaPlugin { cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL); } + private void initializeServices() throws Exception { + // Set the plugin instance and load plugin info from the plugin description. + loadPluginInfo(); + + // Set the Logger instance and log file path + ConsoleLogger.setLogger(getLogger()); + ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME)); + + bukkitService = new BukkitService(this); + Initializer initializer = new Initializer(this, bukkitService); + + // Load settings and set up the console and console filter + settings = initializer.createSettings(); + ConsoleLogger.setLoggingOptions(settings); + initializer.setupConsoleFilter(settings, getLogger()); + + // Connect to the database and set up tables + database = initializer.setupDatabase(settings); + + // Convert deprecated PLAINTEXT hash entries + MigrationService.changePlainTextToSha256(settings, database, new SHA256()); + + // Injector initialization + injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create(); + + // Register elements of the Bukkit / JavaPlugin environment + injector.register(AuthMe.class, this); + injector.register(Server.class, getServer()); + injector.register(PluginManager.class, getServer().getPluginManager()); + injector.register(BukkitScheduler.class, getServer().getScheduler()); + injector.provide(DataFolder.class, getDataFolder()); + + // Register elements we instantiate manually + injector.register(Settings.class, settings); + injector.register(DataSource.class, database); + injector.register(BukkitService.class, bukkitService); + + instantiateServices(injector); + + // Reload support hook + reloadSupportHook(); + + // Register event listeners + registerEventListeners(injector); + + // Start Email recall task if needed + initializer.scheduleRecallEmailTask(settings, database, messages); + } + // Get version and build number of the plugin private void loadPluginInfo() { String versionRaw = this.getDescription().getVersion(); @@ -315,119 +288,18 @@ public class AuthMe extends JavaPlugin { pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this); // Try to register 1.6 player listeners - try { - Class.forName("org.bukkit.event.player.PlayerEditBookEvent"); + if (isClassLoaded("org.bukkit.event.player.PlayerEditBookEvent")) { pluginManager.registerEvents(injector.getSingleton(PlayerListener16.class), this); - } catch (ClassNotFoundException ignore) { } // Try to register 1.8 player listeners - try { - Class.forName("org.bukkit.event.player.PlayerInteractAtEntityEvent"); + if (isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) { pluginManager.registerEvents(injector.getSingleton(PlayerListener18.class), this); - } catch (ClassNotFoundException ignore) { } // Try to register 1.9 player listeners - try { - Class.forName("org.bukkit.event.player.PlayerSwapHandItemsEvent"); + if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) { pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this); - } catch (ClassNotFoundException ignore) { - } - } - - /** - * Loads the plugin's settings. - * - * @return The settings instance, or null if it could not be constructed - */ - private Settings createSettings() { - File configFile = new File(getDataFolder(), "config.yml"); - PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields(); - SettingsMigrationService migrationService = new SettingsMigrationService(); - return FileUtils.copyFileFromResource(configFile, "config.yml") - ? new Settings(configFile, getDataFolder(), properties, migrationService) - : null; - } - - /** - * Set up the console filter. - */ - private void setupConsoleFilter() { - if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) { - return; - } - // Try to set the log4j filter - try { - Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter"); - setLog4JFilter(); - } catch (ClassNotFoundException | NoClassDefFoundError e) { - // log4j is not available - ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled"); - ConsoleFilter filter = new ConsoleFilter(); - getLogger().setFilter(filter); - Bukkit.getLogger().setFilter(filter); - Logger.getLogger("Minecraft").setFilter(filter); - } - } - - // Set the console filter to remove the passwords - private void setLog4JFilter() { - org.apache.logging.log4j.core.Logger logger; - logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); - logger.addFilter(new Log4JFilter()); - } - - /** - * Sets up the data source. - * - * @throws ClassNotFoundException if no driver could be found for the datasource - * @throws SQLException when initialization of a SQL datasource failed - * @throws IOException if flat file cannot be read - * @see AuthMe#database - */ - private void setupDatabase() throws ClassNotFoundException, SQLException, IOException { - if (database != null) { - database.close(); - database = null; - } - - DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); - DataSource dataSource; - switch (dataSourceType) { - case FILE: - File source = new File(getDataFolder(), FLATFILE_FILENAME); - dataSource = new FlatFile(source); - break; - case MYSQL: - dataSource = new MySQL(settings); - break; - case SQLITE: - dataSource = new SQLite(settings); - break; - default: - throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'"); - } - - DataSource convertedSource = MigrationService.convertFlatfileToSqlite(settings, dataSource); - dataSource = convertedSource == null ? dataSource : convertedSource; - - if (settings.getProperty(DatabaseSettings.USE_CACHING)) { - dataSource = new CacheDataSource(dataSource); - } - - database = dataSource; - if (DataSourceType.SQLITE == dataSourceType) { - getServer().getScheduler().runTaskAsynchronously(this, new Runnable() { - @Override - public void run() { - int accounts = database.getAccountsRegistered(); - if (accounts >= SQLITE_MAX_SIZE) { - ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH " - + accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!"); - } - } - }); } } @@ -442,26 +314,6 @@ public class AuthMe extends JavaPlugin { } } - private void scheduleRecallEmailTask() { - if (!settings.getProperty(RECALL_PLAYERS)) { - return; - } - Bukkit.getScheduler().runTaskTimerAsynchronously(this, new Runnable() { - @Override - public void run() { - for (PlayerAuth auth : database.getLoggedPlayers()) { - String email = auth.getEmail(); - if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) { - Player player = bukkitService.getPlayerExact(auth.getRealName()); - if (player != null) { - messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); - } - } - } - } - }, 1, 1200 * settings.getProperty(EmailSettings.DELAY_RECALL)); - } - // TODO: check this, do we really need it? -sgdc3 private void reloadSupportHook() { if (database != null) { @@ -471,8 +323,8 @@ public class AuthMe extends JavaPlugin { } else if (settings.getProperty(SecuritySettings.USE_RELOAD_COMMAND_SUPPORT)) { for (PlayerAuth auth : database.getLoggedPlayers()) { if (auth != null) { - //auth.setLastLogin(new Date().getTime()); - //database.updateSession(auth); + auth.setLastLogin(new Date().getTime()); + database.updateSession(auth); playerCache.addPlayer(auth); } } diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index dea31c90f..6b87cb53e 100644 --- a/src/main/java/fr/xephi/authme/api/NewAPI.java +++ b/src/main/java/fr/xephi/authme/api/NewAPI.java @@ -216,6 +216,6 @@ public class NewAPI { * @param player The player to unregister */ public void forceUnregister(Player player) { - management.performUnregister(player, "", true); + management.performUnregisterByAdmin(null, player.getName(), player); } } 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 5560c694d..eaf14b6b7 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommand.java @@ -1,13 +1,9 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; -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.permission.AuthGroupHandler; -import fr.xephi.authme.permission.AuthGroupType; import fr.xephi.authme.process.Management; import fr.xephi.authme.util.BukkitService; import org.bukkit.command.CommandSender; @@ -27,47 +23,27 @@ public class UnregisterAdminCommand implements ExecutableCommand { @Inject private CommandService commandService; - @Inject - private PlayerCache playerCache; - @Inject private BukkitService bukkitService; - @Inject - private AuthGroupHandler authGroupHandler; - @Inject private Management management; + UnregisterAdminCommand() { + } @Override public void executeCommand(final CommandSender sender, List arguments) { - // Get the player name String playerName = arguments.get(0); - String playerNameLowerCase = playerName.toLowerCase(); - // Make sure the user is valid - if (!dataSource.isAuthAvailable(playerNameLowerCase)) { + // Make sure the user exists + if (!dataSource.isAuthAvailable(playerName)) { commandService.send(sender, MessageKey.UNKNOWN_USER); return; } - // Remove the player - if (!dataSource.removeAuth(playerNameLowerCase)) { - commandService.send(sender, MessageKey.ERROR); - return; - } - - // Unregister the player - Player target = bukkitService.getPlayerExact(playerNameLowerCase); - playerCache.removePlayer(playerNameLowerCase); - authGroupHandler.setGroup(target, AuthGroupType.UNREGISTERED); - if (target != null && target.isOnline()) { - management.performUnregister(target, "dontneed", true); - } - - // Show a status message - commandService.send(sender, MessageKey.UNREGISTERED_SUCCESS); - ConsoleLogger.info(sender.getName() + " unregistered " + playerName); + // Get the player from the server and perform unregister + Player target = bukkitService.getPlayerExact(playerName); + management.performUnregisterByAdmin(sender, playerName, target); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java index 720b136e1..8526e4a1d 100644 --- a/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/unregister/UnregisterCommand.java @@ -10,6 +10,9 @@ import org.bukkit.entity.Player; import javax.inject.Inject; import java.util.List; +/** + * Command for a player to unregister himself. + */ public class UnregisterCommand extends PlayerCommand { @Inject @@ -24,15 +27,15 @@ public class UnregisterCommand extends PlayerCommand { @Override public void runCommand(Player player, List arguments) { String playerPass = arguments.get(0); - final String playerNameLowerCase = player.getName().toLowerCase(); + final String playerName = player.getName(); // Make sure the player is authenticated - if (!playerCache.isAuthenticated(playerNameLowerCase)) { + if (!playerCache.isAuthenticated(playerName)) { commandService.send(player, MessageKey.NOT_LOGGED_IN); return; } // Unregister the player - management.performUnregister(player, playerPass, false); + management.performUnregister(player, playerPass); } } diff --git a/src/main/java/fr/xephi/authme/initialization/Initializer.java b/src/main/java/fr/xephi/authme/initialization/Initializer.java new file mode 100644 index 000000000..5c5a8a1de --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/Initializer.java @@ -0,0 +1,167 @@ +package fr.xephi.authme.initialization; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.cache.auth.PlayerAuth; +import fr.xephi.authme.datasource.CacheDataSource; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceType; +import fr.xephi.authme.datasource.FlatFile; +import fr.xephi.authme.datasource.MySQL; +import fr.xephi.authme.datasource.SQLite; +import fr.xephi.authme.output.ConsoleFilter; +import fr.xephi.authme.output.Log4JFilter; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.SettingsMigrationService; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.EmailSettings; +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.FileUtils; +import fr.xephi.authme.util.MigrationService; +import fr.xephi.authme.util.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.util.logging.Logger; + +import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS; +import static fr.xephi.authme.util.BukkitService.TICKS_PER_MINUTE; + +/** + * Initializes the plugin's data source. + */ +public class Initializer { + + private static final String FLATFILE_FILENAME = "auths.db"; + private static final int SQLITE_MAX_SIZE = 4000; + + private AuthMe authMe; + private BukkitService bukkitService; + + public Initializer(AuthMe authMe, BukkitService bukkitService) { + this.authMe = authMe; + this.bukkitService = bukkitService; + } + + /** + * Loads the plugin's settings. + * + * @return The settings instance, or null if it could not be constructed + */ + public Settings createSettings() throws Exception { + File configFile = new File(authMe.getDataFolder(), "config.yml"); + PropertyMap properties = SettingsFieldRetriever.getAllPropertyFields(); + SettingsMigrationService migrationService = new SettingsMigrationService(); + if (FileUtils.copyFileFromResource(configFile, "config.yml")) { + return new Settings(configFile, authMe.getDataFolder(), properties, migrationService); + } + throw new Exception("Could not copy config.yml from JAR to plugin folder"); + } + + /** + * Sets up the data source. + * + * @throws ClassNotFoundException if no driver could be found for the datasource + * @throws SQLException when initialization of a SQL datasource failed + * @throws IOException if flat file cannot be read + */ + public DataSource setupDatabase(Settings settings) throws ClassNotFoundException, SQLException, IOException { + DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); + DataSource dataSource; + switch (dataSourceType) { + case FILE: + File source = new File(authMe.getDataFolder(), FLATFILE_FILENAME); + dataSource = new FlatFile(source); + break; + case MYSQL: + dataSource = new MySQL(settings); + break; + case SQLITE: + dataSource = new SQLite(settings); + break; + default: + throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'"); + } + + DataSource convertedSource = MigrationService.convertFlatfileToSqlite(settings, dataSource); + dataSource = convertedSource == null ? dataSource : convertedSource; + + if (settings.getProperty(DatabaseSettings.USE_CACHING)) { + dataSource = new CacheDataSource(dataSource); + } + if (DataSourceType.SQLITE.equals(dataSourceType)) { + checkDataSourceSize(dataSource); + } + return dataSource; + } + + private void checkDataSourceSize(final DataSource dataSource) { + bukkitService.runTaskAsynchronously(new Runnable() { + @Override + public void run() { + int accounts = dataSource.getAccountsRegistered(); + if (accounts >= SQLITE_MAX_SIZE) { + ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH " + + accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!"); + } + } + }); + } + + /** + * Sets up the console filter if enabled. + */ + public void setupConsoleFilter(Settings settings, Logger logger) { + if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) { + return; + } + // Try to set the log4j filter + try { + Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter"); + setLog4JFilter(); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // log4j is not available + ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled"); + ConsoleFilter filter = new ConsoleFilter(); + logger.setFilter(filter); + Bukkit.getLogger().setFilter(filter); + Logger.getLogger("Minecraft").setFilter(filter); + } + } + + // Set the console filter to remove the passwords + private static void setLog4JFilter() { + org.apache.logging.log4j.core.Logger logger; + logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger(); + logger.addFilter(new Log4JFilter()); + } + + public void scheduleRecallEmailTask(Settings settings, final DataSource dataSource, final Messages messages) { + if (!settings.getProperty(RECALL_PLAYERS)) { + return; + } + bukkitService.runTaskTimerAsynchronously(new Runnable() { + @Override + public void run() { + for (PlayerAuth auth : dataSource.getLoggedPlayers()) { + String email = auth.getEmail(); + if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) { + Player player = bukkitService.getPlayerExact(auth.getRealName()); + if (player != null) { + messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); + } + } + } + } + }, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL)); + } +} diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index d229edfb0..63d96b970 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -10,6 +10,7 @@ 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.util.BukkitService; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -70,11 +71,20 @@ public class Management { }); } - public void performUnregister(final Player player, final String password, final boolean isForce) { + public void performUnregister(final Player player, final String password) { runTask(new Runnable() { @Override public void run() { - asynchronousUnregister.unregister(player, password, isForce); + asynchronousUnregister.unregister(player, password); + } + }); + } + + public void performUnregisterByAdmin(final CommandSender initiator, final String name, final Player player) { + runTask(new Runnable() { + @Override + public void run() { + asynchronousUnregister.adminUnregister(initiator, name, player); } }); } 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 57e8b6a87..82fea4db5 100644 --- a/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java +++ b/src/main/java/fr/xephi/authme/process/unregister/AsynchronousUnregister.java @@ -6,16 +6,17 @@ 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.permission.AuthGroupHandler; import fr.xephi.authme.permission.AuthGroupType; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.ProcessService; import fr.xephi.authme.security.PasswordSecurity; -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.PlayerDataTaskManager; import fr.xephi.authme.util.BukkitService; import fr.xephi.authme.util.TeleportationService; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -39,7 +40,7 @@ public class AsynchronousUnregister implements AsynchronousProcess { private PlayerCache playerCache; @Inject - private BukkitService bukktiService; + private BukkitService bukkitService; @Inject private LimboCache limboCache; @@ -50,54 +51,84 @@ public class AsynchronousUnregister implements AsynchronousProcess { @Inject private TeleportationService teleportationService; + @Inject + private AuthGroupHandler authGroupHandler; + AsynchronousUnregister() { } - - public void unregister(final Player player, String password, boolean force) { - final String name = player.getName().toLowerCase(); - PlayerAuth cachedAuth = playerCache.getAuth(name); - if (force || passwordSecurity.comparePassword(password, cachedAuth.getPassword(), player.getName())) { - if (!dataSource.removeAuth(name)) { + /** + * Processes a player's request to unregister himself. Unregisters the player after + * successful password check. + * + * @param player the player + * @param password the input password to check before unregister + */ + public void unregister(Player player, String password) { + final String name = player.getName(); + final PlayerAuth cachedAuth = playerCache.getAuth(name); + if (passwordSecurity.comparePassword(password, cachedAuth.getPassword(), name)) { + if (dataSource.removeAuth(name)) { + performUnregister(name, player); + ConsoleLogger.info(name + " unregistered himself"); + } else { service.send(player, MessageKey.ERROR); - return; } - - if (service.getProperty(RegistrationSettings.FORCE)) { - teleportationService.teleportOnJoin(player); - player.saveData(); - playerCache.removePlayer(player.getName().toLowerCase()); - if (!service.getProperty(HooksSettings.REGISTERED_GROUP).isEmpty()) { - service.setGroup(player, AuthGroupType.UNREGISTERED); - } - limboCache.deletePlayerData(player); - limboCache.addPlayerData(player); - playerDataTaskManager.registerTimeoutTask(player); - playerDataTaskManager.registerMessageTask(name, false); - - service.send(player, MessageKey.UNREGISTERED_SUCCESS); - ConsoleLogger.info(player.getName() + " unregistered himself"); - return; // TODO ljacqu 20160612: Why return here? No blind effect? Player not removed from PlayerCache? - } - if (!service.getProperty(HooksSettings.UNREGISTERED_GROUP).isEmpty()) { - service.setGroup(player, AuthGroupType.UNREGISTERED); - } - playerCache.removePlayer(name); - - // Apply blind effect - final int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; - if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { - bukktiService.runTask(new Runnable() { - @Override - public void run() { - player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeout, 2)); - } - }); - } - - service.send(player, MessageKey.UNREGISTERED_SUCCESS); - ConsoleLogger.info(player.getName() + " unregistered himself"); } else { service.send(player, MessageKey.WRONG_PASSWORD); } } + + /** + * Unregisters a player. + * + * @param initiator the initiator of this process (nullable) + * @param name the name of the player + * @param player the according Player object (nullable) + */ + // We need to have the name and the player separate because Player might be null in this case: + // we might have some player in the database that has never been online on the server + public void adminUnregister(CommandSender initiator, String name, Player player) { + if (dataSource.removeAuth(name)) { + performUnregister(name, player); + if (initiator == null) { + ConsoleLogger.info(name + " was unregistered"); + } else { + ConsoleLogger.info(name + " was unregistered by " + initiator.getName()); + service.send(initiator, MessageKey.UNREGISTERED_SUCCESS); + } + } + } + + private void performUnregister(String name, Player player) { + playerCache.removePlayer(name); + if (player == null || !player.isOnline()) { + return; + } + + if (service.getProperty(RegistrationSettings.FORCE)) { + teleportationService.teleportOnJoin(player); + player.saveData(); + + limboCache.deletePlayerData(player); + limboCache.addPlayerData(player); + + playerDataTaskManager.registerTimeoutTask(player); + playerDataTaskManager.registerMessageTask(name, false); + applyBlindEffect(player); + } + authGroupHandler.setGroup(player, AuthGroupType.UNREGISTERED); + service.send(player, MessageKey.UNREGISTERED_SUCCESS); + } + + private void applyBlindEffect(final Player player) { + if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) { + final int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; + bukkitService.runTask(new Runnable() { + @Override + public void run() { + player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeout, 2)); + } + }); + } + } } diff --git a/src/main/java/fr/xephi/authme/security/HashUtils.java b/src/main/java/fr/xephi/authme/security/HashUtils.java index c4fb4edf1..946af52be 100644 --- a/src/main/java/fr/xephi/authme/security/HashUtils.java +++ b/src/main/java/fr/xephi/authme/security/HashUtils.java @@ -67,6 +67,17 @@ public final class HashUtils { } } + /** + * Return whether the given hash starts like a BCrypt hash. Checking with this method + * beforehand prevents the BcryptService from throwing certain exceptions. + * + * @param hash The salt to verify + * @return True if the salt is valid, false otherwise + */ + public static boolean isValidBcryptHash(String hash) { + return hash.length() > 3 && hash.substring(0, 2).equals("$2"); + } + /** * Hash the message with the given algorithm and return the hash in its hexadecimal notation. * diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java index 4a4f92f33..aae6b9109 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java @@ -1,6 +1,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.security.crypts.description.HasSalt; import fr.xephi.authme.security.crypts.description.Recommendation; import fr.xephi.authme.security.crypts.description.SaltType; @@ -36,7 +37,7 @@ public class BCRYPT implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hash, String name) { try { - return hash.getHash().length() > 3 && BCryptService.checkpw(password, hash.getHash()); + return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e)); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/IPB4.java b/src/main/java/fr/xephi/authme/security/crypts/IPB4.java index 95a3bfd2a..0eede2a22 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/IPB4.java +++ b/src/main/java/fr/xephi/authme/security/crypts/IPB4.java @@ -1,6 +1,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.security.crypts.description.HasSalt; import fr.xephi.authme.security.crypts.description.Recommendation; @@ -34,7 +35,7 @@ public class IPB4 implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hash, String name) { try { - return hash.getHash().length() > 3 && BCryptService.checkpw(password, hash.getHash()); + return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { ConsoleLogger.warning("Bcrypt checkpw() returned " + StringUtils.formatException(e)); } diff --git a/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java index c3487f311..a20ee65aa 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java +++ b/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java @@ -1,6 +1,7 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.util.StringUtils; import java.util.regex.Matcher; @@ -29,7 +30,7 @@ public class XFBCRYPT implements EncryptionMethod { @Override public boolean comparePassword(String password, HashedPassword hash, String salt) { try { - return hash.getHash().length() > 3 && BCryptService.checkpw(password, hash.getHash()); + return HashUtils.isValidBcryptHash(hash.getHash()) && BCryptService.checkpw(password, hash.getHash()); } catch (IllegalArgumentException e) { ConsoleLogger.warning("XfBCrypt checkpw() returned " + StringUtils.formatException(e)); } diff --git a/src/main/java/fr/xephi/authme/util/BukkitService.java b/src/main/java/fr/xephi/authme/util/BukkitService.java index 124488098..1483f09b3 100644 --- a/src/main/java/fr/xephi/authme/util/BukkitService.java +++ b/src/main/java/fr/xephi/authme/util/BukkitService.java @@ -14,7 +14,6 @@ import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; -import javax.inject.Inject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -37,8 +36,7 @@ public class BukkitService { private final boolean getOnlinePlayersIsCollection; private Method getOnlinePlayers; - @Inject - BukkitService(AuthMe authMe) { + public BukkitService(AuthMe authMe) { this.authMe = authMe; getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField(); } @@ -113,17 +111,18 @@ public class BukkitService { * Asynchronous tasks should never access any API in Bukkit. Great care * should be taken to assure the thread-safety of asynchronous tasks. *

- * Returns a task that will run asynchronously after the specified number - * of server ticks. + * Returns a task that will repeatedly run asynchronously until cancelled, + * starting after the specified number of server ticks. * * @param task the task to be run - * @param delay the ticks to wait before running the task + * @param delay the ticks to wait before running the task for the first + * time + * @param period the ticks to wait between runs * @return a BukkitTask that contains the id number - * @throws IllegalArgumentException if plugin is null * @throws IllegalArgumentException if task is null */ - public BukkitTask runTaskLaterAsynchronously(Runnable task, long delay) { - return Bukkit.getScheduler().runTaskLaterAsynchronously(authMe, task, delay); + public BukkitTask runTaskTimerAsynchronously(Runnable task, long delay, long period) { + return Bukkit.getScheduler().runTaskTimerAsynchronously(authMe, task, delay, period); } /** diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index 0d4a30b82..162ca308d 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -13,7 +13,7 @@ import static java.lang.String.format; /** * File utilities. */ -public class FileUtils { +public final class FileUtils { private FileUtils() { } diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index e75d02afd..c2122ee3e 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -57,4 +57,20 @@ public final class Utils { public static String getPlayerIp(Player p) { return p.getAddress().getAddress().getHostAddress(); } + + /** + * Returns whether the class exists in the current class loader. + * + * @param className the class name to check + * + * @return true if the class is loaded, false otherwise + */ + public static boolean isClassLoaded(String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } } diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index 4937e7b1b..131ca9e70 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -14,6 +14,7 @@ import fr.xephi.authme.process.login.ProcessSyncPlayerLogin; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.task.purge.PurgeService; +import fr.xephi.authme.util.BukkitService; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.plugin.PluginDescriptionFile; @@ -102,6 +103,7 @@ public class AuthMeInitializationTest { injector.register(AuthMe.class, authMe); injector.register(Settings.class, settings); injector.register(DataSource.class, mock(DataSource.class)); + injector.register(BukkitService.class, mock(BukkitService.class)); // when authMe.instantiateServices(injector); diff --git a/src/test/java/fr/xephi/authme/cache/TempbanManagerTest.java b/src/test/java/fr/xephi/authme/cache/TempbanManagerTest.java index 7435512df..911900e41 100644 --- a/src/test/java/fr/xephi/authme/cache/TempbanManagerTest.java +++ b/src/test/java/fr/xephi/authme/cache/TempbanManagerTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.verifyZeroInteractions; @RunWith(MockitoJUnitRunner.class) public class TempbanManagerTest { - private static final long DATE_TOLERANCE_MILLISECONDS = 100L; + private static final long DATE_TOLERANCE_MILLISECONDS = 200L; @Mock private BukkitService bukkitService; diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java index f41eeb202..aea73ed71 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/UnregisterAdminCommandTest.java @@ -3,7 +3,10 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.process.Management; +import fr.xephi.authme.util.BukkitService; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -12,12 +15,10 @@ import org.mockito.runners.MockitoJUnitRunner; import java.util.Collections; -import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.mockito.BDDMockito.given; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; /** * Test for {@link UnregisterAdminCommand}. @@ -34,6 +35,12 @@ public class UnregisterAdminCommandTest { @Mock private CommandService commandService; + @Mock + private BukkitService bukkitService; + + @Mock + private Management management; + @Test public void shouldHandleUnknownPlayer() { // given @@ -45,27 +52,45 @@ public class UnregisterAdminCommandTest { command.executeCommand(sender, Collections.singletonList(user)); // then - verify(dataSource).isAuthAvailable(user); - verifyNoMoreInteractions(dataSource); + verify(dataSource, only()).isAuthAvailable(user); verify(commandService).send(sender, MessageKey.UNKNOWN_USER); } @Test - public void shouldHandleDatabaseError() { + public void shouldInvokeUnregisterProcess() { // given String user = "personaNonGrata"; - given(dataSource.isAuthAvailable(argThat(equalToIgnoringCase(user)))).willReturn(true); - given(dataSource.removeAuth(argThat(equalToIgnoringCase(user)))).willReturn(false); + given(dataSource.isAuthAvailable(user)).willReturn(true); + given(dataSource.removeAuth(user)).willReturn(false); + Player player = mock(Player.class); + given(bukkitService.getPlayerExact(user)).willReturn(player); CommandSender sender = mock(CommandSender.class); // when command.executeCommand(sender, Collections.singletonList(user)); // then - verify(dataSource).isAuthAvailable(argThat(equalToIgnoringCase(user))); - verify(dataSource).removeAuth(argThat(equalToIgnoringCase(user))); - verifyNoMoreInteractions(dataSource); - verify(commandService).send(sender, MessageKey.ERROR); + verify(dataSource, only()).isAuthAvailable(user); + verify(bukkitService).getPlayerExact(user); + verify(management).performUnregisterByAdmin(sender, user, player); + } + + @Test + public void shouldInvokeUnregisterProcessWithNullPlayer() { + // given + String user = "personaNonGrata"; + given(dataSource.isAuthAvailable(user)).willReturn(true); + given(dataSource.removeAuth(user)).willReturn(false); + given(bukkitService.getPlayerExact(user)).willReturn(null); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Collections.singletonList(user)); + + // then + verify(dataSource, only()).isAuthAvailable(user); + verify(bukkitService).getPlayerExact(user); + verify(management).performUnregisterByAdmin(sender, user, null); } } diff --git a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java index 976649aea..33bbf4c68 100644 --- a/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/unregister/UnregisterCommandTest.java @@ -68,7 +68,7 @@ public class UnregisterCommandTest { // then verify(playerCache).isAuthenticated(name); - verify(management).performUnregister(player, password, false); + verify(management).performUnregister(player, password); } } diff --git a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java index aef01ed61..590dd502e 100644 --- a/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/listener/ListenerConsistencyTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.fail; public final class ListenerConsistencyTest { private static final Class[] LISTENERS = { BlockListener.class, EntityListener.class, - PlayerListener.class, PlayerListener16.class, PlayerListener18.class, + PlayerListener.class, PlayerListener16.class, PlayerListener18.class, PlayerListener19.class, ServerListener.class }; private static final Set CANCELED_EXCEPTIONS = Sets.newHashSet( diff --git a/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java new file mode 100644 index 000000000..02035e329 --- /dev/null +++ b/src/test/java/fr/xephi/authme/process/unregister/AsynchronousUnregisterTest.java @@ -0,0 +1,121 @@ +package fr.xephi.authme.process.unregister; + +import fr.xephi.authme.TestHelper; +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.datasource.DataSource; +import fr.xephi.authme.output.MessageKey; +import fr.xephi.authme.permission.AuthGroupHandler; +import fr.xephi.authme.permission.AuthGroupType; +import fr.xephi.authme.process.ProcessService; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.task.PlayerDataTaskManager; +import fr.xephi.authme.util.BukkitService; +import fr.xephi.authme.util.TeleportationService; +import org.bukkit.entity.Player; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Test for {@link AsynchronousUnregister}. + */ +@RunWith(MockitoJUnitRunner.class) +public class AsynchronousUnregisterTest { + + @InjectMocks + private AsynchronousUnregister asynchronousUnregister; + + @Mock + private DataSource dataSource; + @Mock + private ProcessService service; + @Mock + private PasswordSecurity passwordSecurity; + @Mock + private PlayerCache playerCache; + @Mock + private BukkitService bukkitService; + @Mock + private LimboCache limboCache; + @Mock + private PlayerDataTaskManager playerDataTaskManager; + @Mock + private TeleportationService teleportationService; + @Mock + private AuthGroupHandler authGroupHandler; + + @BeforeClass + public static void initLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldRejectWrongPassword() { + // given + Player player = mock(Player.class); + String name = "Bobby"; + given(player.getName()).willReturn(name); + PlayerAuth auth = mock(PlayerAuth.class); + given(playerCache.getAuth(name)).willReturn(auth); + HashedPassword password = new HashedPassword("password", "in_auth_obj"); + given(auth.getPassword()).willReturn(password); + String userPassword = "pass"; + given(passwordSecurity.comparePassword(userPassword, password, name)).willReturn(false); + + // when + asynchronousUnregister.unregister(player, userPassword); + + // then + verify(service).send(player, MessageKey.WRONG_PASSWORD); + verify(passwordSecurity).comparePassword(userPassword, password, name); + verifyZeroInteractions(dataSource, playerDataTaskManager, limboCache, authGroupHandler, teleportationService); + verify(player, only()).getName(); + } + + @Test + public void shouldPerformUnregister() { + // given + Player player = mock(Player.class); + String name = "Frank21"; + given(player.getName()).willReturn(name); + given(player.isOnline()).willReturn(true); + PlayerAuth auth = mock(PlayerAuth.class); + given(playerCache.getAuth(name)).willReturn(auth); + HashedPassword password = new HashedPassword("password", "in_auth_obj"); + given(auth.getPassword()).willReturn(password); + String userPassword = "pass"; + given(passwordSecurity.comparePassword(userPassword, password, name)).willReturn(true); + given(dataSource.removeAuth(name)).willReturn(true); + given(service.getProperty(RegistrationSettings.FORCE)).willReturn(true); + given(service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)).willReturn(true); + given(service.getProperty(RestrictionSettings.TIMEOUT)).willReturn(12); + + // when + asynchronousUnregister.unregister(player, userPassword); + + // then + verify(service).send(player, MessageKey.UNREGISTERED_SUCCESS); + verify(passwordSecurity).comparePassword(userPassword, password, name); + verify(dataSource).removeAuth(name); + verify(playerCache).removePlayer(name); + verify(teleportationService).teleportOnJoin(player); + verify(authGroupHandler).setGroup(player, AuthGroupType.UNREGISTERED); + verify(bukkitService).runTask(any(Runnable.class)); + } + +} diff --git a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java index c7ef1ccd2..5c1fda220 100644 --- a/src/test/java/fr/xephi/authme/security/HashUtilsTest.java +++ b/src/test/java/fr/xephi/authme/security/HashUtilsTest.java @@ -113,4 +113,14 @@ public class HashUtilsTest { assertThat(digest.getAlgorithm(), equalTo("MD5")); } + @Test + public void shouldCheckForValidBcryptHashStart() { + // given / when / then + assertThat(HashUtils.isValidBcryptHash(""), equalTo(false)); + assertThat(HashUtils.isValidBcryptHash("$2afsdaf"), equalTo(true)); + assertThat(HashUtils.isValidBcryptHash("$2"), equalTo(false)); + assertThat(HashUtils.isValidBcryptHash("$2aead234adef"), equalTo(true)); + assertThat(HashUtils.isValidBcryptHash("#2ae5fc78"), equalTo(false)); + } + } diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/src/test/java/fr/xephi/authme/util/UtilsTest.java index 31a6a1ec7..930a5db8a 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -96,4 +96,11 @@ public class UtilsTest { // given / when / then TestHelper.validateHasOnlyPrivateEmptyConstructor(Utils.class); } + + @Test + public void shouldCheckIfClassIsLoaded() { + // given / when / then + assertThat(Utils.isClassLoaded("org.bukkit.event.player.PlayerFishEvent"), equalTo(true)); + assertThat(Utils.isClassLoaded("com.someclass.doesnot.exist"), equalTo(false)); + } }