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:
>- 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)
<hr>

15
pom.xml
View File

@ -61,7 +61,7 @@
<javaVersion>1.7</javaVersion>
<!-- Change Bukkit Version HERE! -->
<bukkitVersion>1.8.8-R0.1-SNAPSHOT</bukkitVersion>
<bukkitVersion>1.9-pre1-SNAPSHOT</bukkitVersion>
</properties>
<profiles>
@ -376,6 +376,19 @@
<scope>compile</scope>
<optional>true</optional>
</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) -->
<dependency>

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;
}

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,7 +118,7 @@ public class PlayerAuth {
* @param email 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) {
this.nickname = nickname.toLowerCase();
this.password = password;

View File

@ -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");

View File

@ -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

View File

@ -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);
@ -69,7 +63,6 @@ public interface DataSource {
*
* @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);

View File

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

View File

@ -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

View File

@ -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);
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);
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);
PlayerAuth auth;
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery();
int id;
try (ResultSet rs = pst.executeQuery()) {
if (!rs.next()) {
return null;
}
int id = rs.getInt(col.ID);
pAuth = buildAuthFromResultSet(rs);
rs.close();
pst.close();
id = rs.getInt(col.ID);
auth = buildAuthFromResultSet(rs);
}
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;");
pst.setInt(1, id);
rs = pst.executeQuery();
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());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
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();
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);
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);
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);
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
ResultSet rs = pst.executeQuery();
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);
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);
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);
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);
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()) {
String sql = "SELECT COUNT(*) FROM " + tableName;
try (Connection con = getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + tableName);
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);
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);
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);
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, ip);
pst.setString(2, user);
pst.executeUpdate();
@ -867,10 +863,10 @@ 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) {
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();
@ -881,9 +877,9 @@ public class MySQL implements DataSource {
}
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();
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);
}
}
}
}

View File

@ -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,6 +350,7 @@ public class SQLite implements DataSource {
@Override
public synchronized void close() {
try {
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

View File

@ -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")) {

View File

@ -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;

View File

@ -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;

View File

@ -64,16 +64,17 @@ 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;
}

View File

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

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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);

View File

@ -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,36 +43,10 @@ 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 {
if (location == null || location.getWorld() == null) {
return false;
}
set("spawn.world", location.getWorld().getName());
set("spawn.x", location.getX());
set("spawn.y", location.getY());
@ -71,20 +55,12 @@ public class Spawn extends CustomConfiguration {
set("spawn.pitch", location.getPitch());
save();
return true;
} catch (NullPointerException npe) {
return false;
}
}
/**
* Method setFirstSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setFirstSpawn(Location location) {
try {
if (location == null || location.getWorld() == null) {
return false;
}
set("firstspawn.world", location.getWorld().getName());
set("firstspawn.x", location.getX());
set("firstspawn.y", location.getY());
@ -93,51 +69,80 @@ public class Spawn extends CustomConfiguration {
set("firstspawn.pitch", location.getPitch());
save();
return true;
} catch (NullPointerException npe) {
return false;
}
}
/**
* 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
}
}

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 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 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 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!'

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"'
valid_captcha: '&2Código do captcha correto!'
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_change: '&cUse: /email change <email antigo> <email novo>'
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_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:'
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_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!'
# TODO two_factor_create: Missing tag %url
two_factor_create: '&2Seu código secreto é %code'
# TODO email_already_used: '&4The email address is already being used'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!'
two_factor_create: '&2Seu código secreto é %code. Você pode escanear ele daqui %url'
email_already_used: '&4Este endereço de email já está em uso'
not_owner_error: 'Você não é o dono desta conta. Por favor, tente outro nome!'
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 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 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 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
two_factor_create: '&2Dein geheimer Code ist %code'
# TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

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'
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!'
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 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 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 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 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 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 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!'

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 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 invalid_name_case: 'You should join using username %valid, not %invalid.'
# 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_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
two_factor_create: '&2Votre code secret est %code'
# TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

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 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 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 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!'
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 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 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!'
# 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 invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!'
# TODO usage_unreg: '&cUsage: /unregister <password>'
# 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.'
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 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!'

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_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 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 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 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 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 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'
# 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 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 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 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 invalid_name_case: 'You should join using username %valid, not %invalid.'
# 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_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 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 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 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 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 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 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'
add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"'
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 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 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 email_changed: '&2Email address changed correctly!'
# 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 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 wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!'
# 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!'

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 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 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 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 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 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 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 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 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 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 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 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 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] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器'
email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:'
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!'

View File

@ -63,5 +63,6 @@ antibot_auto_enabled: '&b【AuthMe】&6AntiBotMod已自動啟用!'
antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉'
# 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 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 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.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* AuthMe test utilities.
@ -18,11 +20,31 @@ public final class TestHelper {
* @return The project file
*/
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);
if (url == null) {
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
public void setUpMessages() {
File testFile = TestHelper.getJarFile(YML_TEST_FILE);
File defaultFile = TestHelper.getJarFile(YML_DEFAULT_TEST_FILE);
messages = new Messages(testFile, defaultFile);
messages = new Messages(testFile, YML_DEFAULT_TEST_FILE);
}
@Test
@ -267,4 +266,16 @@ public class MessagesIntegrationTest {
assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE),
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.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.Matchers.equalTo;
@ -55,17 +56,19 @@ public class NewSettingTest {
}
@Test
public void shouldReturnDefaultFile() {
public void shouldReturnDefaultFile() throws IOException {
// given
YamlConfiguration configuration = mock(YamlConfiguration.class);
NewSetting settings = new NewSetting(configuration, null, null);
// when
File defaultFile = settings.getDefaultMessagesFile();
String defaultFile = settings.getDefaultMessagesFile();
// then
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) {

View File

@ -35,7 +35,7 @@ public final class TestConfiguration implements SettingsClass {
newProperty(PropertyType.STRING_LIST, "features.boring.colors");
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 =
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');