Merge remote-tracking branch 'origin/master' into jsoncache-fix

Conflicts:
	src/main/java/fr/xephi/authme/settings/Settings.java
This commit is contained in:
DNx5 2016-07-03 19:53:55 +07:00
commit 7ea0763966
38 changed files with 488 additions and 567 deletions

View File

@ -26,7 +26,6 @@ import fr.xephi.authme.listener.AuthMePlayerListener;
import fr.xephi.authme.listener.AuthMePlayerListener16;
import fr.xephi.authme.listener.AuthMePlayerListener18;
import fr.xephi.authme.listener.AuthMeServerListener;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.output.MessageKey;
@ -80,8 +79,6 @@ import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
/**
@ -115,7 +112,6 @@ public class AuthMe extends JavaPlugin {
private BukkitService bukkitService;
private AuthMeServiceInitializer initializer;
private GeoLiteAPI geoLiteApi;
private SendMailSSL mail;
/**
* Constructor.
@ -249,9 +245,6 @@ public class AuthMe extends JavaPlugin {
// Set console filter
setupConsoleFilter();
// Set up the mail API
setupMailApi();
// Do a backup on start
// TODO: maybe create a backup manager?
new PerformBackup(this, newSettings).doBackup(PerformBackup.BackupCause.START);
@ -304,16 +297,6 @@ public class AuthMe extends JavaPlugin {
initializer.get(API.class);
}
/**
* Set up the mail API, if enabled.
*/
private void setupMailApi() {
// Make sure the mail API is enabled
if (!newSettings.getProperty(MAIL_ACCOUNT).isEmpty() && !newSettings.getProperty(MAIL_PASSWORD).isEmpty()) {
this.mail = new SendMailSSL(this, newSettings);
}
}
/**
* Show the settings warnings, for various risky settings.
*/
@ -687,15 +670,6 @@ public class AuthMe extends JavaPlugin {
return commandHandler.processCommand(sender, commandLabel, args);
}
/**
* Get the mailing instance.
*
* @return The send mail instance.
*/
public SendMailSSL getMail() {
return this.mail;
}
// -------------
// Service getters (deprecated)
// Use @Inject fields instead

View File

@ -53,6 +53,16 @@ public class CommandService {
return messages.retrieve(key);
}
/**
* Retrieve a message as a single String by its message key.
*
* @param key The message to retrieve
* @return The message
*/
public String retrieveSingle(MessageKey key) {
return messages.retrieveSingle(key);
}
/**
* Retrieve the given property's value.
*

View File

@ -75,7 +75,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand {
if (dataSource.updatePassword(auth)) {
commandService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS);
ConsoleLogger.info(playerNameLowerCase + "'s password changed");
ConsoleLogger.info(sender.getName() + " changed password of " + playerNameLowerCase);
} else {
commandService.send(sender, MessageKey.ERROR);
}

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.command.executable.authme;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.converter.Converter;
@ -51,7 +52,11 @@ public class ConverterCommand implements ExecutableCommand {
bukkitService.runTaskAsynchronously(new Runnable() {
@Override
public void run() {
try {
converter.execute(sender);
} catch (Exception e) {
ConsoleLogger.logException("Error during conversion:", e);
}
}
});

View File

@ -79,7 +79,7 @@ public class RegisterAdminCommand implements ExecutableCommand {
bukkitService.scheduleSyncDelayedTask(new Runnable() {
@Override
public void run() {
player.kickPlayer("An admin just registered you, please log in again");
player.kickPlayer(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER));
}
});
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.command.executable.email;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.security.PasswordSecurity;
import fr.xephi.authme.security.RandomString;
@ -33,17 +33,16 @@ public class RecoverEmailCommand extends PlayerCommand {
private PlayerCache playerCache;
@Inject
// TODO #655: Remove injected AuthMe instance once Authme#mail is encapsulated
private AuthMe plugin;
private SendMailSSL sendMailSsl;
@Override
public void runCommand(Player player, List<String> arguments) {
final String playerMail = arguments.get(0);
final String playerName = player.getName();
if (plugin.getMail() == null) {
if (!sendMailSsl.hasAllInformation()) {
ConsoleLogger.showError("Mail API is not set");
commandService.send(player, MessageKey.ERROR);
commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
return;
}
if (dataSource.isAuthAvailable(playerName)) {
@ -76,7 +75,7 @@ public class RecoverEmailCommand extends PlayerCommand {
}
auth.setPassword(hashNew);
dataSource.updatePassword(auth);
plugin.getMail().main(auth, thePass);
sendMailSsl.sendPasswordMail(auth, thePass);
commandService.send(player, MessageKey.RECOVERY_EMAIL_SENT_MESSAGE);
} else {
commandService.send(player, MessageKey.REGISTER_EMAIL_MESSAGE);

View File

@ -3,6 +3,7 @@ package fr.xephi.authme.command.executable.register;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.PlayerCommand;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
@ -14,6 +15,7 @@ import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.List;
import static fr.xephi.authme.output.MessageKey.INCOMPLETE_EMAIL_SETTINGS;
import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH;
import static fr.xephi.authme.settings.properties.RegistrationSettings.ENABLE_CONFIRM_EMAIL;
import static fr.xephi.authme.settings.properties.RegistrationSettings.USE_EMAIL_REGISTRATION;
@ -27,6 +29,9 @@ public class RegisterCommand extends PlayerCommand {
@Inject
private CommandService commandService;
@Inject
private SendMailSSL sendMailSsl;
@Override
public void runCommand(Player player, List<String> arguments) {
if (commandService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) {
@ -63,11 +68,10 @@ public class RegisterCommand extends PlayerCommand {
}
private void handleEmailRegistration(Player player, List<String> arguments) {
if (commandService.getProperty(EmailSettings.MAIL_ACCOUNT).isEmpty()) {
player.sendMessage("Cannot register: no email address is set for the server. "
+ "Please contact an administrator");
ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email is set "
+ "to send emails from. Please add one in your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
if (!sendMailSsl.hasAllInformation()) {
commandService.send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
ConsoleLogger.showError("Cannot register player '" + player.getName() + "': no email or password is set "
+ "to send emails from. Please adjust your config at " + EmailSettings.MAIL_ACCOUNT.getPath());
return;
}

View File

@ -1,28 +1,77 @@
package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.UUID;
import static fr.xephi.authme.util.StringUtils.makePath;
public class vAuthConverter implements Converter {
private final AuthMe plugin;
private final DataSource dataSource;
private final File vAuthPasswordsFile;
@Inject
vAuthConverter(AuthMe plugin) {
this.plugin = plugin;
vAuthConverter(@DataFolder File dataFolder, DataSource dataSource) {
vAuthPasswordsFile = new File(dataFolder.getParent(), makePath("vAuth", "passwords.yml"));
this.dataSource = dataSource;
}
@Override
public void execute(CommandSender sender) {
try (Scanner scanner = new Scanner(vAuthPasswordsFile)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String name = line.split(": ")[0];
String password = line.split(": ")[1];
PlayerAuth auth;
if (isUuidInstance(password)) {
String pname;
try {
new vAuthFileReader(plugin).convert();
} catch (Exception e) {
sender.sendMessage(e.getMessage());
ConsoleLogger.showError(e.getMessage());
pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName();
} catch (Exception | NoSuchMethodError e) {
pname = getName(UUID.fromString(name));
}
if (pname == null)
continue;
auth = PlayerAuth.builder()
.name(pname.toLowerCase())
.realName(pname)
.password(password, null).build();
} else {
auth = PlayerAuth.builder()
.name(name.toLowerCase())
.realName(name)
.password(password, null).build();
}
dataSource.saveAuth(auth);
}
} catch (IOException e) {
ConsoleLogger.logException("Error while trying to import some vAuth data", e);
}
}
private static boolean isUuidInstance(String s) {
return s.length() > 8 && s.charAt(8) == '-';
}
private String getName(UUID uuid) {
for (OfflinePlayer op : Bukkit.getOfflinePlayers()) {
if (op.getUniqueId().compareTo(uuid) == 0) {
return op.getName();
}
}
return null;
}
}

View File

@ -1,83 +0,0 @@
package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.UUID;
import static fr.xephi.authme.util.StringUtils.makePath;
class vAuthFileReader {
private final AuthMe plugin;
private final DataSource database;
/**
* Constructor for vAuthFileReader.
*
* @param plugin AuthMe
*/
public vAuthFileReader(AuthMe plugin) {
this.plugin = plugin;
this.database = plugin.getDataSource();
}
public void convert() {
final File file = new File(plugin.getDataFolder().getParent(), makePath("vAuth", "passwords.yml"));
Scanner scanner;
try {
scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String name = line.split(": ")[0];
String password = line.split(": ")[1];
PlayerAuth auth;
if (isUuidInstance(password)) {
String pname;
try {
pname = Bukkit.getOfflinePlayer(UUID.fromString(name)).getName();
} catch (Exception | NoSuchMethodError e) {
pname = getName(UUID.fromString(name));
}
if (pname == null)
continue;
auth = PlayerAuth.builder()
.name(pname.toLowerCase())
.realName(pname)
.password(password, null).build();
} else {
auth = PlayerAuth.builder()
.name(name.toLowerCase())
.realName(name)
.password(password, null).build();
}
database.saveAuth(auth);
}
scanner.close();
} catch (IOException e) {
ConsoleLogger.logException("Error while trying to import some vAuth data", e);
}
}
private static boolean isUuidInstance(String s) {
return s.length() > 8 && s.charAt(8) == '-';
}
private String getName(UUID uuid) {
for (OfflinePlayer op : Bukkit.getOfflinePlayers()) {
if (op.getUniqueId().compareTo(uuid) == 0) {
return op.getName();
}
}
return null;
}
}

View File

@ -1,28 +1,146 @@
package fr.xephi.authme.converter;
import fr.xephi.authme.AuthMe;
import de.luricos.bukkit.xAuth.database.DatabaseTables;
import de.luricos.bukkit.xAuth.utils.xAuthLog;
import de.luricos.bukkit.xAuth.xAuth;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.util.CollectionUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginManager;
import javax.inject.Inject;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static fr.xephi.authme.util.StringUtils.makePath;
public class xAuthConverter implements Converter {
private final AuthMe plugin;
@Inject
xAuthConverter(AuthMe plugin) {
this.plugin = plugin;
@DataFolder
private File dataFolder;
@Inject
private DataSource database;
@Inject
private PluginManager pluginManager;
xAuthConverter() {
}
@Override
public void execute(CommandSender sender) {
try {
Class.forName("de.luricos.bukkit.xAuth.xAuth");
xAuthToFlat converter = new xAuthToFlat(plugin, sender);
converter.convert();
convert(sender);
} catch (ClassNotFoundException ce) {
sender.sendMessage("xAuth has not been found, please put xAuth.jar in your plugin folder and restart!");
}
}
private void convert(CommandSender sender) {
if (pluginManager.getPlugin("xAuth") == null) {
sender.sendMessage("[AuthMe] xAuth plugin not found");
return;
}
// TODO ljacqu 20160702: xAuthDb is not used except for the existence check -- is this intended?
File xAuthDb = new File(dataFolder.getParent(), makePath("xAuth", "xAuth.h2.db"));
if (!xAuthDb.exists()) {
sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data...");
}
List<Integer> players = getXAuthPlayers();
if (CollectionUtils.isEmpty(players)) {
sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players");
return;
}
sender.sendMessage("[AuthMe] Starting import...");
for (int id : players) {
String pl = getIdPlayer(id);
String psw = getPassword(id);
if (psw != null && !psw.isEmpty() && pl != null) {
PlayerAuth auth = PlayerAuth.builder()
.name(pl.toLowerCase())
.realName(pl)
.password(psw, null).build();
database.saveAuth(auth);
}
}
sender.sendMessage("[AuthMe] Successfully converted from xAuth database");
}
private String getIdPlayer(int id) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("playername").toLowerCase();
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve name for account: " + id, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
private List<Integer> getXAuthPlayers() {
List<Integer> xP = new ArrayList<>();
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT * FROM `%s`",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
xP.add(rs.getInt("id"));
}
} catch (SQLException e) {
xAuthLog.severe("Cannot import xAuthPlayers", e);
return new ArrayList<>();
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return xP;
}
private String getPassword(int accountId) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, accountId);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("password");
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
}

View File

@ -1,136 +0,0 @@
package fr.xephi.authme.converter;
import de.luricos.bukkit.xAuth.database.DatabaseTables;
import de.luricos.bukkit.xAuth.utils.xAuthLog;
import de.luricos.bukkit.xAuth.xAuth;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.util.CollectionUtils;
import org.bukkit.command.CommandSender;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
class xAuthToFlat {
private final AuthMe instance;
private final DataSource database;
private final CommandSender sender;
public xAuthToFlat(AuthMe instance, CommandSender sender) {
this.instance = instance;
this.database = instance.getDataSource();
this.sender = sender;
}
public boolean convert() {
if (instance.getServer().getPluginManager().getPlugin("xAuth") == null) {
sender.sendMessage("[AuthMe] xAuth plugin not found");
return false;
}
File xAuthDb = new File(instance.getDataFolder().getParent(), "xAuth" + File.separator + "xAuth.h2.db");
if (!xAuthDb.exists()) {
sender.sendMessage("[AuthMe] xAuth H2 database not found, checking for MySQL or SQLite data...");
}
List<Integer> players = getXAuthPlayers();
if (CollectionUtils.isEmpty(players)) {
sender.sendMessage("[AuthMe] Error while importing xAuthPlayers: did not find any players");
return false;
}
sender.sendMessage("[AuthMe] Starting import...");
try {
for (int id : players) {
String pl = getIdPlayer(id);
String psw = getPassword(id);
if (psw != null && !psw.isEmpty() && pl != null) {
PlayerAuth auth = PlayerAuth.builder()
.name(pl.toLowerCase())
.realName(pl)
.password(psw, null).build();
database.saveAuth(auth);
}
}
sender.sendMessage("[AuthMe] Successfully converted from xAuth database");
} catch (Exception e) {
sender.sendMessage("[AuthMe] An error has occurred while importing the xAuth database."
+ " The import may have succeeded partially.");
ConsoleLogger.logException("Error during xAuth database import", e);
}
return true;
}
private String getIdPlayer(int id) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `playername` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("playername").toLowerCase();
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve name for account: " + id, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
private List<Integer> getXAuthPlayers() {
List<Integer> xP = new ArrayList<>();
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT * FROM `%s`",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
xP.add(rs.getInt("id"));
}
} catch (SQLException e) {
xAuthLog.severe("Cannot import xAuthPlayers", e);
return new ArrayList<>();
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return xP;
}
private String getPassword(int accountId) {
String realPass = "";
Connection conn = xAuth.getPlugin().getDatabaseController().getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = String.format("SELECT `password`, `pwtype` FROM `%s` WHERE `id` = ?",
xAuth.getPlugin().getDatabaseController().getTable(DatabaseTables.ACCOUNT));
ps = conn.prepareStatement(sql);
ps.setInt(1, accountId);
rs = ps.executeQuery();
if (!rs.next())
return null;
realPass = rs.getString("password");
} catch (SQLException e) {
xAuthLog.severe("Failed to retrieve password hash for account: " + accountId, e);
return null;
} finally {
xAuth.getPlugin().getDatabaseController().close(conn, ps, rs);
}
return realPass;
}
}

View File

@ -3,14 +3,12 @@ package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.XFBCRYPT;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
@ -56,12 +54,10 @@ public class MySQL implements DataSource {
if (e instanceof IllegalArgumentException) {
ConsoleLogger.showError("Invalid database arguments! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!");
throw e;
}
if (e instanceof PoolInitializationException) {
ConsoleLogger.showError("Can't initialize database connection! Please check your configuration!");
ConsoleLogger.showError("If this error persists, please report it to the developer!");
throw new PoolInitializationException(e);
}
ConsoleLogger.showError("Can't use the Hikari Connection Pool! Please, report this error to the developer!");
throw e;
@ -69,7 +65,7 @@ public class MySQL implements DataSource {
// Initialize the database
try {
setupConnection();
checkTablesAndColumns();
} catch (SQLException e) {
close();
ConsoleLogger.logException("Can't initialize the MySQL database:", e);
@ -140,24 +136,13 @@ public class MySQL implements DataSource {
return ds.getConnection();
}
private void setupConnection() throws SQLException {
private void checkTablesAndColumns() throws SQLException {
try (Connection con = getConnection(); Statement st = con.createStatement()) {
// Create table if not exists.
// Create table with ID column if it doesn't exist
String sql = "CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " MEDIUMINT(8) UNSIGNED AUTO_INCREMENT,"
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE,"
+ col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ col.PASSWORD + " VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,"
+ col.IP + " VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '127.0.0.1',"
+ 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',"
+ col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "',"
+ col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com',"
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0',"
+ "PRIMARY KEY (" + col.ID + ")"
+ ") CHARACTER SET = utf8";
+ ") CHARACTER SET = utf8;";
st.executeUpdate(sql);
DatabaseMetaData md = con.getMetaData();
@ -177,8 +162,7 @@ public class MySQL implements DataSource {
}
if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.SALT + " VARCHAR(255);");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
if (isColumnMissing(md, col.IP)) {
@ -219,8 +203,6 @@ public class MySQL implements DataSource {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL);
}
st.close();
}
ConsoleLogger.info("MySQL setup finished");
}

View File

@ -5,11 +5,11 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.StringUtils;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -69,67 +69,73 @@ public class SQLite implements DataSource {
this.con = DriverManager.getConnection("jdbc:sqlite:plugins/AuthMe/" + database + ".db");
}
private void setup() throws SQLException {
Statement st = null;
ResultSet rs = null;
try {
st = con.createStatement();
st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (" + col.ID + " INTEGER AUTO_INCREMENT," + col.NAME + " VARCHAR(255) NOT NULL UNIQUE," + col.PASSWORD + " VARCHAR(255) NOT NULL," + col.IP + " VARCHAR(40) NOT NULL," + col.LAST_LOGIN + " BIGINT," + 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'," + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT '" + Settings.defaultWorld + "'," + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com'," + "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));");
rs = con.getMetaData().getColumns(null, null, tableName, col.PASSWORD);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL;");
@VisibleForTesting
protected void setup() throws SQLException {
try (Statement st = con.createStatement()) {
// Note: cannot add unique fields later on in SQLite, so we add it on initialization
st.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " ("
+ col.ID + " INTEGER AUTO_INCREMENT, "
+ col.NAME + " VARCHAR(255) NOT NULL UNIQUE, "
+ "CONSTRAINT table_const_prim PRIMARY KEY (" + col.ID + "));");
DatabaseMetaData md = con.getMetaData();
if (isColumnMissing(md, col.REAL_NAME)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
}
rs.close();
if (!col.SALT.isEmpty()) {
rs = con.getMetaData().getColumns(null, null, tableName, col.SALT);
if (!rs.next()) {
if (isColumnMissing(md, col.PASSWORD)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.PASSWORD + " VARCHAR(255) NOT NULL DEFAULT '';");
}
if (!col.SALT.isEmpty() && isColumnMissing(md, col.SALT)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.SALT + " VARCHAR(255);");
}
rs.close();
if (isColumnMissing(md, col.IP)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL DEFAULT '';");
}
rs = con.getMetaData().getColumns(null, null, tableName, col.IP);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IP + " VARCHAR(40) NOT NULL;");
if (isColumnMissing(md, col.LAST_LOGIN)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP;");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LAST_LOGIN);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP DEFAULT current_timestamp;");
if (isColumnMissing(md, col.LASTLOC_X)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X
+ " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y
+ " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z
+ " DOUBLE NOT NULL DEFAULT '0.0';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_X);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0';");
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0';");
if (isColumnMissing(md, col.LASTLOC_WORLD)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.LASTLOC_WORLD);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.LASTLOC_WORLD + " VARCHAR(255) NOT NULL DEFAULT 'world';");
if (isColumnMissing(md, col.EMAIL)) {
st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.EMAIL);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.EMAIL + " VARCHAR(255) DEFAULT 'your@email.com';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.IS_LOGGED);
if (!rs.next()) {
if (isColumnMissing(md, col.IS_LOGGED)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';");
}
rs.close();
rs = con.getMetaData().getColumns(null, null, tableName, col.REAL_NAME);
if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.REAL_NAME + " VARCHAR(255) NOT NULL DEFAULT 'Player';");
}
} finally {
close(rs);
close(st);
}
ConsoleLogger.info("SQLite Setup finished");
}
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) {
return !rs.next();
}
}
@Override
public void reload() {
close(con);
@ -146,7 +152,7 @@ public class SQLite implements DataSource {
PreparedStatement pst = null;
ResultSet rs = null;
try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);");
pst = con.prepareStatement("SELECT 1 FROM " + tableName + " WHERE LOWER(" + col.NAME + ")=LOWER(?);");
pst.setString(1, user);
rs = pst.executeQuery();
return rs.next();

View File

@ -1,120 +0,0 @@
package fr.xephi.authme.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.google.common.collect.Lists;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.util.BukkitService;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
class AuthMeTablistPacketAdapter extends PacketAdapter {
private final BukkitService bukkitService;
private boolean isRegistered;
public AuthMeTablistPacketAdapter(AuthMe plugin, BukkitService bukkitService) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO);
this.bukkitService = bukkitService;
}
@Override
public void onPacketSending(PacketEvent packetEvent) {
Player receiver = packetEvent.getPlayer();
if (packetEvent.getPacketType() == PacketType.Play.Server.PLAYER_INFO
&& !PlayerCache.getInstance().isAuthenticated(receiver.getName().toLowerCase())) {
//this hides the tablist for the new joining players. Already playing users will see the new player
try {
PacketContainer packet = packetEvent.getPacket();
PlayerInfoAction playerInfoAction = packet.getPlayerInfoAction().read(0);
if (playerInfoAction == PlayerInfoAction.ADD_PLAYER) {
List<PlayerInfoData> playerInfoList = Lists.newArrayList(packet.getPlayerInfoDataLists().read(0));
for (Iterator<PlayerInfoData> iterator = playerInfoList.iterator(); iterator.hasNext();) {
PlayerInfoData current = iterator.next();
UUID uuid = current.getProfile().getUUID();
if (Bukkit.getPlayer(uuid) == null) {
//player is not online -> a NPC
iterator.remove();
}
}
packet.getPlayerInfoDataLists().write(0, playerInfoList);
}
} catch (Exception ex) {
ConsoleLogger.logException("Couldn't modify outgoing tablist packet", ex);
}
}
}
public void sendTablist(Player receiver) {
if (!isRegistered) {
return;
}
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(receiver);
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
NativeGameMode gamemode = NativeGameMode.fromBukkit(receiver.getGameMode());
WrappedChatComponent displayName = WrappedChatComponent.fromText(receiver.getDisplayName());
PlayerInfoData playerInfoData = new PlayerInfoData(gameProfile, 0, gamemode, displayName);
//add info containing the skin data
PacketContainer addInfo = protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO);
addInfo.getPlayerInfoAction().write(0, PlayerInfoAction.ADD_PLAYER);
addInfo.getPlayerInfoDataLists().write(0, Arrays.asList(playerInfoData));
try {
//adds the skin
protocolManager.sendServerPacket(receiver, addInfo);
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Exception sending instant skin change packet", ex);
}
//triggers an update for others player to see them
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (onlinePlayer.equals(receiver) || !receiver.canSee(onlinePlayer)) {
continue;
}
//removes the entity and display them
receiver.hidePlayer(onlinePlayer);
receiver.showPlayer(onlinePlayer);
}
}
public void register() {
if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
ProtocolLibrary.getProtocolManager().addPacketListener(this);
isRegistered = true;
} else {
ConsoleLogger.info("The hideTablist feature is not compatible with your minecraft version");
ConsoleLogger.info("It requires 1.8+. Disabling the hideTablist feature...");
}
}
public void unregister() {
ProtocolLibrary.getProtocolManager().removePacketListener(this);
isRegistered = false;
}
}

View File

@ -15,12 +15,10 @@ public class ProtocolLibService implements SettingsDependent {
/* Packet Adapters */
private AuthMeInventoryPacketAdapter inventoryPacketAdapter;
private AuthMeTabCompletePacketAdapter tabCompletePacketAdapter;
private AuthMeTablistPacketAdapter tablistPacketAdapter;
/* Settings */
private boolean protectInvBeforeLogin;
private boolean denyTabCompleteBeforeLogin;
private boolean hideTablistBeforeLogin;
/* Service */
private boolean isEnabled;
@ -44,12 +42,10 @@ public class ProtocolLibService implements SettingsDependent {
if (protectInvBeforeLogin) {
ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
}
if (denyTabCompleteBeforeLogin) {
ConsoleLogger.showError("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it...");
}
if (hideTablistBeforeLogin) {
ConsoleLogger.showError("WARNING! The hideTablist feature requires ProtocolLib! Disabling it...");
}
this.isEnabled = false;
return;
@ -70,13 +66,6 @@ public class ProtocolLibService implements SettingsDependent {
tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null;
}
if (hideTablistBeforeLogin && tablistPacketAdapter == null) {
tablistPacketAdapter = new AuthMeTablistPacketAdapter(plugin, bukkitService);
tablistPacketAdapter.register();
} else if (tablistPacketAdapter != null) {
tablistPacketAdapter.unregister();
tablistPacketAdapter = null;
}
this.isEnabled = true;
}
@ -92,10 +81,6 @@ public class ProtocolLibService implements SettingsDependent {
tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null;
}
if (tablistPacketAdapter != null) {
tablistPacketAdapter.unregister();
tablistPacketAdapter = null;
}
}
/**
@ -109,21 +94,9 @@ public class ProtocolLibService implements SettingsDependent {
}
}
/**
* Send a tab list packet to a player.
*
* @param player The player to send the packet to.
*/
public void sendTabList(Player player) {
if (isEnabled && tablistPacketAdapter != null) {
tablistPacketAdapter.sendTablist(player);
}
}
@Override
public void loadSettings(NewSetting settings) {
this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
this.hideTablistBeforeLogin = settings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN);
}
}

View File

@ -5,45 +5,72 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.util.BukkitService;
import fr.xephi.authme.util.StringUtils;
import org.apache.commons.mail.EmailConstants;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.bukkit.Bukkit;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import javax.mail.Session;
import java.io.File;
import java.io.IOException;
import java.security.Security;
import java.util.Properties;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT;
import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD;
/**
* @author Xephi59
*/
public class SendMailSSL {
private final AuthMe plugin;
private final NewSetting settings;
@Inject
private AuthMe plugin;
@Inject
private NewSetting settings;
@Inject
private BukkitService bukkitService;
public SendMailSSL(AuthMe plugin, NewSetting settings) {
this.plugin = plugin;
this.settings = settings;
SendMailSSL() {
}
public void main(final PlayerAuth auth, final String newPass) {
final String mailText = replaceMailTags(settings.getEmailMessage(), plugin, auth, newPass);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
/**
* Returns whether all necessary settings are set for sending mails.
*
* @return true if the necessary email settings are set, false otherwise
*/
public boolean hasAllInformation() {
return !settings.getProperty(MAIL_ACCOUNT).isEmpty()
&& !settings.getProperty(MAIL_PASSWORD).isEmpty();
}
/**
* Sends an email to the user with his new password.
*
* @param auth the player auth of the player
* @param newPass the new password
*/
public void sendPasswordMail(final PlayerAuth auth, final String newPass) {
if (!hasAllInformation()) {
ConsoleLogger.showError("Cannot perform email registration: not all email settings are complete");
return;
}
final String mailText = replaceMailTags(settings.getEmailMessage(), auth, newPass);
bukkitService.runTaskAsynchronously(new Runnable() {
@Override
public void run() {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
HtmlEmail email;
try {
email = initializeMail(auth, settings);
email = initializeMail(auth.getEmail());
} catch (EmailException e) {
ConsoleLogger.logException("Failed to create email with the given settings:", e);
return;
@ -54,7 +81,7 @@ public class SendMailSSL {
File file = null;
if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) {
try {
file = generateImage(auth, plugin, newPass);
file = generateImage(auth.getNickname(), plugin, newPass);
content = embedImageIntoEmailContent(file, email, content);
} catch (IOException | EmailException e) {
ConsoleLogger.logException(
@ -67,13 +94,12 @@ public class SendMailSSL {
file.delete();
}
}
});
}
private static File generateImage(PlayerAuth auth, AuthMe plugin, String newPass) throws IOException {
private static File generateImage(String name, AuthMe plugin, String newPass) throws IOException {
ImageGenerator gen = new ImageGenerator(newPass);
File file = new File(plugin.getDataFolder(), auth.getNickname() + "_new_pass.jpg");
File file = new File(plugin.getDataFolder(), name + "_new_pass.jpg");
ImageIO.write(gen.generateImage(), "jpg", file);
return file;
}
@ -85,8 +111,7 @@ public class SendMailSSL {
return content.replace("<image />", "<img src=\"cid:" + tag + "\">");
}
private static HtmlEmail initializeMail(PlayerAuth auth, NewSetting settings)
throws EmailException {
private HtmlEmail initializeMail(String emailAddress) throws EmailException {
String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT);
String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME))
? senderMail
@ -98,12 +123,12 @@ public class SendMailSSL {
email.setCharset(EmailConstants.UTF_8);
email.setSmtpPort(port);
email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST));
email.addTo(auth.getEmail());
email.addTo(emailAddress);
email.setFrom(senderMail, senderName);
email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT));
email.setAuthentication(senderMail, mailPassword);
setPropertiesForPort(email, port, settings);
setPropertiesForPort(email, port);
return email;
}
@ -124,15 +149,14 @@ public class SendMailSSL {
}
}
private static String replaceMailTags(String mailText, AuthMe plugin, PlayerAuth auth, String newPass) {
private String replaceMailTags(String mailText, PlayerAuth auth, String newPass) {
return mailText
.replace("<playername />", auth.getNickname())
.replace("<servername />", plugin.getServer().getServerName())
.replace("<generatedpass />", newPass);
}
private static void setPropertiesForPort(HtmlEmail email, int port, NewSetting settings)
throws EmailException {
private void setPropertiesForPort(HtmlEmail email, int port) throws EmailException {
switch (port) {
case 587:
String oAuth2Token = settings.getProperty(EmailSettings.OAUTH2_TOKEN);

View File

@ -143,7 +143,11 @@ public enum MessageKey {
ACCOUNTS_OWNED_SELF("accounts_owned_self", "%count"),
ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count");
ACCOUNTS_OWNED_OTHER("accounts_owned_other", "%name", "%count"),
KICK_FOR_ADMIN_REGISTER("kicked_admin_registered"),
INCOMPLETE_EMAIL_SETTINGS("incomplete_email_settings");
private String key;
private String[] tags;

View File

@ -111,10 +111,6 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
restoreInventory(player);
}
if (service.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) {
protocolLibService.sendTabList(player);
}
// Clean up no longer used temporary data
limboCache.deleteLimboPlayer(player);
}

View File

@ -4,6 +4,7 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.AsynchronousProcess;
@ -55,6 +56,9 @@ public class AsyncRegister implements AsynchronousProcess {
@Inject
private ValidationService validationService;
@Inject
private SendMailSSL sendMailSsl;
AsyncRegister() { }
private boolean preRegisterCheck(Player player, String password) {
@ -137,7 +141,7 @@ public class AsyncRegister implements AsynchronousProcess {
}
database.updateEmail(auth);
database.updateSession(auth);
plugin.getMail().main(auth, password);
sendMailSsl.sendPasswordMail(auth, password);
syncProcessManager.processSyncEmailRegister(player);
}

View File

@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.LimboPlayerTaskManager;
import fr.xephi.authme.util.BukkitService;
@ -96,10 +95,6 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
final String name = player.getName().toLowerCase();
LimboPlayer limbo = limboCache.getLimboPlayer(name);
if (limbo != null) {
if (service.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) {
protocolLibService.sendTabList(player);
}
Utils.teleportToSpawn(player);
if (service.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) {

View File

@ -19,15 +19,15 @@ public final class Settings {
public static boolean isPermissionCheckEnabled;
public static boolean isTeleportToSpawnEnabled;
public static boolean isAllowRestrictedIp;
public static boolean isSaveQuitLocationEnabled;
public static boolean protectInventoryBeforeLogInEnabled;
public static boolean isStopEnabled;
public static boolean reloadSupport;
public static boolean noTeleport;
public static boolean isRemoveSpeedEnabled;
public static String getUnloggedinGroup;
public static String unRegisteredGroup;
public static String getRegisteredGroup;
public static String defaultWorld;
public static String crazyloginFileName;
public static int getNonActivatedGroup;
private static FileConfiguration configFile;
@ -45,6 +45,7 @@ public final class Settings {
isPermissionCheckEnabled = load(PluginSettings.ENABLE_PERMISSION_CHECK);
isTeleportToSpawnEnabled = load(RestrictionSettings.TELEPORT_UNAUTHED_TO_SPAWN);
isAllowRestrictedIp = load(RestrictionSettings.ENABLE_RESTRICTED_USERS);
isSaveQuitLocationEnabled = load(RestrictionSettings.SAVE_QUIT_LOCATION);
isRemoveSpeedEnabled = load(RestrictionSettings.REMOVE_SPEED);
getUnloggedinGroup = load(SecuritySettings.UNLOGGEDIN_GROUP);
getNonActivatedGroup = configFile.getInt("ExternalBoardOptions.nonActivedUserGroup", -1);
@ -55,7 +56,6 @@ public final class Settings {
reloadSupport = configFile.getBoolean("Security.ReloadCommand.useReloadCommandSupport", true);
defaultWorld = configFile.getString("Purge.defaultWorld", "world");
noTeleport = load(RestrictionSettings.NO_TELEPORT);
crazyloginFileName = configFile.getString("Converter.CrazyLogin.fileName", "accounts.db");
}
/**

View File

@ -92,10 +92,10 @@ public class SettingsMigrationService {
final File emailFile = new File(pluginFolder, "email.html");
final String mailText = configuration.getString(oldSettingPath)
.replace("<playername>", "<playername />")
.replace("<servername>", "<servername />")
.replace("<generatedpass>", "<generatedpass />")
.replace("<image>", "<image />");
.replace("<playername>", "<playername />").replace("%playername%", "<playername />")
.replace("<servername>", "<servername />").replace("%servername%", "<servername />")
.replace("<generatedpass>", "<generatedpass />").replace("%generatedpass%", "<generatedpass />")
.replace("<image>", "<image />").replace("%image%", "<image />");
if (!emailFile.exists()) {
try (FileWriter fw = new FileWriter(emailFile)) {
fw.write(mailText);

View File

@ -25,7 +25,6 @@ public class PluginSettings implements SettingsClass {
@Comment({
"After how many minutes should a session expire?",
"0 for unlimited time (Very dangerous, use it at your own risk!)",
"Remember that sessions will end only after the timeout, and",
"if the player's IP has changed but the timeout hasn't expired,",
"the player will be kicked from the server due to invalid session"

View File

@ -141,10 +141,6 @@ public class RestrictionSettings implements SettingsClass {
public static final Property<Boolean> DENY_TABCOMPLETE_BEFORE_LOGIN =
newProperty("settings.restrictions.DenyTabCompleteBeforeLogin", true);
@Comment("Should we hide the tablist before logging in? Requires ProtocolLib.")
public static final Property<Boolean> HIDE_TABLIST_BEFORE_LOGIN =
newProperty("settings.restrictions.HideTablistBeforeLogin", true);
@Comment({
"Should we display all other accounts from a player when he joins?",
"permission: /authme.admin.accounts"})

View File

@ -54,7 +54,6 @@ settings:
# hasn't expired, he will not need to authenticate.
enabled: false
# After how many minutes a session should expire?
# 0 for unlimited time (Very dangerous, use it at your own risk!)
# Consider that session will end only after the timeout time, and
# if the player's ip has changed but the timeout hasn't expired,
# player will be kicked out of sever due to invalidSession!
@ -141,8 +140,6 @@ settings:
ProtectInventoryBeforeLogIn: true
# Should we deny the tabcomplete feature before logging in? Requires ProtocolLib.
DenyTabCompleteBeforeLogin: true
# Should we hide the tablist before logging in? Requires ProtocolLib.
HideTablistBeforeLogin: true
# Should we display all other accounts from a player when he joins?
# permission: /authme.admin.accounts
displayOtherAccounts: true

View File

@ -68,3 +68,5 @@ invalid_name_case: 'You should join using username %valid, not %invalid.'
tempban_max_logins: '&cYou have been temporarily banned for failing to log in too many times.'
accounts_owned_self: 'You own %count accounts:'
accounts_owned_other: 'The player %name has %count accounts:'
kicked_admin_registered: 'An admin just registered you; please log in again'
incomplete_email_settings: 'Error: not all required settings are set for sending emails. Please contact an admin.'

View File

@ -69,3 +69,5 @@ tempban_max_logins: '&cVous êtes temporairement banni suite à plusieurs échec
accounts_owned_self: '&fVous avez %count comptes:'
accounts_owned_other: '&fLe joueur %name a %count comptes:'
denied_command: '&cVous devez être connecté pour pouvoir utiliser cette commande.'
kicked_admin_registered: 'Un admin vient de vous enregistrer, veuillez vous reconnecter.'
incomplete_email_settings: '&cErreur : Tous les paramètres requis ne sont pas présent pour l''envoi de mail, veuillez contacter un admin.'

View File

@ -22,12 +22,10 @@ import org.mockito.runners.MockitoJUnitRunner;
import java.util.Arrays;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@ -160,6 +158,8 @@ public class RegisterAdminCommandTest {
given(passwordSecurity.computeHash(password, user)).willReturn(hashedPassword);
Player player = mock(Player.class);
given(bukkitService.getPlayerExact(user)).willReturn(player);
String kickForAdminRegister = "Admin registered you -- log in again";
given(commandService.retrieveSingle(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister);
CommandSender sender = mock(CommandSender.class);
// when
@ -174,7 +174,7 @@ public class RegisterAdminCommandTest {
verify(dataSource).saveAuth(captor.capture());
assertAuthHasInfo(captor.getValue(), user, hashedPassword);
verify(dataSource).setUnlogged(user);
verify(player).kickPlayer(argThat(containsString("please log in again")));
verify(player).kickPlayer(kickForAdminRegister);
}
private void assertAuthHasInfo(PlayerAuth auth, String name, HashedPassword hashedPassword) {

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.register;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.HashAlgorithm;
@ -49,6 +50,8 @@ public class RegisterCommandTest {
@Mock
private Management management;
@Mock
private SendMailSSL sendMailSsl;
@BeforeClass
public static void setup() {
@ -72,7 +75,7 @@ public class RegisterCommandTest {
// then
verify(sender).sendMessage(argThat(containsString("Player only!")));
verifyZeroInteractions(management);
verifyZeroInteractions(management, sendMailSsl);
}
@Test
@ -86,6 +89,7 @@ public class RegisterCommandTest {
// then
verify(management).performRegister(player, "", "", true);
verifyZeroInteractions(sendMailSsl);
}
@Test
@ -98,7 +102,7 @@ public class RegisterCommandTest {
// then
verify(commandService).send(player, MessageKey.USAGE_REGISTER);
verifyZeroInteractions(management);
verifyZeroInteractions(management, sendMailSsl);
}
@Test
@ -112,7 +116,7 @@ public class RegisterCommandTest {
// then
verify(commandService).send(player, MessageKey.USAGE_REGISTER);
verifyZeroInteractions(management);
verifyZeroInteractions(management, sendMailSsl);
}
@Test
@ -127,7 +131,7 @@ public class RegisterCommandTest {
// then
verify(commandService).send(player, MessageKey.USAGE_REGISTER);
verifyZeroInteractions(management);
verifyZeroInteractions(management, sendMailSsl);
}
@Test
@ -135,14 +139,15 @@ public class RegisterCommandTest {
// given
given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true);
given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(false);
given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("");
given(sendMailSsl.hasAllInformation()).willReturn(false);
Player player = mock(Player.class);
// when
command.executeCommand(player, Collections.singletonList("myMail@example.tld"));
// then
verify(player).sendMessage(argThat(containsString("no email address")));
verify(commandService).send(player, MessageKey.INCOMPLETE_EMAIL_SETTINGS);
verify(sendMailSsl).hasAllInformation();
verifyZeroInteractions(management);
}
@ -155,6 +160,7 @@ public class RegisterCommandTest {
given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true);
given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true);
given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com");
given(sendMailSsl.hasAllInformation()).willReturn(true);
Player player = mock(Player.class);
// when
@ -175,6 +181,7 @@ public class RegisterCommandTest {
given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true);
given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true);
given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com");
given(sendMailSsl.hasAllInformation()).willReturn(true);
Player player = mock(Player.class);
// when
@ -182,6 +189,7 @@ public class RegisterCommandTest {
// then
verify(commandService).send(player, MessageKey.USAGE_REGISTER);
verify(sendMailSsl).hasAllInformation();
verifyZeroInteractions(management);
}
@ -196,6 +204,7 @@ public class RegisterCommandTest {
given(commandService.getProperty(RegistrationSettings.USE_EMAIL_REGISTRATION)).willReturn(true);
given(commandService.getProperty(RegistrationSettings.ENABLE_CONFIRM_EMAIL)).willReturn(true);
given(commandService.getProperty(EmailSettings.MAIL_ACCOUNT)).willReturn("server@example.com");
given(sendMailSsl.hasAllInformation()).willReturn(true);
Player player = mock(Player.class);
// when
@ -203,6 +212,7 @@ public class RegisterCommandTest {
// then
verify(commandService).validateEmail(playerMail);
verify(sendMailSsl).hasAllInformation();
verify(management).performRegister(eq(player), argThat(stringWithLength(passLength)), eq(playerMail), eq(true));
}
@ -217,7 +227,7 @@ public class RegisterCommandTest {
// then
verify(commandService).send(player, MessageKey.PASSWORD_MATCH_ERROR);
verifyZeroInteractions(management);
verifyZeroInteractions(management, sendMailSsl);
}
@Test

View File

@ -12,6 +12,9 @@ import java.util.Set;
import static fr.xephi.authme.AuthMeMatchers.equalToHash;
import static fr.xephi.authme.AuthMeMatchers.hasAuthBasicData;
import static fr.xephi.authme.AuthMeMatchers.hasAuthLocation;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
@ -170,6 +173,22 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(dataSource.getPassword("user"), equalToHash("new_hash"));
}
@Test
public void shouldUpdatePasswordWithPlayerAuth() {
// given
DataSource dataSource = getDataSource("salt");
PlayerAuth bobbyAuth = PlayerAuth.builder().name("bobby").password(new HashedPassword("tt", "cc")).build();
PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").password(new HashedPassword("tt", "cc")).build();
// when
boolean response1 = dataSource.updatePassword(bobbyAuth);
boolean response2 = dataSource.updatePassword(invalidAuth);
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getPassword("bobby"), equalToHash("tt", "cc"));
}
@Test
public void shouldRemovePlayerAuth() {
// given
@ -321,4 +340,49 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(dataSource.getAuth("bobby"), hasAuthBasicData("bobby", "BOBBY", "your@email.com", "123.45.67.89"));
}
@Test
public void shouldGetRecordsToPurge() {
// given
DataSource dataSource = getDataSource();
// 1453242857 -> user, 1449136800 -> bobby
// when
Set<String> records1 = dataSource.getRecordsToPurge(1450000000);
Set<String> records2 = dataSource.getRecordsToPurge(1460000000);
// then
assertThat(records1, contains("bobby"));
assertThat(records2, containsInAnyOrder("bobby", "user"));
// check that the entry was not deleted because of running this command
assertThat(dataSource.isAuthAvailable("bobby"), equalTo(true));
}
@Test
public void shouldPerformOperationsOnIsLoggedColumnSuccessfully() {
DataSource dataSource = getDataSource();
// on startup no one should be marked as logged
assertThat(dataSource.getLoggedPlayers(), empty());
// Mark user as logged
dataSource.setLogged("user");
// non-existent user should not break database
dataSource.setLogged("does-not-exist");
assertThat(dataSource.isLogged("user"), equalTo(true));
assertThat(dataSource.isLogged("bobby"), equalTo(false));
// Set bobby logged and unlog user
dataSource.setLogged("bobby");
dataSource.setUnlogged("user");
assertThat(dataSource.getLoggedPlayers(),
contains(hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89")));
// Set both as logged (even if Bobby already is logged)
dataSource.setLogged("user");
dataSource.setLogged("bobby");
dataSource.purgeLogged();
assertThat(dataSource.isLogged("user"), equalTo(false));
assertThat(dataSource.getLoggedPlayers(), empty());
}
}

View File

@ -6,6 +6,7 @@ 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.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock;
@ -60,7 +61,6 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
@Before
public void initializeConnectionAndTable() throws SQLException {
silentClose(hikariSource);
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.setConnectionTestQuery("VALUES 1");
@ -77,6 +77,11 @@ public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
hikariSource = ds;
}
@After
public void closeConnection() {
silentClose(hikariSource);
}
@Override
protected DataSource getDataSource(String saltColumn) {
when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn);

View File

@ -1,11 +1,14 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -17,6 +20,8 @@ import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -61,7 +66,6 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
@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");
@ -72,6 +76,50 @@ public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
con = connection;
}
@After
public void closeConnection() {
silentClose(con);
}
@Test
public void shouldSetUpTableIfMissing() throws SQLException {
// given
Statement st = con.createStatement();
// table is absent
st.execute("DROP TABLE authme");
SQLite sqLite = new SQLite(settings, con);
// when
sqLite.setup();
// then
// Save some player to verify database is operational
sqLite.saveAuth(PlayerAuth.builder().name("Name").build());
assertThat(sqLite.getAllAuths(), hasSize(1));
}
@Test
public void shouldCreateMissingColumns() throws SQLException {
// given
Statement st = con.createStatement();
// drop table and create one with only some of the columns: SQLite doesn't support ALTER TABLE t DROP COLUMN c
st.execute("DROP TABLE authme");
st.execute("CREATE TABLE authme ("
+ "id bigint, "
+ "username varchar(255) unique, "
+ "password varchar(255) not null, "
+ "primary key (id));");
SQLite sqLite = new SQLite(settings, con);
// when
sqLite.setup();
// then
// Save some player to verify database is operational
sqLite.saveAuth(PlayerAuth.builder().name("Name").build());
assertThat(sqLite.getAllAuths(), hasSize(1));
}
@Override
protected DataSource getDataSource(String saltColumn) {
when(settings.getProperty(DatabaseSettings.MYSQL_COL_SALT)).thenReturn(saltColumn);

View File

@ -22,12 +22,11 @@ public class MessageKeyTest {
// when / then
for (MessageKey messageKey : messageKeys) {
String key = messageKey.getKey();
if (keys.contains(key)) {
if (!keys.add(key)) {
fail("Found key '" + messageKey.getKey() + "' twice!");
} else if (StringUtils.isEmpty(key)) {
fail("Key for message key '" + messageKey + "' is empty");
}
keys.add(key);
}
}
}

View File

@ -33,10 +33,9 @@ public class AdminPermissionTest {
// when/then
for (AdminPermission permission : AdminPermission.values()) {
if (nodes.contains(permission.getNode())) {
if (!nodes.add(permission.getNode())) {
fail("More than one enum value defines the node '" + permission.getNode() + "'");
}
nodes.add(permission.getNode());
}
}

View File

@ -33,10 +33,9 @@ public class PlayerPermissionTest {
// when/then
for (PlayerPermission permission : PlayerPermission.values()) {
if (nodes.contains(permission.getNode())) {
if (!nodes.add(permission.getNode())) {
fail("More than one enum value defines the node '" + permission.getNode() + "'");
}
nodes.add(permission.getNode());
}
}
}

View File

@ -37,10 +37,9 @@ public class PlayerStatePermissionTest {
// when/then
for (PlayerStatePermission permission : PlayerStatePermission.values()) {
if (nodes.contains(permission.getNode())) {
if (!nodes.add(permission.getNode())) {
fail("More than one enum value defines the node '" + permission.getNode() + "'");
}
nodes.add(permission.getNode());
}
}

View File

@ -43,10 +43,9 @@ public class HashAlgorithmIntegrationTest {
// when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
if (classes.contains(algorithm.getClazz())) {
if (!classes.add(algorithm.getClazz())) {
fail("Found class '" + algorithm.getClazz() + "' twice!");
}
classes.add(algorithm.getClazz());
}
}
}

View File

@ -74,10 +74,9 @@ public class SettingsClassConsistencyTest {
if (Property.class.isAssignableFrom(field.getType())) {
Property<?> property =
(Property<?>) ReflectionTestUtils.getFieldValue(clazz, null, field.getName());
if (paths.contains(property.getPath())) {
if (!paths.add(property.getPath())) {
fail("Path '" + property.getPath() + "' should be used by only one constant");
}
paths.add(property.getPath());
}
}
}