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
61 changed files with 1086 additions and 510 deletions
+61 -93
View File
@@ -17,7 +17,6 @@ import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandMapper;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.converter.ForceFlatToSqlite;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
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.PlayerStatePermission;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
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.OtherAccounts;
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.util.CollectionUtils;
import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.MigrationService;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils;
import fr.xephi.authme.util.Wrapper;
import net.minelink.ctplus.CombatTagPlus;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
@@ -77,6 +75,7 @@ import org.bukkit.scheduler.BukkitTask;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
@@ -106,27 +105,23 @@ public class AuthMe extends JavaPlugin {
// Private Instances
private static AuthMe plugin;
private static Server server;
private static Wrapper wrapper = Wrapper.getInstance();
private Management management;
private CommandHandler commandHandler = null;
private PermissionsManager permsMan = null;
private NewSetting newSettings;
private Messages messages;
private JsonCache playerBackup;
private PasswordSecurity passwordSecurity;
private DataSource database;
/*
* Maps and stuff
* TODO: Clean up and Move into a manager
*/
public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
/*
* Public Instances
* TODO: Encapsulation
* TODO #432: Encapsulation
*/
public NewAPI api;
public SendMailSSL mail;
public DataManager dataManager;
public OtherAccounts otherAccounts;
public Location essentialsSpawn;
/*
* Plugin Hooks
* TODO: Move into modules
@@ -137,15 +132,14 @@ public class AuthMe extends JavaPlugin {
public AuthMeInventoryPacketAdapter inventoryProtector;
public AuthMeTabCompletePacketAdapter tabComplete;
public AuthMeTablistPacketAdapter tablistHider;
/*
* Maps and stuff
* TODO: Clean up and Move into a manager
*/
public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
private Management management;
private CommandHandler commandHandler = null;
private PermissionsManager permsMan = null;
private NewSetting newSettings;
private Messages messages;
private JsonCache playerBackup;
private PasswordSecurity passwordSecurity;
private DataSource database;
/**
* Get the plugin's instance.
@@ -247,7 +241,7 @@ public class AuthMe extends JavaPlugin {
// Connect to the database and setup tables
try {
setupDatabase();
setupDatabase(newSettings);
} catch (Exception e) {
ConsoleLogger.logException("Fatal error occurred during database connection! "
+ "Authme initialization aborted!", e);
@@ -255,6 +249,7 @@ public class AuthMe extends JavaPlugin {
return;
}
MigrationService.changePlainTextToSha256(newSettings, database, new SHA256());
passwordSecurity = new PasswordSecurity(getDataSource(), newSettings.getProperty(SecuritySettings.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)
database.close();
// Backend MYSQL - FILE - SQLITE - SQLITEHIKARI
boolean isSQLite = false;
switch (newSettings.getProperty(DatabaseSettings.BACKEND)) {
case FILE:
database = new FlatFile();
break;
case MYSQL:
database = new MySQL(newSettings);
break;
case SQLITE:
database = new SQLite(newSettings);
isSQLite = true;
break;
/**
* Sets up the data source.
*
* @param settings The settings instance
* @see AuthMe#database
*/
public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException {
if (this.database != null) {
this.database.close();
}
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() {
@Override
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) {
tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register();
} else if (inventoryProtector != null) {
} else if (tabComplete != null) {
tabComplete.unregister();
tabComplete = null;
}
if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) {
tablistHider = new AuthMeTablistPacketAdapter(this);
tablistHider.register();
} else if (inventoryProtector != null) {
} else if (tablistHider != null) {
tablistHider.unregister();
tablistHider = null;
}
@@ -788,25 +772,9 @@ public class AuthMe extends JavaPlugin {
}
// Return the spawn location of a player
@Deprecated
public Location getSpawnLocation(Player player) {
World world = player.getWorld();
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 Spawn.getInstance().getSpawnLocation(player);
}
// Return the default spawn point of a world
@@ -948,7 +916,7 @@ public class AuthMe extends JavaPlugin {
String commandLabel, String[] args) {
// Make sure the command handler has been initialized
if (commandHandler == null) {
wrapper.getLogger().severe("AuthMe command handler is not available");
getLogger().severe("AuthMe command handler is not available");
return false;
}
+5 -58
View File
@@ -1,12 +1,11 @@
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.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);
}
/**
* 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.
*
@@ -118,44 +103,6 @@ public class PlayerAuth {
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.
*
@@ -171,8 +118,8 @@ public class PlayerAuth {
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) {
private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) {
this.nickname = nickname.toLowerCase();
this.password = password;
this.ip = ip;
@@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.settings.Spawn;
import org.bukkit.command.CommandSender;
import java.util.List;
@@ -20,7 +21,11 @@ public class ReloadCommand implements ExecutableCommand {
try {
commandService.getSettings().reload();
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);
} catch (Exception e) {
sender.sendMessage("Error occurred during reload of AuthMe: aborting");
@@ -179,8 +179,8 @@ public class CacheDataSource implements DataSource {
}
@Override
public synchronized List<String> getAllAuthsByEmail(final String email) {
return source.getAllAuthsByEmail(email);
public synchronized int countAuthsByEmail(final String email) {
return source.countAuthsByEmail(email);
}
@Override
@@ -14,7 +14,6 @@ public interface DataSource {
* Return whether there is a record for the given username.
*
* @param user The username to look up
*
* @return True if there is a record, false otherwise
*/
boolean isAuthAvailable(String user);
@@ -23,7 +22,6 @@ public interface DataSource {
* Return the hashed password of the player.
*
* @param user The user whose password should be retrieve
*
* @return The password hash of the player
*/
HashedPassword getPassword(String user);
@@ -32,7 +30,6 @@ public interface DataSource {
* Retrieve the entire PlayerAuth object associated with the username.
*
* @param user The user to retrieve
*
* @return The PlayerAuth object for the given username
*/
PlayerAuth getAuth(String user);
@@ -41,7 +38,6 @@ public interface DataSource {
* Save a new PlayerAuth object.
*
* @param auth The new PlayerAuth to persist
*
* @return True upon success, false upon failure
*/
boolean saveAuth(PlayerAuth auth);
@@ -50,7 +46,6 @@ public interface DataSource {
* Update the session of a record (IP, last login, real name).
*
* @param auth The PlayerAuth object to update in the database
*
* @return True upon success, false upon failure
*/
boolean updateSession(PlayerAuth auth);
@@ -59,7 +54,6 @@ public interface DataSource {
* Update the password of the given PlayerAuth object.
*
* @param auth The PlayerAuth whose password should be updated
*
* @return True upon success, false upon failure
*/
boolean updatePassword(PlayerAuth auth);
@@ -67,9 +61,8 @@ public interface DataSource {
/**
* 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
*
* @return True upon success, false upon failure
*/
boolean updatePassword(String user, HashedPassword password);
@@ -79,7 +72,6 @@ public interface DataSource {
* the given time.
*
* @param until The minimum last login
*
* @return The account names that have been removed
*/
List<String> autoPurgeDatabase(long until);
@@ -88,7 +80,6 @@ public interface DataSource {
* Remove a user record from the database.
*
* @param user The user to remove
*
* @return True upon success, false upon failure
*/
boolean removeAuth(String user);
@@ -97,7 +88,6 @@ public interface DataSource {
* Update the quit location of a PlayerAuth.
*
* @param auth The entry whose quit location should be updated
*
* @return True upon success, false upon failure
*/
boolean updateQuitLoc(PlayerAuth auth);
@@ -106,25 +96,22 @@ public interface DataSource {
* Return all usernames associated with the given IP address.
*
* @param ip The IP address to look up
*
* @return Usernames associated with the given IP address
*/
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
*
* @return Users using the given email address
* @return Number of accounts using the given email address
*/
List<String> getAllAuthsByEmail(String email);
int countAuthsByEmail(String email);
/**
* Update the email of the PlayerAuth in the data source.
*
* @param auth The PlayerAuth whose email should be updated
*
* @return True upon success, false upon failure
*/
boolean updateEmail(PlayerAuth auth);
@@ -7,6 +7,7 @@ public enum DataSourceType {
MYSQL,
@Deprecated
FILE,
SQLITE
@@ -487,25 +487,21 @@ public class FlatFile implements DataSource {
}
@Override
public List<String> getAllAuthsByEmail(String email) {
public int countAuthsByEmail(String email) {
BufferedReader br = null;
List<String> countEmail = new ArrayList<>();
int countEmail = 0;
try {
br = new BufferedReader(new FileReader(source));
String line;
while ((line = br.readLine()) != null) {
String[] args = line.split(":");
if (args.length > 8 && args[8].equals(email)) {
countEmail.add(args[0]);
++countEmail;
}
}
return countEmail;
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} finally {
if (br != null) {
try {
@@ -514,6 +510,7 @@ public class FlatFile implements DataSource {
}
}
}
return 0;
}
@Override
@@ -22,7 +22,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
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 {
ds = new HikariDataSource();
ds.setPoolName("AuthMeMYSQLPool");
@@ -131,7 +143,7 @@ public class MySQL implements DataSource {
+ col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ col.PASSWORD + " VARCHAR(255) NOT NULL,"
+ 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_Y + " 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);
if (!rs.next()) {
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 {
migrateLastLoginColumnToTimestamp(con, rs);
migrateLastLoginColumnToBigInt(con, rs);
}
rs.close();
@@ -234,66 +246,72 @@ public class MySQL implements DataSource {
@Override
public synchronized boolean isAuthAvailable(String user) {
try (Connection con = getConnection()) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
ResultSet rs = null;
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery();
rs = pst.executeQuery();
return rs.next();
} catch (SQLException ex) {
logSqlException(ex);
} finally {
close(rs);
}
return false;
}
@Override
public HashedPassword getPassword(String user) {
try (Connection con = getConnection()) {
String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName
+ " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName
+ " WHERE " + col.NAME + "=?;";
ResultSet rs = null;
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery();
rs = pst.executeQuery();
if (rs.next()) {
return new HashedPassword(rs.getString(col.PASSWORD),
!col.SALT.isEmpty() ? rs.getString(col.SALT) : null);
}
} catch (SQLException ex) {
logSqlException(ex);
} finally {
close(rs);
}
return null;
}
@Override
public synchronized PlayerAuth getAuth(String user) {
PlayerAuth pAuth;
try (Connection con = getConnection()) {
String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;";
PlayerAuth auth;
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery();
if (!rs.next()) {
return null;
int id;
try (ResultSet rs = pst.executeQuery()) {
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) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;");
pst.setInt(1, id);
rs = pst.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
try (PreparedStatement pst2 = con.prepareStatement(
"SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
pst2.setInt(1, id);
try (ResultSet rs = pst2.executeQuery()) {
if (rs.next()) {
Blob blob = rs.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
auth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
}
}
}
return auth;
} catch (SQLException ex) {
logSqlException(ex);
return null;
}
return pAuth;
return null;
}
@Override
@@ -314,7 +332,7 @@ public class MySQL implements DataSource {
pst.setString(1, auth.getNickname());
pst.setString(2, auth.getPassword().getHash());
pst.setString(3, auth.getIp());
pst.setTimestamp(4, new Timestamp(auth.getLastLogin()));
pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getRealName());
pst.setString(6, auth.getEmail());
if (useSalt) {
@@ -566,7 +584,7 @@ public class MySQL implements DataSource {
+ col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, auth.getIp());
pst.setTimestamp(2, new Timestamp(auth.getLastLogin()));
pst.setLong(2, auth.getLastLogin());
pst.setString(3, auth.getRealName());
pst.setString(4, auth.getNickname());
pst.executeUpdate();
@@ -580,20 +598,19 @@ public class MySQL implements DataSource {
@Override
public synchronized List<String> autoPurgeDatabase(long until) {
List<String> list = new ArrayList<>();
try (Connection con = getConnection()) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
PreparedStatement st = con.prepareStatement(sql);
st.setLong(1, until);
ResultSet rs = st.executeQuery();
while (rs.next()) {
list.add(rs.getString(col.NAME));
String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
try (Connection con = getConnection();
PreparedStatement selectPst = con.prepareStatement(select);
PreparedStatement deletePst = con.prepareStatement(delete)) {
selectPst.setLong(1, until);
try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(col.NAME));
}
}
rs.close();
sql = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
st = con.prepareStatement(sql);
st.setLong(1, until);
st.executeUpdate();
st.close();
deletePst.setLong(1, until);
deletePst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -634,18 +651,16 @@ public class MySQL implements DataSource {
@Override
public synchronized boolean updateQuitLoc(PlayerAuth auth) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?"
+ " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?"
+ " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setDouble(1, auth.getQuitLocX());
pst.setDouble(2, auth.getQuitLocY());
pst.setDouble(3, auth.getQuitLocZ());
pst.setString(4, auth.getWorld());
pst.setString(5, auth.getNickname());
pst.executeUpdate();
pst.close();
return true;
} catch (SQLException ex) {
logSqlException(ex);
@@ -655,13 +670,11 @@ public class MySQL implements DataSource {
@Override
public synchronized boolean updateEmail(PlayerAuth auth) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, auth.getEmail());
pst.setString(2, auth.getNickname());
pst.executeUpdate();
pst.close();
return true;
} catch (SQLException ex) {
logSqlException(ex);
@@ -690,16 +703,14 @@ public class MySQL implements DataSource {
@Override
public synchronized List<String> getAllAuthsByIp(String ip) {
List<String> result = new ArrayList<>();
try (Connection con = getConnection()) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
ResultSet rs = pst.executeQuery();
while (rs.next()) {
result.add(rs.getString(col.NAME));
try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) {
result.add(rs.getString(col.NAME));
}
}
rs.close();
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -707,33 +718,29 @@ public class MySQL implements DataSource {
}
@Override
public synchronized List<String> getAllAuthsByEmail(String email) {
List<String> countEmail = new ArrayList<>();
try (Connection con = getConnection()) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.EMAIL + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
public synchronized int countAuthsByEmail(String email) {
String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, email);
ResultSet rs = pst.executeQuery();
while (rs.next()) {
countEmail.add(rs.getString(col.NAME));
try (ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
return rs.getInt(1);
}
}
rs.close();
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
return countEmail;
return 0;
}
@Override
public synchronized void purgeBanned(List<String> banned) {
try (Connection con = getConnection()) {
PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;");
String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) {
pst.setString(1, name);
pst.executeUpdate();
}
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -746,28 +753,25 @@ public class MySQL implements DataSource {
@Override
public boolean isLogged(String user) {
boolean isLogged = false;
try (Connection con = getConnection()) {
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user);
ResultSet rs = pst.executeQuery();
isLogged = rs.next() && (rs.getInt(col.IS_LOGGED) == 1);
try (ResultSet rs = pst.executeQuery()) {
return rs.next() && (rs.getInt(col.IS_LOGGED) == 1);
}
} catch (SQLException ex) {
logSqlException(ex);
}
return isLogged;
return false;
}
@Override
public void setLogged(String user) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, 1);
pst.setString(2, user.toLowerCase());
pst.executeUpdate();
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -775,13 +779,11 @@ public class MySQL implements DataSource {
@Override
public void setUnlogged(String user) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, 0);
pst.setString(2, user.toLowerCase());
pst.executeUpdate();
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -789,13 +791,11 @@ public class MySQL implements DataSource {
@Override
public void purgeLogged() {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setInt(1, 0);
pst.setInt(2, 1);
pst.executeUpdate();
pst.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -804,14 +804,13 @@ public class MySQL implements DataSource {
@Override
public int getAccountsRegistered() {
int result = 0;
try (Connection con = getConnection()) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + tableName);
String sql = "SELECT COUNT(*) FROM " + tableName;
try (Connection con = getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql)) {
if (rs.next()) {
result = rs.getInt(1);
}
rs.close();
st.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -820,9 +819,8 @@ public class MySQL implements DataSource {
@Override
public void updateName(String oldOne, String newOne) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, newOne);
pst.setString(2, oldOne);
pst.executeUpdate();
@@ -833,9 +831,8 @@ public class MySQL implements DataSource {
@Override
public boolean updateRealName(String user, String realName) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, realName);
pst.setString(2, user);
pst.executeUpdate();
@@ -848,9 +845,8 @@ public class MySQL implements DataSource {
@Override
public boolean updateIp(String user, String ip) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
pst.setString(2, user);
pst.executeUpdate();
@@ -867,23 +863,23 @@ public class MySQL implements DataSource {
try (Connection con = getConnection()) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM " + tableName);
PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;");
while (rs.next()) {
PlayerAuth pAuth = buildAuthFromResultSet(rs);
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
int id = rs.getInt(col.ID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
try (PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
int id = rs.getInt(col.ID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
rs2.close();
}
rs2.close();
}
auths.add(pAuth);
}
pst.close();
rs.close();
st.close();
} catch (SQLException ex) {
@@ -922,12 +918,12 @@ public class MySQL implements DataSource {
@Override
public synchronized boolean isEmailStored(String email) {
String sql = "SELECT 1 FROM " + tableName + " WHERE " + col.EMAIL + " = ?";
try (Connection con = ds.getConnection()) {
PreparedStatement pst = con.prepareStatement(sql);
String sql = "SELECT 1 FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, email);
ResultSet rs = pst.executeQuery();
return rs.next();
try (ResultSet rs = pst.executeQuery()) {
return rs.next();
}
} catch (SQLException e) {
logSqlException(e);
}
@@ -941,7 +937,7 @@ public class MySQL implements DataSource {
.name(row.getString(col.NAME))
.realName(row.getString(col.REAL_NAME))
.password(row.getString(col.PASSWORD), salt)
.lastLogin(safeGetTimestamp(row))
.lastLogin(row.getLong(col.LAST_LOGIN))
.ip(row.getString(col.IP))
.locWorld(row.getString(col.LASTLOC_WORLD))
.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
* @return The timestamp (as number of milliseconds since 1970-01-01 00:00:00 GMT)
* @param con Connection to the database
* @param rs ResultSet containing meta data for the lastlogin column
*/
private long safeGetTimestamp(ResultSet row) {
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 {
private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException {
final int columnType = rs.getInt("DATA_TYPE");
if (columnType == Types.BIGINT) {
ConsoleLogger.info("Migrating lastlogin column from bigint to timestamp");
if (columnType == Types.TIMESTAMP) {
ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
final String lastLoginOld = col.LAST_LOGIN + "_old";
// Rename lastlogin to lastlogin_old
@@ -981,12 +968,12 @@ public class MySQL implements DataSource {
// Create lastlogin column
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);
con.prepareStatement(sql).execute();
// 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);
con.prepareStatement(sql).execute();
@@ -994,7 +981,7 @@ public class MySQL implements DataSource {
sql = String.format("ALTER TABLE %s DROP COLUMN %s",
tableName, lastLoginOld);
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);
}
private static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
ConsoleLogger.logException("Could not close ResultSet", e);
}
}
}
}
@@ -1,5 +1,6 @@
package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
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 {
Class.forName("org.sqlite.JDBC");
ConsoleLogger.info("SQLite driver loaded");
@@ -341,7 +350,8 @@ public class SQLite implements DataSource {
@Override
public synchronized void close() {
try {
con.close();
if (con != null && !con.isClosed())
con.close();
} catch (SQLException ex) {
logSqlException(ex);
}
@@ -394,25 +404,19 @@ public class SQLite implements DataSource {
}
@Override
public List<String> getAllAuthsByEmail(String email) {
PreparedStatement pst = null;
ResultSet rs = null;
List<String> countEmail = new ArrayList<>();
try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.EMAIL + "=?;");
public int countAuthsByEmail(String email) {
String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, email);
rs = pst.executeQuery();
while (rs.next()) {
countEmail.add(rs.getString(col.NAME));
try (ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
return rs.getInt(1);
}
}
return countEmail;
} catch (SQLException ex) {
logSqlException(ex);
} finally {
close(rs);
close(pst);
}
return new ArrayList<>();
return 0;
}
@Override
@@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -243,9 +242,7 @@ public class AuthMePlayerListener implements Listener {
String realName = auth.getRealName();
if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE
event.setKickMessage("You should join using username: " + ChatColor.AQUA + realName +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName());
event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CASE, realName, event.getName()));
return;
}
if (realName.isEmpty() || realName.equals("Player")) {
@@ -127,7 +127,9 @@ public enum MessageKey {
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[] tags;
@@ -8,6 +8,8 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Class for retrieving and sending translatable messages to players.
@@ -16,7 +18,7 @@ public class Messages {
private FileConfiguration configuration;
private String fileName;
private final File defaultFile;
private final String defaultFile;
private FileConfiguration defaultConfiguration;
/**
@@ -25,7 +27,7 @@ public class Messages {
* @param messageFile The messages file to use
* @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);
this.defaultFile = defaultFile;
}
@@ -53,17 +55,7 @@ public class Messages {
* @param replacements The replacements to apply for the tags
*/
public void send(CommandSender sender, 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 + "'");
send(sender, key);
}
String message = retrieveSingle(key, replacements);
for (String line : message.split("\n")) {
sender.sendMessage(line);
}
@@ -97,6 +89,27 @@ public class Messages {
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.
*
@@ -117,7 +130,8 @@ public class Messages {
}
if (defaultConfiguration == null) {
defaultConfiguration = YamlConfiguration.loadConfiguration(defaultFile);
InputStream stream = Messages.class.getResourceAsStream(defaultFile);
defaultConfiguration = YamlConfiguration.loadConfiguration(new InputStreamReader(stream));
}
String message = defaultConfiguration.getString(code);
return message == null ? getDefaultErrorMessage(code) : message;
@@ -64,15 +64,16 @@ public class AsynchronousJoin {
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() {
@Override
public void run() {
AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
player.kickPlayer(m.retrieveSingle(MessageKey.NOT_OWNER_ERROR));
if (Settings.banUnsafeIp)
if (Settings.banUnsafeIp) {
plugin.getServer().banIP(ip);
}
}
});
return;
@@ -220,7 +220,7 @@ public class AsynchronousLogin {
}
}
public void displayOtherAccounts(PlayerAuth auth) {
private void displayOtherAccounts(PlayerAuth auth) {
if (!Settings.displayOtherAccounts || auth == null) {
return;
}
@@ -2,6 +2,8 @@ package fr.xephi.authme.process.login;
import fr.xephi.authme.settings.NewSetting;
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.Location;
import org.bukkit.entity.Player;
@@ -92,7 +94,7 @@ public class ProcessSyncPlayerLogin implements Runnable {
}
private void restoreSpeedEffects() {
if (Settings.isRemoveSpeedEnabled) {
if (settings.getProperty(RestrictionSettings.REMOVE_SPEED)) {
player.setWalkSpeed(0.2F);
player.setFlySpeed(0.1F);
}
@@ -173,19 +175,19 @@ public class ProcessSyncPlayerLogin implements Runnable {
}
restoreSpeedEffects();
if (Settings.applyBlindEffect) {
if (settings.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
player.removePotionEffect(PotionEffectType.BLINDNESS);
}
// The Login event now fires (as intended) after everything is processed
Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player));
player.saveData();
if (Settings.bungee) {
if (settings.getProperty(HooksSettings.BUNGEECORD)) {
sendBungeeMessage();
}
// Login is finish, display welcome message if we use email registration
if (Settings.useWelcomeMessage && Settings.emailRegistration) {
if (Settings.broadcastWelcomeMessage) {
// Login is done, display welcome message
if (settings.getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) {
if (settings.getProperty(RegistrationSettings.BROADCAST_WELCOME_MESSAGE)) {
for (String s : settings.getWelcomeMessage()) {
Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player));
}
@@ -98,7 +98,7 @@ public class AsyncRegister {
private void emailRegister() {
if (Settings.getmaxRegPerEmail > 0
&& !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);
return;
}
@@ -113,7 +113,7 @@ public class PasswordSecurity {
*/
private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) {
try {
return HashAlgorithm.CUSTOM.equals(algorithm)
return HashAlgorithm.CUSTOM.equals(algorithm) || HashAlgorithm.PLAINTEXT.equals(algorithm)
? null
: algorithm.getClazz().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
@@ -11,7 +11,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
* and store the salt with the hash itself.
*/
@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 int getSaltLength();
@@ -82,4 +82,13 @@ public abstract class CustomConfiguration extends YamlConfiguration {
}
return false;
}
public boolean containsAll(String... paths) {
for (String path : paths) {
if (!contains(path)) {
return false;
}
}
return true;
}
}
@@ -18,7 +18,6 @@ import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
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() {
String defaultFilePath = "/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 getDefaultMessagesFile() {
return "/messages/messages_en.yml";
}
public String getEmailMessage() {
@@ -5,6 +5,7 @@ import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
@@ -144,8 +145,6 @@ public final class Settings {
denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN);
plugin.checkProtocolLib();
passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH);
backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true);
@@ -176,7 +175,7 @@ public final class Settings {
emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false);
saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8);
getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1);
multiverse = configFile.getBoolean("Hooks.multiverse", true);
multiverse = load(HooksSettings.MULTIVERSE);
bungee = configFile.getBoolean("Hooks.bungeecord", false);
getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds");
banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false);
@@ -213,7 +212,7 @@ public final class Settings {
broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false);
forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", 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);
getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0);
checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false);
@@ -1,7 +1,13 @@
package fr.xephi.authme.settings;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.io.File;
@@ -12,13 +18,17 @@ import java.io.File;
public class Spawn extends CustomConfiguration {
private static Spawn spawn;
private static String[] spawnPriority;
public Spawn() {
super(new File("." + File.separator + "plugins" + File.separator + "AuthMe" + File.separator + "spawn.yml"));
spawn = this;
private Spawn() {
super(new File(Settings.PLUGIN_FOLDER, "spawn.yml"));
load();
save();
saveDefault();
spawnPriority = Settings.spawnPriority.split(",");
}
public static void reload() {
spawn = new Spawn();
}
/**
@@ -33,111 +43,106 @@ public class Spawn extends CustomConfiguration {
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) {
try {
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) {
if (location == null || location.getWorld() == null) {
return false;
}
set("spawn.world", location.getWorld().getName());
set("spawn.x", location.getX());
set("spawn.y", location.getY());
set("spawn.z", location.getZ());
set("spawn.yaw", location.getYaw());
set("spawn.pitch", location.getPitch());
save();
return true;
}
/**
* Method setFirstSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setFirstSpawn(Location location) {
try {
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) {
if (location == null || location.getWorld() == null) {
return false;
}
set("firstspawn.world", location.getWorld().getName());
set("firstspawn.x", location.getX());
set("firstspawn.y", location.getY());
set("firstspawn.z", location.getZ());
set("firstspawn.yaw", location.getYaw());
set("firstspawn.pitch", location.getPitch());
save();
return true;
}
/**
* Method getLocation.
*
* @return Location
*/
@Deprecated
public Location getLocation() {
return getSpawn();
}
/**
* Method getSpawn.
*
* @return Location
*/
public Location getSpawn() {
try {
if (this.getString("spawn.world").isEmpty() || this.getString("spawn.world").equals(""))
return null;
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")));
return location;
} catch (NullPointerException | NumberFormatException npe) {
return null;
if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) {
String worldName = getString("spawn.world");
World world = Bukkit.getWorld(worldName);
if (!StringUtils.isEmpty(worldName) && world != null) {
return new Location(
world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"),
Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch"))
);
}
}
return null;
}
/**
* Method getFirstSpawn.
*
* @return Location
*/
public Location getFirstSpawn() {
try {
if (this.getString("firstspawn.world").isEmpty() || this.getString("firstspawn.world").equals(""))
return null;
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")));
return location;
} catch (NullPointerException | NumberFormatException npe) {
return null;
if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y",
"firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) {
String worldName = getString("firstspawn.world");
World world = Bukkit.getWorld(worldName);
if (!StringUtils.isEmpty(worldName) && world != null) {
return new Location(
world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"),
Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch"))
);
}
}
return 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
}
}
@@ -20,7 +20,7 @@ public class BackupSettings implements SettingsClass {
public static final Property<Boolean> ON_SERVER_STOP =
newProperty("BackupSystem.OnServerStop", true);
@Comment(" Windows only mysql installation Path")
@Comment("Windows only mysql installation Path")
public static final Property<String> MYSQL_WINDOWS_PATH =
newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
@@ -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;
}
}