Merge branch 'master' into db-improve

Conflicts:
	src/main/java/fr/xephi/authme/AuthMe.java
This commit is contained in:
DNx5 2016-02-26 12:42:32 +07:00
commit 8324decfa1
61 changed files with 1086 additions and 510 deletions

View File

@ -50,7 +50,7 @@ McStats: http://mcstats.org/plugin/AuthMe
#####Running Requirements: #####Running Requirements:
>- Java 1.7 (should work also with Java 1.8) >- Java 1.7 (should work also with Java 1.8)
>- Spigot or CraftBukkit (1.7.10 or 1.8.X) >- Spigot or CraftBukkit (1.7.10, 1.8.X or 1.9-pre1)
>- ProtocolLib (optional, required by the protectInventory feature) >- ProtocolLib (optional, required by the protectInventory feature)
<hr> <hr>

15
pom.xml
View File

@ -61,7 +61,7 @@
<javaVersion>1.7</javaVersion> <javaVersion>1.7</javaVersion>
<!-- Change Bukkit Version HERE! --> <!-- Change Bukkit Version HERE! -->
<bukkitVersion>1.8.8-R0.1-SNAPSHOT</bukkitVersion> <bukkitVersion>1.9-pre1-SNAPSHOT</bukkitVersion>
</properties> </properties>
<profiles> <profiles>
@ -376,6 +376,19 @@
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- JDBC drivers for datasource integration tests -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.8.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
<scope>test</scope>
</dependency>
<!-- Log4J Logger (required by the console filter) --> <!-- Log4J Logger (required by the console filter) -->
<dependency> <dependency>

View File

@ -17,7 +17,6 @@ import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandMapper; import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.DataSourceType;
@ -43,9 +42,8 @@ import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PlayerStatePermission; import fr.xephi.authme.permission.PlayerStatePermission;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.OtherAccounts; import fr.xephi.authme.settings.OtherAccounts;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
@ -58,9 +56,9 @@ import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.MigrationService;
import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.Wrapper;
import net.minelink.ctplus.CombatTagPlus; import net.minelink.ctplus.CombatTagPlus;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -77,6 +75,7 @@ import org.bukkit.scheduler.BukkitTask;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
@ -106,27 +105,23 @@ public class AuthMe extends JavaPlugin {
// Private Instances // Private Instances
private static AuthMe plugin; private static AuthMe plugin;
private static Server server; private static Server server;
private static Wrapper wrapper = Wrapper.getInstance(); /*
private Management management; * Maps and stuff
private CommandHandler commandHandler = null; * TODO: Clean up and Move into a manager
private PermissionsManager permsMan = null; */
private NewSetting newSettings; public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>();
private Messages messages; public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>();
private JsonCache playerBackup; public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>();
private PasswordSecurity passwordSecurity; public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
private DataSource database;
/* /*
* Public Instances * Public Instances
* TODO: Encapsulation * TODO #432: Encapsulation
*/ */
public NewAPI api; public NewAPI api;
public SendMailSSL mail; public SendMailSSL mail;
public DataManager dataManager; public DataManager dataManager;
public OtherAccounts otherAccounts; public OtherAccounts otherAccounts;
public Location essentialsSpawn; public Location essentialsSpawn;
/* /*
* Plugin Hooks * Plugin Hooks
* TODO: Move into modules * TODO: Move into modules
@ -137,15 +132,14 @@ public class AuthMe extends JavaPlugin {
public AuthMeInventoryPacketAdapter inventoryProtector; public AuthMeInventoryPacketAdapter inventoryProtector;
public AuthMeTabCompletePacketAdapter tabComplete; public AuthMeTabCompletePacketAdapter tabComplete;
public AuthMeTablistPacketAdapter tablistHider; public AuthMeTablistPacketAdapter tablistHider;
private Management management;
/* private CommandHandler commandHandler = null;
* Maps and stuff private PermissionsManager permsMan = null;
* TODO: Clean up and Move into a manager private NewSetting newSettings;
*/ private Messages messages;
public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>(); private JsonCache playerBackup;
public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>(); private PasswordSecurity passwordSecurity;
public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>(); private DataSource database;
public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
/** /**
* Get the plugin's instance. * Get the plugin's instance.
@ -247,7 +241,7 @@ public class AuthMe extends JavaPlugin {
// Connect to the database and setup tables // Connect to the database and setup tables
try { try {
setupDatabase(); setupDatabase(newSettings);
} catch (Exception e) { } catch (Exception e) {
ConsoleLogger.logException("Fatal error occurred during database connection! " ConsoleLogger.logException("Fatal error occurred during database connection! "
+ "Authme initialization aborted!", e); + "Authme initialization aborted!", e);
@ -255,6 +249,7 @@ public class AuthMe extends JavaPlugin {
return; return;
} }
MigrationService.changePlainTextToSha256(newSettings, database, new SHA256());
passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.PASSWORD_HASH), passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.PASSWORD_HASH),
Bukkit.getPluginManager(), newSettings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH)); Bukkit.getPluginManager(), newSettings.getProperty(SecuritySettings.SUPPORT_OLD_PASSWORD_HASH));
@ -547,25 +542,42 @@ public class AuthMe extends JavaPlugin {
} }
} }
public void setupDatabase() throws Exception { /**
if (database != null) * Sets up the data source.
database.close(); *
// Backend MYSQL - FILE - SQLITE - SQLITEHIKARI * @param settings The settings instance
boolean isSQLite = false; * @see AuthMe#database
switch (newSettings.getProperty(DatabaseSettings.BACKEND)) { */
case FILE: public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException {
database = new FlatFile(); if (this.database != null) {
break; this.database.close();
case MYSQL:
database = new MySQL(newSettings);
break;
case SQLITE:
database = new SQLite(newSettings);
isSQLite = true;
break;
} }
if (isSQLite) { DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
dataSource = new FlatFile();
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(newSettings, dataSource);
dataSource = convertedSource == null ? dataSource : convertedSource;
if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
database = dataSource;
if (DataSourceType.SQLITE == dataSourceType) {
server.getScheduler().runTaskAsynchronously(this, new Runnable() { server.getScheduler().runTaskAsynchronously(this, new Runnable() {
@Override @Override
public void run() { public void run() {
@ -577,34 +589,6 @@ public class AuthMe extends JavaPlugin {
} }
}); });
} }
if (Settings.getDataSource == DataSourceType.FILE) {
ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated, it will be changed " +
"to SQLite... Connection will be impossible until conversion is done!");
ForceFlatToSqlite converter = new ForceFlatToSqlite(database, newSettings);
DataSource source = converter.run();
if (source != null) {
database = source;
}
}
// TODO: Move this to another place maybe ?
if (HashAlgorithm.PLAINTEXT == newSettings.getProperty(SecuritySettings.PASSWORD_HASH)) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " +
"it will be changed and hashed now to the AuthMe default hashing method");
for (PlayerAuth auth : database.getAllAuths()) {
HashedPassword hashedPassword = passwordSecurity.computeHash(
HashAlgorithm.SHA256, auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(hashedPassword);
database.updatePassword(auth);
}
newSettings.setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256);
newSettings.save();
}
if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) {
database = new CacheDataSource(database);
}
} }
/** /**
@ -712,14 +696,14 @@ public class AuthMe extends JavaPlugin {
if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) { if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) {
tabComplete = new AuthMeTabCompletePacketAdapter(this); tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register(); tabComplete.register();
} else if (inventoryProtector != null) { } else if (tabComplete != null) {
tabComplete.unregister(); tabComplete.unregister();
tabComplete = null; tabComplete = null;
} }
if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) { if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) {
tablistHider = new AuthMeTablistPacketAdapter(this); tablistHider = new AuthMeTablistPacketAdapter(this);
tablistHider.register(); tablistHider.register();
} else if (inventoryProtector != null) { } else if (tablistHider != null) {
tablistHider.unregister(); tablistHider.unregister();
tablistHider = null; tablistHider = null;
} }
@ -788,25 +772,9 @@ public class AuthMe extends JavaPlugin {
} }
// Return the spawn location of a player // Return the spawn location of a player
@Deprecated
public Location getSpawnLocation(Player player) { public Location getSpawnLocation(Player player) {
World world = player.getWorld(); return Spawn.getInstance().getSpawnLocation(player);
String[] spawnPriority = Settings.spawnPriority.split(",");
Location spawnLoc = world.getSpawnLocation();
for (int i = spawnPriority.length - 1; i >= 0; i--) {
String s = spawnPriority[i];
if (s.equalsIgnoreCase("default") && getDefaultSpawn(world) != null)
spawnLoc = getDefaultSpawn(world);
if (s.equalsIgnoreCase("multiverse") && getMultiverseSpawn(world) != null)
spawnLoc = getMultiverseSpawn(world);
if (s.equalsIgnoreCase("essentials") && getEssentialsSpawn() != null)
spawnLoc = getEssentialsSpawn();
if (s.equalsIgnoreCase("authme") && getAuthMeSpawn(player) != null)
spawnLoc = getAuthMeSpawn(player);
}
if (spawnLoc == null) {
spawnLoc = world.getSpawnLocation();
}
return spawnLoc;
} }
// Return the default spawn point of a world // Return the default spawn point of a world
@ -948,7 +916,7 @@ public class AuthMe extends JavaPlugin {
String commandLabel, String[] args) { String commandLabel, String[] args) {
// Make sure the command handler has been initialized // Make sure the command handler has been initialized
if (commandHandler == null) { if (commandHandler == null) {
wrapper.getLogger().severe("AuthMe command handler is not available"); getLogger().severe("AuthMe command handler is not available");
return false; return false;
} }

View File

@ -1,12 +1,11 @@
package fr.xephi.authme.cache.auth; package fr.xephi.authme.cache.auth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.Location;
import static com.google.common.base.Objects.firstNonNull; import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import org.bukkit.Location;
import fr.xephi.authme.security.crypts.HashedPassword;
/** /**
*/ */
@ -85,20 +84,6 @@ public class PlayerAuth {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, 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 salt String
* @param ip String
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, String realName) {
this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/** /**
* Constructor for PlayerAuth. * Constructor for PlayerAuth.
* *
@ -118,44 +103,6 @@ public class PlayerAuth {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName); this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName);
} }
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt 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 salt, String ip, long lastLogin, double x, double y,
double z, String world, String email, String realName) {
this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin,
x, y, z, world, email, realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt String
* @param groupId int
* @param ip String
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip,
long lastLogin, String realName) {
this(nickname, new HashedPassword(hash, salt), groupId, ip, lastLogin,
0, 0, 0, "world", "your@email.com", realName);
}
/** /**
* Constructor for PlayerAuth. * Constructor for PlayerAuth.
* *
@ -171,8 +118,8 @@ public class PlayerAuth {
* @param email String * @param email String
* @param realName String * @param realName String
*/ */
public PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) { double x, double y, double z, String world, String email, String realName) {
this.nickname = nickname.toLowerCase(); this.nickname = nickname.toLowerCase();
this.password = password; this.password = password;
this.ip = ip; this.ip = ip;

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.settings.Spawn;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
@ -20,7 +21,11 @@ public class ReloadCommand implements ExecutableCommand {
try { try {
commandService.getSettings().reload(); commandService.getSettings().reload();
commandService.reloadMessages(commandService.getSettings().getMessagesFile()); commandService.reloadMessages(commandService.getSettings().getMessagesFile());
plugin.setupDatabase(); 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());
commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting"); sender.sendMessage("Error occurred during reload of AuthMe: aborting");

View File

@ -179,8 +179,8 @@ public class CacheDataSource implements DataSource {
} }
@Override @Override
public synchronized List<String> getAllAuthsByEmail(final String email) { public synchronized int countAuthsByEmail(final String email) {
return source.getAllAuthsByEmail(email); return source.countAuthsByEmail(email);
} }
@Override @Override

View File

@ -14,7 +14,6 @@ public interface DataSource {
* Return whether there is a record for the given username. * Return whether there is a record for the given username.
* *
* @param user The username to look up * @param user The username to look up
*
* @return True if there is a record, false otherwise * @return True if there is a record, false otherwise
*/ */
boolean isAuthAvailable(String user); boolean isAuthAvailable(String user);
@ -23,7 +22,6 @@ public interface DataSource {
* Return the hashed password of the player. * Return the hashed password of the player.
* *
* @param user The user whose password should be retrieve * @param user The user whose password should be retrieve
*
* @return The password hash of the player * @return The password hash of the player
*/ */
HashedPassword getPassword(String user); HashedPassword getPassword(String user);
@ -32,7 +30,6 @@ public interface DataSource {
* Retrieve the entire PlayerAuth object associated with the username. * Retrieve the entire PlayerAuth object associated with the username.
* *
* @param user The user to retrieve * @param user The user to retrieve
*
* @return The PlayerAuth object for the given username * @return The PlayerAuth object for the given username
*/ */
PlayerAuth getAuth(String user); PlayerAuth getAuth(String user);
@ -41,7 +38,6 @@ public interface DataSource {
* Save a new PlayerAuth object. * Save a new PlayerAuth object.
* *
* @param auth The new PlayerAuth to persist * @param auth The new PlayerAuth to persist
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean saveAuth(PlayerAuth auth); boolean saveAuth(PlayerAuth auth);
@ -50,7 +46,6 @@ public interface DataSource {
* Update the session of a record (IP, last login, real name). * Update the session of a record (IP, last login, real name).
* *
* @param auth The PlayerAuth object to update in the database * @param auth The PlayerAuth object to update in the database
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateSession(PlayerAuth auth); boolean updateSession(PlayerAuth auth);
@ -59,7 +54,6 @@ public interface DataSource {
* Update the password of the given PlayerAuth object. * Update the password of the given PlayerAuth object.
* *
* @param auth The PlayerAuth whose password should be updated * @param auth The PlayerAuth whose password should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updatePassword(PlayerAuth auth); boolean updatePassword(PlayerAuth auth);
@ -67,9 +61,8 @@ public interface DataSource {
/** /**
* Update the password of the given player. * Update the password of the given player.
* *
* @param user The user whose password should be updated * @param user The user whose password should be updated
* @param password The new password * @param password The new password
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updatePassword(String user, HashedPassword password); boolean updatePassword(String user, HashedPassword password);
@ -79,7 +72,6 @@ public interface DataSource {
* the given time. * the given time.
* *
* @param until The minimum last login * @param until The minimum last login
*
* @return The account names that have been removed * @return The account names that have been removed
*/ */
List<String> autoPurgeDatabase(long until); List<String> autoPurgeDatabase(long until);
@ -88,7 +80,6 @@ public interface DataSource {
* Remove a user record from the database. * Remove a user record from the database.
* *
* @param user The user to remove * @param user The user to remove
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean removeAuth(String user); boolean removeAuth(String user);
@ -97,7 +88,6 @@ public interface DataSource {
* Update the quit location of a PlayerAuth. * Update the quit location of a PlayerAuth.
* *
* @param auth The entry whose quit location should be updated * @param auth The entry whose quit location should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateQuitLoc(PlayerAuth auth); boolean updateQuitLoc(PlayerAuth auth);
@ -106,25 +96,22 @@ public interface DataSource {
* Return all usernames associated with the given IP address. * Return all usernames associated with the given IP address.
* *
* @param ip The IP address to look up * @param ip The IP address to look up
*
* @return Usernames associated with the given IP address * @return Usernames associated with the given IP address
*/ */
List<String> getAllAuthsByIp(String ip); List<String> getAllAuthsByIp(String ip);
/** /**
* Return all usernames associated with the given email address. * Return the number of accounts associated with the given email address.
* *
* @param email The email address to look up * @param email The email address to look up
* * @return Number of accounts using the given email address
* @return Users using the given email address
*/ */
List<String> getAllAuthsByEmail(String email); int countAuthsByEmail(String email);
/** /**
* Update the email of the PlayerAuth in the data source. * Update the email of the PlayerAuth in the data source.
* *
* @param auth The PlayerAuth whose email should be updated * @param auth The PlayerAuth whose email should be updated
*
* @return True upon success, false upon failure * @return True upon success, false upon failure
*/ */
boolean updateEmail(PlayerAuth auth); boolean updateEmail(PlayerAuth auth);

View File

@ -7,6 +7,7 @@ public enum DataSourceType {
MYSQL, MYSQL,
@Deprecated
FILE, FILE,
SQLITE SQLITE

View File

@ -487,25 +487,21 @@ public class FlatFile implements DataSource {
} }
@Override @Override
public List<String> getAllAuthsByEmail(String email) { public int countAuthsByEmail(String email) {
BufferedReader br = null; BufferedReader br = null;
List<String> countEmail = new ArrayList<>(); int countEmail = 0;
try { try {
br = new BufferedReader(new FileReader(source)); br = new BufferedReader(new FileReader(source));
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args.length > 8 && args[8].equals(email)) { if (args.length > 8 && args[8].equals(email)) {
countEmail.add(args[0]); ++countEmail;
} }
} }
return countEmail; return countEmail;
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} finally { } finally {
if (br != null) { if (br != null) {
try { try {
@ -514,6 +510,7 @@ public class FlatFile implements DataSource {
} }
} }
} }
return 0;
} }
@Override @Override

View File

@ -22,7 +22,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -82,6 +81,19 @@ public class MySQL implements DataSource {
} }
} }
MySQL(NewSetting settings, HikariDataSource hikariDataSource) {
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
this.password = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
this.col = new Columns(settings);
this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
ds = hikariDataSource;
}
private synchronized void setConnectionArguments() throws RuntimeException { private synchronized void setConnectionArguments() throws RuntimeException {
ds = new HikariDataSource(); ds = new HikariDataSource();
ds.setPoolName("AuthMeMYSQLPool"); ds.setPoolName("AuthMeMYSQLPool");
@ -131,7 +143,7 @@ public class MySQL implements DataSource {
+ col.REAL_NAME + " VARCHAR(255) NOT NULL," + col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ col.PASSWORD + " VARCHAR(255) NOT NULL," + col.PASSWORD + " VARCHAR(255) NOT NULL,"
+ col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1'," + col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1',"
+ col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp," + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0,"
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0',"
@ -182,9 +194,9 @@ public class MySQL implements DataSource {
rs = md.getColumns(null, null, tableName, col.LAST_LOGIN); rs = md.getColumns(null, null, tableName, col.LAST_LOGIN);
if (!rs.next()) { if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp;"); + " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;");
} else { } else {
migrateLastLoginColumnToTimestamp(con, rs); migrateLastLoginColumnToBigInt(con, rs);
} }
rs.close(); rs.close();
@ -234,66 +246,72 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean isAuthAvailable(String user) { public synchronized boolean isAuthAvailable(String user) {
try (Connection con = getConnection()) { String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; ResultSet rs = null;
PreparedStatement pst = con.prepareStatement(sql); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); rs = pst.executeQuery();
return rs.next(); return rs.next();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
} }
return false; return false;
} }
@Override @Override
public HashedPassword getPassword(String user) { public HashedPassword getPassword(String user) {
try (Connection con = getConnection()) { String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName
String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
+ " WHERE " + col.NAME + "=?;"; ResultSet rs = null;
PreparedStatement pst = con.prepareStatement(sql); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); rs = pst.executeQuery();
if (rs.next()) { if (rs.next()) {
return new HashedPassword(rs.getString(col.PASSWORD), return new HashedPassword(rs.getString(col.PASSWORD),
!col.SALT.isEmpty() ? rs.getString(col.SALT) : null); !col.SALT.isEmpty() ? rs.getString(col.SALT) : null);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
} }
return null; return null;
} }
@Override @Override
public synchronized PlayerAuth getAuth(String user) { public synchronized PlayerAuth getAuth(String user) {
PlayerAuth pAuth; String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection()) { PlayerAuth auth;
String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); int id;
if (!rs.next()) { try (ResultSet rs = pst.executeQuery()) {
return null; if (!rs.next()) {
return null;
}
id = rs.getInt(col.ID);
auth = buildAuthFromResultSet(rs);
} }
int id = rs.getInt(col.ID);
pAuth = buildAuthFromResultSet(rs);
rs.close();
pst.close();
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;"); try (PreparedStatement pst2 = con.prepareStatement(
pst.setInt(1, id); "SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
rs = pst.executeQuery(); pst2.setInt(1, id);
if (rs.next()) { try (ResultSet rs = pst2.executeQuery()) {
Blob blob = rs.getBlob("data"); if (rs.next()) {
byte[] bytes = blob.getBytes(1, (int) blob.length()); Blob blob = rs.getBlob("data");
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); byte[] bytes = blob.getBytes(1, (int) blob.length());
auth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
}
} }
} }
return auth;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
return null;
} }
return pAuth; return null;
} }
@Override @Override
@ -314,7 +332,7 @@ public class MySQL implements DataSource {
pst.setString(1, auth.getNickname()); pst.setString(1, auth.getNickname());
pst.setString(2, auth.getPassword().getHash()); pst.setString(2, auth.getPassword().getHash());
pst.setString(3, auth.getIp()); pst.setString(3, auth.getIp());
pst.setTimestamp(4, new Timestamp(auth.getLastLogin())); pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getRealName()); pst.setString(5, auth.getRealName());
pst.setString(6, auth.getEmail()); pst.setString(6, auth.getEmail());
if (useSalt) { if (useSalt) {
@ -566,7 +584,7 @@ public class MySQL implements DataSource {
+ col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; + col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, auth.getIp()); pst.setString(1, auth.getIp());
pst.setTimestamp(2, new Timestamp(auth.getLastLogin())); pst.setLong(2, auth.getLastLogin());
pst.setString(3, auth.getRealName()); pst.setString(3, auth.getRealName());
pst.setString(4, auth.getNickname()); pst.setString(4, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
@ -580,20 +598,19 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized List<String> autoPurgeDatabase(long until) { public synchronized List<String> autoPurgeDatabase(long until) {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
try (Connection con = getConnection()) { String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
PreparedStatement st = con.prepareStatement(sql); try (Connection con = getConnection();
st.setLong(1, until); PreparedStatement selectPst = con.prepareStatement(select);
ResultSet rs = st.executeQuery(); PreparedStatement deletePst = con.prepareStatement(delete)) {
while (rs.next()) { selectPst.setLong(1, until);
list.add(rs.getString(col.NAME)); try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(col.NAME));
}
} }
rs.close(); deletePst.setLong(1, until);
sql = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; deletePst.executeUpdate();
st = con.prepareStatement(sql);
st.setLong(1, until);
st.executeUpdate();
st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -634,18 +651,16 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean updateQuitLoc(PlayerAuth auth) { public synchronized boolean updateQuitLoc(PlayerAuth auth) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName
String sql = "UPDATE " + tableName + " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?"
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?" + " WHERE " + col.NAME + "=?;";
+ " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setDouble(1, auth.getQuitLocX()); pst.setDouble(1, auth.getQuitLocX());
pst.setDouble(2, auth.getQuitLocY()); pst.setDouble(2, auth.getQuitLocY());
pst.setDouble(3, auth.getQuitLocZ()); pst.setDouble(3, auth.getQuitLocZ());
pst.setString(4, auth.getWorld()); pst.setString(4, auth.getWorld());
pst.setString(5, auth.getNickname()); pst.setString(5, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
return true; return true;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
@ -655,13 +670,11 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean updateEmail(PlayerAuth auth) { public synchronized boolean updateEmail(PlayerAuth auth) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, auth.getEmail()); pst.setString(1, auth.getEmail());
pst.setString(2, auth.getNickname()); pst.setString(2, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
return true; return true;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
@ -690,16 +703,14 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized List<String> getAllAuthsByIp(String ip) { public synchronized List<String> getAllAuthsByIp(String ip) {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
try (Connection con = getConnection()) { String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, ip); pst.setString(1, ip);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { while (rs.next()) {
result.add(rs.getString(col.NAME)); result.add(rs.getString(col.NAME));
}
} }
rs.close();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -707,33 +718,29 @@ public class MySQL implements DataSource {
} }
@Override @Override
public synchronized List<String> getAllAuthsByEmail(String email) { public synchronized int countAuthsByEmail(String email) {
List<String> countEmail = new ArrayList<>(); String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = getConnection()) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.EMAIL + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, email); pst.setString(1, email);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { if (rs.next()) {
countEmail.add(rs.getString(col.NAME)); return rs.getInt(1);
}
} }
rs.close();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
return countEmail; return 0;
} }
@Override @Override
public synchronized void purgeBanned(List<String> banned) { public synchronized void purgeBanned(List<String> banned) {
try (Connection con = getConnection()) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) { for (String name : banned) {
pst.setString(1, name); pst.setString(1, name);
pst.executeUpdate(); pst.executeUpdate();
} }
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -746,28 +753,25 @@ public class MySQL implements DataSource {
@Override @Override
public boolean isLogged(String user) { public boolean isLogged(String user) {
boolean isLogged = false; String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection()) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, user); pst.setString(1, user);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
isLogged = rs.next() && (rs.getInt(col.IS_LOGGED) == 1); return rs.next() && (rs.getInt(col.IS_LOGGED) == 1);
}
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
return isLogged; return false;
} }
@Override @Override
public void setLogged(String user) { public void setLogged(String user) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 1); pst.setInt(1, 1);
pst.setString(2, user.toLowerCase()); pst.setString(2, user.toLowerCase());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -775,13 +779,11 @@ public class MySQL implements DataSource {
@Override @Override
public void setUnlogged(String user) { public void setUnlogged(String user) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 0); pst.setInt(1, 0);
pst.setString(2, user.toLowerCase()); pst.setString(2, user.toLowerCase());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -789,13 +791,11 @@ public class MySQL implements DataSource {
@Override @Override
public void purgeLogged() { public void purgeLogged() {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 0); pst.setInt(1, 0);
pst.setInt(2, 1); pst.setInt(2, 1);
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -804,14 +804,13 @@ public class MySQL implements DataSource {
@Override @Override
public int getAccountsRegistered() { public int getAccountsRegistered() {
int result = 0; int result = 0;
try (Connection con = getConnection()) { String sql = "SELECT COUNT(*) FROM " + tableName;
Statement st = con.createStatement(); try (Connection con = getConnection();
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + tableName); Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql)) {
if (rs.next()) { if (rs.next()) {
result = rs.getInt(1); result = rs.getInt(1);
} }
rs.close();
st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -820,9 +819,8 @@ public class MySQL implements DataSource {
@Override @Override
public void updateName(String oldOne, String newOne) { public void updateName(String oldOne, String newOne) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, newOne); pst.setString(1, newOne);
pst.setString(2, oldOne); pst.setString(2, oldOne);
pst.executeUpdate(); pst.executeUpdate();
@ -833,9 +831,8 @@ public class MySQL implements DataSource {
@Override @Override
public boolean updateRealName(String user, String realName) { public boolean updateRealName(String user, String realName) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, realName); pst.setString(1, realName);
pst.setString(2, user); pst.setString(2, user);
pst.executeUpdate(); pst.executeUpdate();
@ -848,9 +845,8 @@ public class MySQL implements DataSource {
@Override @Override
public boolean updateIp(String user, String ip) { public boolean updateIp(String user, String ip) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, ip); pst.setString(1, ip);
pst.setString(2, user); pst.setString(2, user);
pst.executeUpdate(); pst.executeUpdate();
@ -867,23 +863,23 @@ public class MySQL implements DataSource {
try (Connection con = getConnection()) { try (Connection con = getConnection()) {
Statement st = con.createStatement(); Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM " + tableName); ResultSet rs = st.executeQuery("SELECT * FROM " + tableName);
PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;");
while (rs.next()) { while (rs.next()) {
PlayerAuth pAuth = buildAuthFromResultSet(rs); PlayerAuth pAuth = buildAuthFromResultSet(rs);
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
int id = rs.getInt(col.ID); try (PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
pst.setInt(1, id); int id = rs.getInt(col.ID);
ResultSet rs2 = pst.executeQuery(); pst.setInt(1, id);
if (rs2.next()) { ResultSet rs2 = pst.executeQuery();
Blob blob = rs2.getBlob("data"); if (rs2.next()) {
byte[] bytes = blob.getBytes(1, (int) blob.length()); Blob blob = rs2.getBlob("data");
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
rs2.close();
} }
rs2.close();
} }
auths.add(pAuth); auths.add(pAuth);
} }
pst.close();
rs.close(); rs.close();
st.close(); st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
@ -922,12 +918,12 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean isEmailStored(String email) { public synchronized boolean isEmailStored(String email) {
String sql = "SELECT 1 FROM " + tableName + " WHERE " + col.EMAIL + " = ?"; String sql = "SELECT 1 FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = ds.getConnection()) { try (Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, email); pst.setString(1, email);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
return rs.next(); return rs.next();
}
} catch (SQLException e) { } catch (SQLException e) {
logSqlException(e); logSqlException(e);
} }
@ -941,7 +937,7 @@ public class MySQL implements DataSource {
.name(row.getString(col.NAME)) .name(row.getString(col.NAME))
.realName(row.getString(col.REAL_NAME)) .realName(row.getString(col.REAL_NAME))
.password(row.getString(col.PASSWORD), salt) .password(row.getString(col.PASSWORD), salt)
.lastLogin(safeGetTimestamp(row)) .lastLogin(row.getLong(col.LAST_LOGIN))
.ip(row.getString(col.IP)) .ip(row.getString(col.IP))
.locWorld(row.getString(col.LASTLOC_WORLD)) .locWorld(row.getString(col.LASTLOC_WORLD))
.locX(row.getDouble(col.LASTLOC_X)) .locX(row.getDouble(col.LASTLOC_X))
@ -953,24 +949,15 @@ public class MySQL implements DataSource {
} }
/** /**
* Retrieve the last login timestamp in a safe way. * Check if the lastlogin column is of type timestamp and, if so, revert it to the bigint format.
* *
* @param row The ResultSet to read * @param con Connection to the database
* @return The timestamp (as number of milliseconds since 1970-01-01 00:00:00 GMT) * @param rs ResultSet containing meta data for the lastlogin column
*/ */
private long safeGetTimestamp(ResultSet row) { private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException {
try {
return row.getTimestamp(col.LAST_LOGIN).getTime();
} catch (SQLException e) {
ConsoleLogger.logException("Could not get timestamp from resultSet. Defaulting to current time", e);
}
return System.currentTimeMillis();
}
private void migrateLastLoginColumnToTimestamp(Connection con, ResultSet rs) throws SQLException {
final int columnType = rs.getInt("DATA_TYPE"); final int columnType = rs.getInt("DATA_TYPE");
if (columnType == Types.BIGINT) { if (columnType == Types.TIMESTAMP) {
ConsoleLogger.info("Migrating lastlogin column from bigint to timestamp"); ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
final String lastLoginOld = col.LAST_LOGIN + "_old"; final String lastLoginOld = col.LAST_LOGIN + "_old";
// Rename lastlogin to lastlogin_old // Rename lastlogin to lastlogin_old
@ -981,12 +968,12 @@ public class MySQL implements DataSource {
// Create lastlogin column // Create lastlogin column
sql = String.format("ALTER TABLE %s ADD COLUMN %s " sql = String.format("ALTER TABLE %s ADD COLUMN %s "
+ "TIMESTAMP NOT NULL DEFAULT current_timestamp AFTER %s", + "BIGINT NOT NULL DEFAULT 0 AFTER %s",
tableName, col.LAST_LOGIN, col.IP); tableName, col.LAST_LOGIN, col.IP);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
// Set values of lastlogin based on lastlogin_old // Set values of lastlogin based on lastlogin_old
sql = String.format("UPDATE %s SET %s = FROM_UNIXTIME(%s)", sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s)",
tableName, col.LAST_LOGIN, lastLoginOld); tableName, col.LAST_LOGIN, lastLoginOld);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
@ -994,7 +981,7 @@ public class MySQL implements DataSource {
sql = String.format("ALTER TABLE %s DROP COLUMN %s", sql = String.format("ALTER TABLE %s DROP COLUMN %s",
tableName, lastLoginOld); tableName, lastLoginOld);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
ConsoleLogger.info("Finished migration of lastlogin (bigint to timestamp)"); ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
} }
} }
@ -1002,4 +989,14 @@ public class MySQL implements DataSource {
ConsoleLogger.logException("Error during SQL operation:", e); ConsoleLogger.logException("Error during SQL operation:", e);
} }
private static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
ConsoleLogger.logException("Could not close ResultSet", e);
}
}
}
} }

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
@ -46,6 +47,14 @@ public class SQLite implements DataSource {
} }
} }
@VisibleForTesting
SQLite(NewSetting settings, Connection connection) {
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings);
this.con = connection;
}
private synchronized void connect() throws ClassNotFoundException, SQLException { private synchronized void connect() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
ConsoleLogger.info("SQLite driver loaded"); ConsoleLogger.info("SQLite driver loaded");
@ -341,7 +350,8 @@ public class SQLite implements DataSource {
@Override @Override
public synchronized void close() { public synchronized void close() {
try { try {
con.close(); if (con != null && !con.isClosed())
con.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -394,25 +404,19 @@ public class SQLite implements DataSource {
} }
@Override @Override
public List<String> getAllAuthsByEmail(String email) { public int countAuthsByEmail(String email) {
PreparedStatement pst = null; String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;";
ResultSet rs = null; try (PreparedStatement pst = con.prepareStatement(sql)) {
List<String> countEmail = new ArrayList<>();
try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.EMAIL + "=?;");
pst.setString(1, email); pst.setString(1, email);
rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { if (rs.next()) {
countEmail.add(rs.getString(col.NAME)); return rs.getInt(1);
}
} }
return countEmail;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
close(pst);
} }
return new ArrayList<>(); return 0;
} }
@Override @Override

View File

@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -243,9 +242,7 @@ public class AuthMePlayerListener implements Listener {
String realName = auth.getRealName(); String realName = auth.getRealName();
if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) { if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CASE, realName, event.getName()));
event.setKickMessage("You should join using username: " + ChatColor.AQUA + realName +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName());
return; return;
} }
if (realName.isEmpty() || realName.equals("Player")) { if (realName.isEmpty() || realName.equals("Player")) {

View File

@ -127,7 +127,9 @@ public enum MessageKey {
TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"), TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"),
NOT_OWNER_ERROR("not_owner_error"); NOT_OWNER_ERROR("not_owner_error"),
INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid");
private String key; private String key;
private String[] tags; private String[] tags;

View File

@ -8,6 +8,8 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
/** /**
* Class for retrieving and sending translatable messages to players. * Class for retrieving and sending translatable messages to players.
@ -16,7 +18,7 @@ public class Messages {
private FileConfiguration configuration; private FileConfiguration configuration;
private String fileName; private String fileName;
private final File defaultFile; private final String defaultFile;
private FileConfiguration defaultConfiguration; private FileConfiguration defaultConfiguration;
/** /**
@ -25,7 +27,7 @@ public class Messages {
* @param messageFile The messages file to use * @param messageFile The messages file to use
* @param defaultFile The file with messages to use as default if missing * @param defaultFile The file with messages to use as default if missing
*/ */
public Messages(File messageFile, File defaultFile) { public Messages(File messageFile, String defaultFile) {
initializeFile(messageFile); initializeFile(messageFile);
this.defaultFile = defaultFile; this.defaultFile = defaultFile;
} }
@ -53,17 +55,7 @@ public class Messages {
* @param replacements The replacements to apply for the tags * @param replacements The replacements to apply for the tags
*/ */
public void send(CommandSender sender, MessageKey key, String... replacements) { public void send(CommandSender sender, MessageKey key, String... replacements) {
String message = retrieveSingle(key); String message = retrieveSingle(key, replacements);
String[] tags = key.getTags();
if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
} else {
ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'");
send(sender, key);
}
for (String line : message.split("\n")) { for (String line : message.split("\n")) {
sender.sendMessage(line); sender.sendMessage(line);
} }
@ -97,6 +89,27 @@ public class Messages {
return StringUtils.join("\n", retrieve(key)); return StringUtils.join("\n", retrieve(key));
} }
/**
* Retrieve the given message code with the given tag replacements. Note that this method
* logs an error if the number of supplied replacements doesn't correspond to the number of tags
* the message key contains.
*
* @param key The key of the message to send
* @param replacements The replacements to apply for the tags
*/
public String retrieveSingle(MessageKey key, String... replacements) {
String message = retrieveSingle(key);
String[] tags = key.getTags();
if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
} else {
ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'");
}
return message;
}
/** /**
* Reload the messages manager. * Reload the messages manager.
* *
@ -117,7 +130,8 @@ public class Messages {
} }
if (defaultConfiguration == null) { if (defaultConfiguration == null) {
defaultConfiguration = YamlConfiguration.loadConfiguration(defaultFile); InputStream stream = Messages.class.getResourceAsStream(defaultFile);
defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream));
} }
String message = defaultConfiguration.getString(code); String message = defaultConfiguration.getString(code);
return message == null ? getDefaultErrorMessage(code) : message; return message == null ? getDefaultErrorMessage(code) : message;

View File

@ -64,15 +64,16 @@ public class AsynchronousJoin {
final String ip = plugin.getIP(player); final String ip = plugin.getIP(player);
if (Settings.isAllowRestrictedIp && !isNameRestricted(name, ip, player.getAddress().getHostName())) { if (Settings.isAllowRestrictedIp && isNameRestricted(name, ip, player.getAddress().getHostName())) {
sched.scheduleSyncDelayedTask(plugin, new Runnable() { sched.scheduleSyncDelayedTask(plugin, new Runnable() {
@Override @Override
public void run() { public void run() {
AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true); AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR)); player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR));
if (Settings.banUnsafeIp) if (Settings.banUnsafeIp) {
plugin.getServer().banIP(ip); plugin.getServer().banIP(ip);
}
} }
}); });
return; return;

View File

@ -220,7 +220,7 @@ public class AsynchronousLogin {
} }
} }
public void displayOtherAccounts(PlayerAuth auth) { private void displayOtherAccounts(PlayerAuth auth) {
if (!Settings.displayOtherAccounts || auth == null) { if (!Settings.displayOtherAccounts || auth == null) {
return; return;
} }

View File

@ -2,6 +2,8 @@ package fr.xephi.authme.process.login;
import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.HooksSettings; import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -92,7 +94,7 @@ public class ProcessSyncPlayerLogin implements Runnable {
} }
private void restoreSpeedEffects() { private void restoreSpeedEffects() {
if (Settings.isRemoveSpeedEnabled) { if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
player.setWalkSpeed(0.2F); player.setWalkSpeed(0.2F);
player.setFlySpeed(0.1F); player.setFlySpeed(0.1F);
} }
@ -173,19 +175,19 @@ public class ProcessSyncPlayerLogin implements Runnable {
} }
restoreSpeedEffects(); restoreSpeedEffects();
if (Settings.applyBlindEffect) { if (settings.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
player.removePotionEffect(PotionEffectType.BLINDNESS); player.removePotionEffect(PotionEffectType.BLINDNESS);
} }
// The Login event now fires (as intended) after everything is processed // The Login event now fires (as intended) after everything is processed
Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player)); Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player));
player.saveData(); player.saveData();
if (Settings.bungee) { if (settings.getProperty(HooksSettings.BUNGEECORD)) {
sendBungeeMessage(); sendBungeeMessage();
} }
// Login is finish, display welcome message if we use email registration // Login is done, display welcome message
if (Settings.useWelcomeMessage && Settings.emailRegistration) { if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) {
if (Settings.broadcastWelcomeMessage) { if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) {
for (String s : settings.getWelcomeMessage()) { for (String s : settings.getWelcomeMessage()) {
Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player));
} }

View File

@ -98,7 +98,7 @@ public class AsyncRegister {
private void emailRegister() { private void emailRegister() {
if (Settings.getmaxRegPerEmail > 0 if (Settings.getmaxRegPerEmail > 0
&& !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
&& database.getAllAuthsByEmail(email).size() >= Settings.getmaxRegPerEmail) { && database.countAuthsByEmail(email) >= Settings.getmaxRegPerEmail) {
m.send(player, MessageKey.MAX_REGISTER_EXCEEDED); m.send(player, MessageKey.MAX_REGISTER_EXCEEDED);
return; return;
} }

View File

@ -113,7 +113,7 @@ public class PasswordSecurity {
*/ */
private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) { private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) {
try { try {
return HashAlgorithm.CUSTOM.equals(algorithm) return HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)
? null ? null
: algorithm.getClazz().newInstance(); : algorithm.getClazz().newInstance();
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {

View File

@ -11,7 +11,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
* and store the salt with the hash itself. * and store the salt with the hash itself.
*/ */
@Recommendation(Usage.ACCEPTABLE) @Recommendation(Usage.ACCEPTABLE)
@HasSalt(SaltType.TEXT) // See saltLength() for length @HasSalt(SaltType.TEXT) // See getSaltLength() for length
public abstract class HexSaltedMethod implements EncryptionMethod { public abstract class HexSaltedMethod implements EncryptionMethod {
public abstract int getSaltLength(); public abstract int getSaltLength();

View File

@ -82,4 +82,13 @@ public abstract class CustomConfiguration extends YamlConfiguration {
} }
return false; return false;
} }
public boolean containsAll(String... paths) {
for (String path : paths) {
if (!contains(path)) {
return false;
}
}
return true;
}
} }

View File

@ -18,7 +18,6 @@ import org.yaml.snakeyaml.Yaml;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -110,18 +109,12 @@ public class NewSetting {
} }
/** /**
* Return the default messages file within the JAR that should contain all messages. * Return the path to the default messages file within the JAR.
* *
* @return The default messages file, or {@code null} if it could not be retrieved * @return The default messages file path
*/ */
public File getDefaultMessagesFile() { public String getDefaultMessagesFile() {
String defaultFilePath = "/messages/messages_en.yml"; return "/messages/messages_en.yml";
URL url = NewSetting.class.getResource(defaultFilePath);
if (url == null) {
return null;
}
File file = new File(url.getFile());
return file.exists() ? file : null;
} }
public String getEmailMessage() { public String getEmailMessage() {

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; 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.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
@ -144,8 +145,6 @@ public final class Settings {
denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN); hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN);
plugin.checkProtocolLib();
passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH); passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH);
backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true); isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true);
@ -176,7 +175,7 @@ public final class Settings {
emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false); emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false);
saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8); saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8);
getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1); getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1);
multiverse = configFile.getBoolean("Hooks.multiverse", true); multiverse = load(HooksSettings.MULTIVERSE);
bungee = configFile.getBoolean("Hooks.bungeecord", false); bungee = configFile.getBoolean("Hooks.bungeecord", false);
getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds"); getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds");
banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false); banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false);
@ -213,7 +212,7 @@ public final class Settings {
broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false); broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false);
forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false); forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false);
forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false); forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false);
spawnPriority = configFile.getString("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default"); spawnPriority = load(RestrictionSettings.SPAWN_PRIORITY);
getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0);
getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0);
checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false);

View File

@ -1,7 +1,13 @@
package fr.xephi.authme.settings; 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.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.io.File; import java.io.File;
@ -12,13 +18,17 @@ import java.io.File;
public class Spawn extends CustomConfiguration { public class Spawn extends CustomConfiguration {
private static Spawn spawn; private static Spawn spawn;
private static String[] spawnPriority;
public Spawn() { private Spawn() {
super(new File("." + File.separator + "plugins" + File.separator + "AuthMe" + File.separator + "spawn.yml")); super(new File(Settings.PLUGIN_FOLDER, "spawn.yml"));
spawn = this;
load(); load();
save(); save();
saveDefault(); spawnPriority = Settings.spawnPriority.split(",");
}
public static void reload() {
spawn = new Spawn();
} }
/** /**
@ -33,111 +43,106 @@ public class Spawn extends CustomConfiguration {
return spawn; return spawn;
} }
private void saveDefault() {
if (!contains("spawn")) {
set("spawn.world", "");
set("spawn.x", "");
set("spawn.y", "");
set("spawn.z", "");
set("spawn.yaw", "");
set("spawn.pitch", "");
save();
}
if (!contains("firstspawn")) {
set("firstspawn.world", "");
set("firstspawn.x", "");
set("firstspawn.y", "");
set("firstspawn.z", "");
set("firstspawn.yaw", "");
set("firstspawn.pitch", "");
save();
}
}
/**
* Method setSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setSpawn(Location location) { public boolean setSpawn(Location location) {
try { if (location == null || location.getWorld() == null) {
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;
} catch (NullPointerException npe) {
return false; 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;
} }
/**
* Method setFirstSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setFirstSpawn(Location location) { public boolean setFirstSpawn(Location location) {
try { if (location == null || location.getWorld() == null) {
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;
} catch (NullPointerException npe) {
return false; 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;
} }
/**
* Method getLocation.
*
* @return Location
*/
@Deprecated
public Location getLocation() {
return getSpawn();
}
/**
* Method getSpawn.
*
* @return Location
*/
public Location getSpawn() { public Location getSpawn() {
try { if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) {
if (this.getString("spawn.world").isEmpty() || this.getString("spawn.world").equals("")) String worldName = getString("spawn.world");
return null; World world = Bukkit.getWorld(worldName);
Location location = new Location(Bukkit.getWorld(this.getString("spawn.world")), this.getDouble("spawn.x"), this.getDouble("spawn.y"), this.getDouble("spawn.z"), Float.parseFloat(this.getString("spawn.yaw")), Float.parseFloat(this.getString("spawn.pitch"))); if (!StringUtils.isEmpty(worldName) && world != null) {
return location; return new Location(
} catch (NullPointerException | NumberFormatException npe) { world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"),
return null; Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch"))
);
}
} }
return null;
} }
/**
* Method getFirstSpawn.
*
* @return Location
*/
public Location getFirstSpawn() { public Location getFirstSpawn() {
try { if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y",
if (this.getString("firstspawn.world").isEmpty() || this.getString("firstspawn.world").equals("")) "firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) {
return null; String worldName = getString("firstspawn.world");
Location location = new Location(Bukkit.getWorld(this.getString("firstspawn.world")), this.getDouble("firstspawn.x"), this.getDouble("firstspawn.y"), this.getDouble("firstspawn.z"), Float.parseFloat(this.getString("firstspawn.yaw")), Float.parseFloat(this.getString("firstspawn.pitch"))); World world = Bukkit.getWorld(worldName);
return location; if (!StringUtils.isEmpty(worldName) && world != null) {
} catch (NullPointerException | NumberFormatException npe) { return new Location(
return null; 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
}
} }

View File

@ -20,7 +20,7 @@ public class BackupSettings implements SettingsClass {
public static final Property<Boolean> ON_SERVER_STOP = public static final Property<Boolean> ON_SERVER_STOP =
newProperty("BackupSystem.OnServerStop", true); newProperty("BackupSystem.OnServerStop", true);
@Comment(" Windows only mysql installation Path") @Comment("Windows only mysql installation Path")
public static final Property<String> MYSQL_WINDOWS_PATH = public static final Property<String> MYSQL_WINDOWS_PATH =
newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");

View File

@ -0,0 +1,72 @@
package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger;
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.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import java.util.List;
/**
* Migrations to perform during the initialization of AuthMe.
*/
public final class MigrationService {
private MigrationService() {
}
/**
* Hash all passwords to SHA256 and updated the setting if the password hash is set to the deprecated PLAINTEXT.
*
* @param settings The settings instance
* @param dataSource The data source
* @param authmeSha256 Instance to the AuthMe SHA256 encryption method implementation
*/
public static void changePlainTextToSha256(NewSetting settings, DataSource dataSource,
SHA256 authmeSha256) {
if (HashAlgorithm.PLAINTEXT == settings.getProperty(SecuritySettings.PASSWORD_HASH)) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated;"
+ " it will be changed and hashed now to the AuthMe default hashing method");
List<PlayerAuth> allAuths = dataSource.getAllAuths();
for (PlayerAuth auth : allAuths) {
HashedPassword hashedPassword = authmeSha256.computeHash(
auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(hashedPassword);
dataSource.updatePassword(auth);
}
settings.setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256);
settings.save();
ConsoleLogger.info("Migrated " + allAuths.size() + " accounts from plaintext to SHA256");
}
}
/**
* Converts the data source from the deprecated FLATFILE type to SQLITE.
*
* @param settings The settings instance
* @param dataSource The data source
* @return The converted datasource (SQLite), or null if no migration was necessary
*/
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;
}
}
return null;
}
}

View File

@ -58,6 +58,7 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично изключ
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -44,7 +44,7 @@ usage_captcha: '&3Para logar você deve resolver o captcha, por favor use o coma
wrong_captcha: '&cCaptcha inválido, por favor escreva "/captcha THE_CAPTCHA"' wrong_captcha: '&cCaptcha inválido, por favor escreva "/captcha THE_CAPTCHA"'
valid_captcha: '&2Código do captcha correto!' valid_captcha: '&2Código do captcha correto!'
kick_forvip: '&3Um vip entrou no servidor!' kick_forvip: '&3Um vip entrou no servidor!'
kick_fullserver: '&4Servidor esta cheio! Para entrar mesmo cheio compre vip no site www.site.com.br' kick_fullserver: '&4Servidor esta cheio, tente outra vez mais tarde'
usage_email_add: '&cUse: /email add <email> <email>' usage_email_add: '&cUse: /email add <email> <email>'
usage_email_change: '&cUse: /email change <email antigo> <email novo>' usage_email_change: '&cUse: /email change <email antigo> <email novo>'
usage_email_recovery: '&cUse: /email recovery <email>' usage_email_recovery: '&cUse: /email recovery <email>'
@ -56,10 +56,10 @@ email_confirm: '&cPor favor confirme o email!'
email_changed: '&2Email mudado com sucesso!' email_changed: '&2Email mudado com sucesso!'
email_send: '&2Email de recuperação enviado com sucesso! !' email_send: '&2Email de recuperação enviado com sucesso! !'
email_exists: '&cUm email de recuperação já foi enviado! Você pode reenviar outro usando o comando:' email_exists: '&cUm email de recuperação já foi enviado! Você pode reenviar outro usando o comando:'
country_banned: '&4Seu país foi banido do servidor! Your country is banned from this server!' country_banned: '&4Seu país foi banido do servidor!'
antibot_auto_enabled: '&4[AntiBotService] AntiBot ativado devido ao grande número de conexões!' antibot_auto_enabled: '&4[AntiBotService] AntiBot ativado devido ao grande número de conexões!'
antibot_auto_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!' antibot_auto_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!'
# TODO two_factor_create: Missing tag %url two_factor_create: '&2Seu código secreto é %code. Você pode escanear ele daqui %url'
two_factor_create: '&2Seu código secreto é %code' email_already_used: '&4Este endereço de email já está em uso'
# TODO email_already_used: '&4The email address is already being used' not_owner_error: 'Você não é o dono desta conta. Por favor, tente outro nome!'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' invalid_name_case: 'Você deve entrar usando %valid, não %invalid.'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod automaticky ukoncen po %m minutach,
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,4 +60,5 @@ kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du di
# TODO two_factor_create: Missing tag %url # TODO two_factor_create: Missing tag %url
two_factor_create: '&2Dein geheimer Code ist %code' two_factor_create: '&2Dein geheimer Code ist %code'
# TODO email_already_used: '&4The email address is already being used' # 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!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,3 +60,4 @@ antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m mi
email_already_used: '&4The email address is already being used' 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' 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 try another name!'
invalid_name_case: 'You should join using username %valid, not %invalid.'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automáticamente luego d
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -52,6 +52,7 @@ country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'

View File

@ -55,6 +55,7 @@ email_send: '[AuthMe] Palautus sähköposti lähetetty!'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'

View File

@ -61,4 +61,5 @@ email_exists: '&cUn email de restauration a déjà été envoyé ! Vous pouvez l
# TODO two_factor_create: Missing tag %url # TODO two_factor_create: Missing tag %url
two_factor_create: '&2Votre code secret est %code' two_factor_create: '&2Votre code secret est %code'
# TODO email_already_used: '&4The email address is already being used' # 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!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,5 +60,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despo
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt a nagy számú
antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!' antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!'
kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.' kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -55,6 +55,7 @@ antibot_auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak
antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO usage_unreg: '&cUsage: /unregister <password>' # TODO usage_unreg: '&cUsage: /unregister <password>'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'

View File

@ -60,4 +60,5 @@ antibot_auto_disabled: "Il servizio di AntiBot è stato automaticamente disabili
kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.' kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.'
two_factor_create: '&2Il tuo codice segreto è: &f%code&n&2Puoi anche scannerizzare il codice QR da qui: &f%url' two_factor_create: '&2Il tuo codice segreto è: &f%code&n&2Puoi anche scannerizzare il codice QR da qui: &f%url'
# TODO email_already_used: '&4The email address is already being used' # 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!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -61,5 +61,6 @@ antibot_auto_disabled: '[AuthMe] 봇차단모드가 %m 분 후에 자동적으
# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' # TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -46,6 +46,7 @@ kick_fullserver: '&cServeris yra pilnas, Atsiprasome.'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO new_email_invalid: '&cInvalid new email, try again!' # TODO new_email_invalid: '&cInvalid new email, try again!'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' # TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>' # TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO email_confirm: '&cPlease confirm your email address!' # TODO email_confirm: '&cPlease confirm your email address!'

View File

@ -59,5 +59,6 @@ kick_antibot: 'AntiBot is aangezet! Wacht alsjeblieft enkele minuten voor je met
two_factor_create: '&2Je geheime code is %code' two_factor_create: '&2Je geheime code is %code'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO reg_email_msg: '&3Please, register to the server with the command "/register <email> <confirmEmail>"' # TODO reg_email_msg: '&3Please, register to the server with the command "/register <email> <confirmEmail>"'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -55,6 +55,7 @@ email_send: '[AuthMe] Email z odzyskaniem wyslany!'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automaticamente após %m
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '&a[AuthMe] AntiBot-режим автоматичски
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -38,28 +38,29 @@ name_len: '&cTvoje meno je velmi krátke alebo dlhé'
regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX' regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX'
add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"' add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"'
recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>' recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>'
# TODO email_already_used: '&4The email address is already being used'
# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO usage_email_change: '&cUsage: /email change <oldEmail> <newEmail>' # TODO usage_email_change: '&cUsage: /email change <oldEmail> <newEmail>'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO email_already_used: '&4The email address is already being used'
# TODO new_email_invalid: '&cInvalid new email, try again!' # TODO new_email_invalid: '&cInvalid new email, try again!'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO email_confirm: '&cPlease confirm your email address!'
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO email_changed: '&2Email address changed correctly!'
# TODO old_email_invalid: '&cInvalid old email, try again!' # TODO old_email_invalid: '&cInvalid old email, try again!'
# TODO email_changed: '&2Email address changed correctly!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'
# TODO kick_fullserver: '&4The server is full, try again later!'
# TODO email_added: '&2Email address successfully added to your account!'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO email_invalid: '&cInvalid email address, try again!'
# TODO kick_forvip: '&3A VIP player has joined the server when it was full!'
# TODO usage_email_add: '&cUsage: /email add <email> <confirmEmail>' # TODO usage_email_add: '&cUsage: /email add <email> <confirmEmail>'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!' # TODO valid_captcha: '&2Captcha code solved correctly!'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"'
# TODO email_confirm: '&cPlease confirm your email address!'
# TODO kick_fullserver: '&4The server is full, try again later!'
# TODO email_added: '&2Email address successfully added to your account!'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO kick_forvip: '&3A VIP player has joined the server when it was full!'
# TODO email_invalid: '&cInvalid email address, try again!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMode %m dakika sonra otomatik olarak isg
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично вимкну
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBot tự huỷ kích hoạt sau %m phút, h
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,5 +63,6 @@ antibot_auto_disabled: '&8[&6用戶系統&8] 防止機械人程序檢查到不
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,4 +63,5 @@ email_already_used: '&4邮箱已被使用'
kick_antibot: '[AuthMe] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器' kick_antibot: '[AuthMe] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器'
email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:' email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:'
two_factor_create: '&2你的代码是 %code你可以使用 %url 来扫描' two_factor_create: '&2你的代码是 %code你可以使用 %url 来扫描'
# 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!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,5 +63,6 @@ antibot_auto_enabled: '&b【AuthMe】&6AntiBotMod已自動啟用!'
antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉' antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -2,6 +2,8 @@ package fr.xephi.authme;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
/** /**
* AuthMe test utilities. * AuthMe test utilities.
@ -18,11 +20,31 @@ public final class TestHelper {
* @return The project file * @return The project file
*/ */
public static File getJarFile(String path) { public static File getJarFile(String path) {
URL url = getUrlOrThrow(path);
return new File(url.getFile());
}
/**
* Return a {@link Path} to a file in the JAR's resources (main or test).
*
* @param path The absolute path to the file
* @return The Path object to the file
*/
public static Path getJarPath(String path) {
String sqlFilePath = getUrlOrThrow(path).getPath();
// Windows preprends the path with a '/' or '\', which Paths cannot handle
String appropriatePath = System.getProperty("os.name").contains("indow")
? sqlFilePath.substring(1)
: sqlFilePath;
return Paths.get(appropriatePath);
}
private static URL getUrlOrThrow(String path) {
URL url = TestHelper.class.getResource(path); URL url = TestHelper.class.getResource(path);
if (url == null) { if (url == null) {
throw new IllegalStateException("File '" + path + "' could not be loaded"); throw new IllegalStateException("File '" + path + "' could not be loaded");
} }
return new File(url.getFile()); return url;
} }
} }

View File

@ -0,0 +1,204 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.junit.Test;
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 org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* Abstract class for data source integration tests.
*/
public abstract class AbstractDataSourceIntegrationTest {
protected abstract DataSource getDataSource();
@Test
public void shouldReturnIfAuthIsAvailableOrNot() {
// given
DataSource dataSource = getDataSource();
// 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 shouldReturnPassword() {
// given
DataSource dataSource = getDataSource();
// when
HashedPassword bobbyPassword = dataSource.getPassword("bobby");
HashedPassword invalidPassword = dataSource.getPassword("doesNotExist");
HashedPassword userPassword = dataSource.getPassword("user");
// then
assertThat(bobbyPassword, equalToHash("$SHA$11aa0706173d7272$dbba966"));
assertThat(invalidPassword, nullValue());
assertThat(userPassword, equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"));
}
@Test
public void shouldGetAuth() {
// given
DataSource dataSource = getDataSource();
// when
PlayerAuth invalidAuth = dataSource.getAuth("notInDB");
PlayerAuth bobbyAuth = dataSource.getAuth("Bobby");
PlayerAuth userAuth = dataSource.getAuth("user");
// then
assertThat(invalidAuth, nullValue());
assertThat(bobbyAuth, hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"));
assertThat(bobbyAuth, hasAuthLocation(1.05, 2.1, 4.2, "world"));
assertThat(bobbyAuth.getLastLogin(), equalTo(1449136800L));
assertThat(bobbyAuth.getPassword(), equalToHash("$SHA$11aa0706173d7272$dbba966"));
assertThat(userAuth, hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90"));
assertThat(userAuth, hasAuthLocation(124.1, 76.3, -127.8, "nether"));
assertThat(userAuth.getLastLogin(), equalTo(1453242857L));
assertThat(userAuth.getPassword(), equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"));
}
@Test
public void shouldFindIfEmailExists() {
// given
DataSource dataSource = getDataSource();
// when
boolean isUserMailPresent = dataSource.isEmailStored("user@example.org");
boolean isUserMailPresentCaseInsensitive = dataSource.isEmailStored("user@example.ORG");
boolean isInvalidMailPresent = dataSource.isEmailStored("not-in-database@example.com");
// then
assertThat(isUserMailPresent, equalTo(true));
assertThat(isUserMailPresentCaseInsensitive, equalTo(true));
assertThat(isInvalidMailPresent, equalTo(false));
}
@Test
public void shouldCountAuthsByEmail() {
// given
DataSource dataSource = getDataSource();
// when
int userMailCount = dataSource.countAuthsByEmail("user@example.ORG");
int invalidMailCount = dataSource.countAuthsByEmail("not.in.db@example.com");
boolean response = dataSource.saveAuth(
PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build());
int newUserCount = dataSource.countAuthsByEmail("user@Example.org");
// then
assertThat(userMailCount, equalTo(1));
assertThat(invalidMailCount, equalTo(0));
assertThat(response, equalTo(true));
assertThat(newUserCount, equalTo(2));
}
@Test
public void shouldReturnAllAuths() {
// given
DataSource dataSource = getDataSource();
// when
List<PlayerAuth> authList = dataSource.getAllAuths();
boolean response = dataSource.saveAuth(
PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build());
List<PlayerAuth> newAuthList = dataSource.getAllAuths();
// then
assertThat(response, equalTo(true));
assertThat(authList, hasSize(2));
assertThat(newAuthList, hasSize(3));
boolean hasBobby = false;
for (PlayerAuth auth : authList) {
if (auth.getNickname().equals("bobby")) {
hasBobby = true;
break;
}
}
assertThat(hasBobby, equalTo(true));
}
@Test
public void shouldUpdatePassword() {
// given
DataSource dataSource = getDataSource();
HashedPassword newHash = new HashedPassword("new_hash");
// when
boolean response1 = dataSource.updatePassword("user", newHash);
boolean response2 = dataSource.updatePassword("non-existent-name", new HashedPassword("sd"));
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getPassword("user"), equalToHash(newHash));
}
@Test
public void shouldRemovePlayerAuth() {
// given
DataSource dataSource = getDataSource();
// when
boolean response1 = dataSource.removeAuth("bobby");
boolean response2 = dataSource.removeAuth("does-not-exist");
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getAuth("bobby"), nullValue());
assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false));
}
@Test
public void shouldUpdateSession() {
// given
DataSource dataSource = getDataSource();
PlayerAuth bobby = PlayerAuth.builder()
.name("bobby").realName("BOBBY").lastLogin(123L)
.ip("12.12.12.12").build();
// when
boolean response = dataSource.updateSession(bobby);
// then
assertThat(response, equalTo(true));
PlayerAuth result = dataSource.getAuth("bobby");
assertThat(result, hasAuthBasicData("bobby", "BOBBY", "your@email.com", "12.12.12.12"));
assertThat(result.getLastLogin(), equalTo(123L));
}
@Test
public void shouldUpdateLastLoc() {
// given
DataSource dataSource = getDataSource();
PlayerAuth user = PlayerAuth.builder()
.name("user").locX(143).locY(-42.12).locZ(29.47)
.locWorld("the_end").build();
// when
boolean response = dataSource.updateQuitLoc(user);
// then
assertThat(response, equalTo(true));
assertThat(dataSource.getAuth("user"), hasAuthLocation(143, -42.12, 29.47, "the_end"));
}
}

View File

@ -0,0 +1,84 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.Objects;
/**
* Custom matchers for AuthMe entities.
*/
public final class AuthMeMatchers {
private AuthMeMatchers() {
}
public static Matcher<? super HashedPassword> equalToHash(final String hash) {
return equalToHash(new HashedPassword(hash));
}
public static Matcher<? super HashedPassword> equalToHash(final String hash, final String salt) {
return equalToHash(new HashedPassword(hash, salt));
}
public static Matcher<? super HashedPassword> equalToHash(final HashedPassword hash) {
return new TypeSafeMatcher<HashedPassword>() {
@Override
public boolean matchesSafely(HashedPassword item) {
return Objects.equals(hash.getHash(), item.getHash())
&& Objects.equals(hash.getSalt(), item.getSalt());
}
@Override
public void describeTo(Description description) {
String representation = "'" + hash.getHash() + "'";
if (hash.getSalt() != null) {
representation += ", '" + hash.getSalt() + "'";
}
description.appendValue("HashedPassword(" + representation + ")");
}
};
}
public static Matcher<? super PlayerAuth> hasAuthBasicData(final String name, final String realName,
final String email, final String ip) {
return new TypeSafeMatcher<PlayerAuth>() {
@Override
public boolean matchesSafely(PlayerAuth item) {
return Objects.equals(name, item.getNickname())
&& Objects.equals(realName, item.getRealName())
&& Objects.equals(email, item.getEmail())
&& Objects.equals(ip, item.getIp());
}
@Override
public void describeTo(Description description) {
description.appendValue(String.format("PlayerAuth with name %s, realname %s, email %s, ip %s",
name, realName, email, ip));
}
};
}
public static Matcher<? super PlayerAuth> hasAuthLocation(final double x, final double y, final double z,
final String world) {
return new TypeSafeMatcher<PlayerAuth>() {
@Override
public boolean matchesSafely(PlayerAuth item) {
return Objects.equals(x, item.getQuitLocX())
&& Objects.equals(y, item.getQuitLocY())
&& Objects.equals(z, item.getQuitLocZ())
&& Objects.equals(world, item.getWorld());
}
@Override
public void describeTo(Description description) {
description.appendValue(String.format("PlayerAuth with quit location (x: %f, y: %f, z: %f, world: %s)",
x, y, z, world));
}
};
}
}

View File

@ -0,0 +1,96 @@
package fr.xephi.authme.datasource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Integration test for {@link MySQL}.
*/
public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
/** Mock of a settings instance. */
private static NewSetting settings;
/** SQL statement to execute before running a test. */
private static String sqlInitialize;
/** Connection to the H2 test database. */
private HikariDataSource hikariSource;
/**
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
*/
@BeforeClass
public static void initializeSettings() throws IOException, ClassNotFoundException {
// Check that we have an H2 driver
Class.forName("org.h2.jdbcx.JdbcDataSource");
settings = mock(NewSetting.class);
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Property) invocation.getArguments()[0]).getDefaultValue();
}
});
set(DatabaseSettings.MYSQL_DATABASE, "h2_test");
set(DatabaseSettings.MYSQL_TABLE, "authme");
set(DatabaseSettings.MYSQL_COL_SALT, "salt");
ConsoleLoggerTestInitializer.setupLogger();
Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql");
sqlInitialize = new String(Files.readAllBytes(sqlInitFile));
}
@Before
public void initializeConnectionAndTable() throws SQLException {
silentClose(hikariSource);
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.setConnectionTestQuery("VALUES 1");
config.addDataSourceProperty("URL", "jdbc:h2:mem:test");
config.addDataSourceProperty("user", "sa");
config.addDataSourceProperty("password", "sa");
HikariDataSource ds = new HikariDataSource(config);
Connection connection = ds.getConnection();
try (Statement st = connection.createStatement()) {
st.execute("DROP TABLE IF EXISTS authme");
st.execute(sqlInitialize);
}
hikariSource = ds;
}
@Override
protected DataSource getDataSource() {
return new MySQL(settings, hikariSource);
}
private static <T> void set(Property<T> property, T value) {
when(settings.getProperty(property)).thenReturn(value);
}
private static void silentClose(HikariDataSource con) {
if (con != null && !con.isClosed()) {
con.close();
}
}
}

View File

@ -0,0 +1,96 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Integration test for {@link SQLite}.
*/
public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
/** Mock of a settings instance. */
private static NewSetting settings;
/** Collection of SQL statements to execute for initialization of a test. */
private static String[] sqlInitialize;
/** Connection to the SQLite test database. */
private Connection con;
/**
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
*/
@BeforeClass
public static void initializeSettings() throws IOException, ClassNotFoundException {
// Check that we have an implementation for SQLite
Class.forName("org.sqlite.JDBC");
settings = mock(NewSetting.class);
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Property) invocation.getArguments()[0]).getDefaultValue();
}
});
set(DatabaseSettings.MYSQL_DATABASE, "sqlite-test");
set(DatabaseSettings.MYSQL_TABLE, "authme");
set(DatabaseSettings.MYSQL_COL_SALT, "salt");
ConsoleLoggerTestInitializer.setupLogger();
Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql");
// Note ljacqu 20160221: It appears that we can only run one statement per Statement.execute() so we split
// the SQL file by ";\n" as to get the individual statements
sqlInitialize = new String(Files.readAllBytes(sqlInitFile)).split(";(\\r?)\\n");
}
@Before
public void initializeConnectionAndTable() throws SQLException {
silentClose(con);
Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
try (Statement st = connection.createStatement()) {
st.execute("DROP TABLE IF EXISTS authme");
for (String statement : sqlInitialize) {
st.execute(statement);
}
}
con = connection;
}
@Override
protected DataSource getDataSource() {
return new SQLite(settings, con);
}
private static <T> void set(Property<T> property, T value) {
when(settings.getProperty(property)).thenReturn(value);
}
private static void silentClose(Connection con) {
if (con != null) {
try {
if (!con.isClosed()) {
con.close();
}
} catch (SQLException e) {
// silent
}
}
}
}

View File

@ -50,8 +50,7 @@ public class MessagesIntegrationTest {
@Before @Before
public void setUpMessages() { public void setUpMessages() {
File testFile = TestHelper.getJarFile(YML_TEST_FILE); File testFile = TestHelper.getJarFile(YML_TEST_FILE);
File defaultFile = TestHelper.getJarFile(YML_DEFAULT_TEST_FILE); messages = new Messages(testFile, YML_DEFAULT_TEST_FILE);
messages = new Messages(testFile, defaultFile);
} }
@Test @Test
@ -267,4 +266,16 @@ public class MessagesIntegrationTest {
assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE), assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE),
equalTo("Message from default file")); equalTo("Message from default file"));
} }
@Test
public void shouldRetrieveMessageWithReplacements() {
// given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
// when
String result = messages.retrieveSingle(key, "24680");
// then
assertThat(result, equalTo("Use /captcha 24680 to solve the captcha"));
}
} }

View File

@ -7,7 +7,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import java.io.File; import java.io.IOException;
import java.io.InputStream;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -55,17 +56,19 @@ public class NewSettingTest {
} }
@Test @Test
public void shouldReturnDefaultFile() { public void shouldReturnDefaultFile() throws IOException {
// given // given
YamlConfiguration configuration = mock(YamlConfiguration.class); YamlConfiguration configuration = mock(YamlConfiguration.class);
NewSetting settings = new NewSetting(configuration, null, null); NewSetting settings = new NewSetting(configuration, null, null);
// when // when
File defaultFile = settings.getDefaultMessagesFile(); String defaultFile = settings.getDefaultMessagesFile();
// then // then
assertThat(defaultFile, not(nullValue())); assertThat(defaultFile, not(nullValue()));
assertThat(defaultFile.exists(), equalTo(true)); InputStream stream = this.getClass().getResourceAsStream(defaultFile);
assertThat(stream, not(nullValue()));
assertThat(stream.read(), not(equalTo(0)));
} }
private static <T> void setReturnValue(YamlConfiguration config, Property<T> property, T value) { private static <T> void setReturnValue(YamlConfiguration config, Property<T> property, T value) {

View File

@ -35,7 +35,7 @@ public final class TestConfiguration implements SettingsClass {
newProperty(PropertyType.STRING_LIST, "features.boring.colors"); newProperty(PropertyType.STRING_LIST, "features.boring.colors");
public static final Property<Integer> DUST_LEVEL = public static final Property<Integer> DUST_LEVEL =
newProperty(PropertyType.INTEGER, "features.boring.dustLevel", -1); newProperty("features.boring.dustLevel", -1);
public static final Property<Boolean> USE_COOL_FEATURES = public static final Property<Boolean> USE_COOL_FEATURES =
newProperty("features.cool.enabled", false); newProperty("features.cool.enabled", false);

View File

@ -0,0 +1,22 @@
-- Important: separate SQL statements by ; followed directly by a newline. We split the file contents by ";\n"
CREATE TABLE authme (
id INTEGER AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
ip VARCHAR(40) NOT NULL,
lastlogin BIGINT,
x DOUBLE NOT NULL DEFAULT '0.0',
y DOUBLE NOT NULL DEFAULT '0.0',
z DOUBLE NOT NULL DEFAULT '0.0',
world VARCHAR(255) NOT NULL DEFAULT 'world',
email VARCHAR(255) DEFAULT 'your@email.com',
isLogged INT DEFAULT '0', realname VARCHAR(255) NOT NULL DEFAULT 'Player',
salt varchar(255),
CONSTRAINT table_const_prim PRIMARY KEY (id)
);
INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt)
VALUES (1,'bobby','$SHA$11aa0706173d7272$dbba966','123.45.67.89',1449136800,1.05,2.1,4.2,'world','your@email.com',0,'Bobby',NULL);
INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt)
VALUES (NULL,'user','b28c32f624a4eb161d6adc9acb5bfc5b','34.56.78.90',1453242857,124.1,76.3,-127.8,'nether','user@example.org',0,'user','f750ba32');